Pytanie Długość danych do odszyfrowania jest nieprawidłowa


Próbuję zaszyfrować i odszyfrować strumień plików przez gniazdo za pomocą RijndaelManaged, ale wciąż wpadają na wyjątek

CryptographicException: długość danych do odszyfrowania jest nieprawidłowa.
    at System.Security.Cryptography.RijndaelManagedTransform.TransformFinalBlock (Byte [] inputBuffer, Int32 inputOffset, Int32 inputCount)
    w System.Security.Cryptography.CryptoStream.FlushFinalBlock ()
    at System.Security.Cryptography.CryptoStream.Dispose (Boolean disposing)

Wyjątek jest zgłaszany na końcu instrukcji using w receiveFile, kiedy cały plik został przesłany.

Próbowałem przeszukiwać sieć, ale znalazłem tylko odpowiedzi na problemy, które pojawiają się podczas korzystania z kodowania podczas szyfrowania i odszyfrowywania pojedynczego ciągu. Używam FileStream, więc nie określam żadnego kodowania, które powinno być używane, więc nie powinno to być problemem. Oto moje metody:

private void transferFile(FileInfo file, long position, long readBytes)
{
    // transfer on socket stream
    Stream stream = new FileStream(file.FullName, FileMode.Open);
    if (position > 0)
    {
        stream.Seek(position, SeekOrigin.Begin);
    }
    // if this should be encrypted, wrap the encryptor stream
    if (UseCipher)
    {
        stream = new CryptoStream(stream, streamEncryptor, CryptoStreamMode.Read);
    }
    using (stream)
    {
        int read;
        byte[] array = new byte[8096];
        while ((read = stream.Read(array, 0, array.Length)) > 0)
        {
            streamSocket.Send(array, 0, read, SocketFlags.None);
            position += read;
        }
    }
}

private void receiveFile(FileInfo transferFile)
{
    byte[] array = new byte[8096];
    // receive file
    Stream stream = new FileStream(transferFile.FullName, FileMode.Append);
    if (UseCipher)
    {
        stream = new CryptoStream(stream, streamDecryptor, CryptoStreamMode.Write);
    }
    using (stream)
    {
        long position = new FileInfo(transferFile.Path).Length;
        while (position < transferFile.Length)
        {
            int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position));
            int read = position < array.Length
                        ? streamSocket.Receive(array, maxRead, SocketFlags.None)
                        : streamSocket.Receive(array, SocketFlags.None);
            stream.Write(array, 0, read);
            position += read;
        }
    }
}

Jest to metoda, której używam do ustawienia szyfrów. byte [] init to wygenerowana tablica bajtów.

private void setupStreamCipher(byte[] init)
{
    RijndaelManaged cipher = new RijndaelManaged();
    cipher.KeySize = cipher.BlockSize = 256; // bit size
    cipher.Mode = CipherMode.ECB;
    cipher.Padding = PaddingMode.ISO10126;
    byte[] keyBytes = new byte[32];
    byte[] ivBytes = new byte[32];

    Array.Copy(init, keyBytes, 32);
    Array.Copy(init, 32, ivBytes, 0, 32);

    streamEncryptor = cipher.CreateEncryptor(keyBytes, ivBytes);
    streamDecryptor = cipher.CreateDecryptor(keyBytes, ivBytes);
}

Ktoś ma pomysł na to, co robię źle?


12
2018-06-02 22:07


pochodzenie




Odpowiedzi:


Wydaje mi się, że nie wysyłasz poprawnie ostatniego bloku. Musisz przynajmniej FlushFinalBlock() wysyłanie CryptoStream w celu zapewnienia, że ​​zostanie wysłany ostatni blok (który szuka strumień docelowy).

Tak poza tym, CipherMode.ECB to więcej niż prawdopodobne epickie niepowodzenie pod względem bezpieczeństwa dla tego, co robisz. Przynajmniej używaj CipherMode.CBC (łańcuchy bloków szyfru), które faktycznie używają IV i uzależniają każdy blok od poprzedniego.

EDYCJA: Whoops, strumień enciphering jest w trybie odczytu. W takim przypadku musisz upewnić się, że czytasz EOF, aby CryptoStream mógł poradzić sobie z ostatnim blokiem, zamiast przerywać po readBytes. Prawdopodobnie łatwiej jest kontrolować, jeśli uruchamiasz strumień enciphering w trybie zapisu.

Jeszcze jedna uwaga: nie można założyć, że bajty są równe bajtom. Szyfrowanie blokowe ma stały rozmiar bloku, który przetwarzają, i jeśli nie używasz trybu szyfrowania, który konwertuje szyfr blokowy do szyfru strumieniowego, będzie padding, który sprawia, że ​​tekst zaszyfrowany jest dłuższy niż tekst jawny.


6
2018-06-02 22:21



Metoda FlushFinalBlock () jest wywoływana w "sekcji zamykającej" instrukcji using <pre> przy użyciu (stream) {//} // wywołań Close () -> FlushFinalBlock () </ pre> Zmieniam tryb CipherMode, I po prostu wpisałem to jako przykład, więc wiesz, że nie zainicjuję mojego szyfru w jakikolwiek "dziwny" sposób. ReadBytes w sendFile () nie jest jeszcze używany, zapomniałem go usunąć. Przeczytałem do końca pliku, więc nie powinno to być tutaj problemem. Myślałem, że <pre> cipher.Padding = PaddingMode.ISO10126; </ pre> zajmował się dopełnieniem? Co mogę zmienić, aby działało? - Patrick
Jeśli strumień encyklopedyczny jest w trybie odczytu, ostatni blok zostanie utracony, jeśli go wyrzucisz; musi faktycznie odczytać koniec pliku z jego źródłowego strumienia źródłowego, aby wygenerować końcowy blok. - Jeffrey Hantin
W odpowiedzi na Jeffreya: Jeśli spróbuję wywołać stream.FlushFinalBlock (), mówi NonSupportedException: FlushFinalBlock nie może być wywołany dwa razy w tym samym strumieniu. Czy to nie oznacza, że ​​koniec pliku został przeczytany (i wysłany)? - Patrick
Niekoniecznie. Ponieważ strumień jest w trybie odczytu, nie można samemu wywołać funkcji FlushFinalBlock lub wystąpi problem: należy upewnić się, że warunek EOF "odczytuje" kod CryptoStream. Po stronie odbiorczej kod wydaje się przyjmować założenie, że istnieje zależność między bajtami w bajtach między bajtami na kablu i bajtami w pliku, co w rzeczywistości bardziej jest przyczyną wyjątku. Będziesz musiał przeczytać więcej bajtów z gniazda niż rozmiar pliku, aby odczytać dopełnienie. - Jeffrey Hantin
Ach, oczywiście, nie czytam z kryptostreamu, ale z gniazda! Głupi ja... - Patrick


Po komentarzu Jeffrey'a Hantina zmieniłem niektóre linie w receiveFile na

using (stream) {
    FileInfo finfo = new FileInfo(transferFile.Path);
    long position = finfo.Length;
    while (position < transferFile.Length) {
        int maxRead = Math.Min(array.Length, (int)(transferFile.Length - position));
        int read = position < array.Length
                   ? streamSocket.Receive(array, maxRead, SocketFlags.None)
                   : streamSocket.Receive(array, SocketFlags.None);
        stream.Write(array, 0, read);
        position += read;
    }
}

->

using (stream) {
    int read = array.Length;
    while ((read = streamSocket.Receive(array, read, SocketFlags.None)) > 0) {
        stream.Write(array, 0, read);
        if ((read = streamSocket.Available) == 0) {
            break;
        }
    }
}

I voila, ona pracuje (z powodu tego rodzaju wyściółki, o której wcześniej nie dbałem). Nie jestem pewien, co się stanie, jeśli Dostępne zwróci 0, mimo że wszystkie dane nie zostały przeniesione, ale zajmie to później w tym przypadku. Dzięki za pomoc Jeffrey!

Pozdrowienia.


1
2018-06-04 16:41





cipher.Mode = CipherMode.ECB;

Argh! Rolowanie własnego kodu bezpieczeństwa prawie zawsze jest złym pomysłem.


0
2018-06-02 23:40



????? co? Używa Rijndael? to nie jest "rzuć własną". Chociaż jest dobry punkt wyjścia, aby programiści musieli uważać na to, jak używają szyfrowania. - Cheeso
EBC nie działa tutaj, ponieważ każdy blok jest niezależnie szyfrowany. - Jeffrey Hantin
Nie ma znaczenia, z którego CipherMode korzystam, nadal otrzymuję wyjątek "Długość danych ..." ... - Patrick
@Cheeso: Nie musisz wymyślać własnego algorytmu kryptograficznego (lub nawet implementacji), aby wdrażać własne zabezpieczenia. W tym przypadku wygląda na to, że Patrick próbował wysłać plik przez kabel - jest na to wiele dobrych sposobów. Staczając się po swojemu, prawie włożył w jego aplikację ogromną lukę w zabezpieczeniach, która nie została znaleziona przez dobrą analizę (tak jak w przypadku przyzwoitej biblioteki), ale przez wysłanie niepowiązanego pytania do StackOverflow. - Alun Harford


Mój po prostu usunąłem wyściółkę i działa

Skomentował to - cipher.Padding = PaddingMode.ISO10126;


0
2017-07-08 01:02