Por que uma senha incorreta causa “O preenchimento é inválido e não pode ser removido”?

StackOverflow https://stackoverflow.com/questions/11762

  •  08-06-2019
  •  | 
  •  

Pergunta

Eu precisava de uma criptografia de string simples, então escrevi o código a seguir (com muita "inspiração" de aqui):

    // create and initialize a crypto algorithm
    private static SymmetricAlgorithm getAlgorithm(string password) {
        SymmetricAlgorithm algorithm = Rijndael.Create();
        Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes(
            password, new byte[] {
            0x53,0x6f,0x64,0x69,0x75,0x6d,0x20,             // salty goodness
            0x43,0x68,0x6c,0x6f,0x72,0x69,0x64,0x65
        }
        );
        algorithm.Padding = PaddingMode.ISO10126;
        algorithm.Key = rdb.GetBytes(32);
        algorithm.IV = rdb.GetBytes(16);
        return algorithm;
    }

    /* 
     * encryptString
     * provides simple encryption of a string, with a given password
     */
    public static string encryptString(string clearText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateEncryptor(), CryptoStreamMode.Write);
        cs.Write(clearBytes, 0, clearBytes.Length);
        cs.Close();
        return Convert.ToBase64String(ms.ToArray());
    }

    /*
     * decryptString
     * provides simple decryption of a string, with a given password
     */
    public static string decryptString(string cipherText, string password) {
        SymmetricAlgorithm algorithm = getAlgorithm(password);
        byte[] cipherBytes = Convert.FromBase64String(cipherText);
        MemoryStream ms = new MemoryStream();
        CryptoStream cs = new CryptoStream(ms, algorithm.CreateDecryptor(), CryptoStreamMode.Write);
        cs.Write(cipherBytes, 0, cipherBytes.Length);
        cs.Close();            
        return System.Text.Encoding.Unicode.GetString(ms.ToArray());
    }

O código parece funcionar bem, exceto que, ao descriptografar dados com uma chave incorreta, recebo uma CryptographicException - "O preenchimento é inválido e não pode ser removido" - na linha cs.Close() em decryptString.

código de exemplo:

    string password1 = "password";
    string password2 = "letmein";
    string startClearText = "The quick brown fox jumps over the lazy dog";
    string cipherText = encryptString(startClearText, password1);
    string endClearText = decryptString(cipherText, password2);     // exception thrown

Minha pergunta é: isso é esperado?Eu teria pensado que descriptografar com a senha errada resultaria apenas em uma saída sem sentido, em vez de uma exceção.

Foi útil?

Solução

Embora este tenha sido respondidas Eu acho que seria uma boa idéia para explicar por é de se esperar.

Um esquema de preenchimento é normalmente aplicada porque a maioria dos filtros de criptografia não são semanticamente seguro e para evitar algumas formas de cryptoatacks. Por exemplo, geralmente em RSA a OAEP esquema de estofamento é utilizado o que impede que alguns tipos de ataques (tal como um ataque de texto plano escolhido ou cegueira ).

É criado um mecanismo de preenchimento acrescenta alguns (geralmente) de lixo aleatório para a mensagem m antes que a mensagem é enviada. No método OAEP, por exemplo, duas Oráculos são usadas (esta é uma explicação simplista):

  1. Dado o tamanho do módulo você PADD k1 pedaços com 0 e k0 bits com um número aleatório.
  2. Em seguida, aplicando alguma transformação para a mensagem que você obter a mensagem acolchoada wich é criptografado e enviado.

Isso fornece uma randomização para as mensagens e com uma maneira de testar se a mensagem é lixo ou não. Como o esquema de preenchimento é reversível, quando você descriptografar a mensagem enquanto você não pode dizer nada sobre a integridade da mensagem em si pode, de fato, fazer alguma afirmação sobre o preenchimento e, portanto, você pode saber se a mensagem foi correctamente descodificada ou você está fazendo algo errado (ou seja, alguém adulterou a mensagem ou você está usando a tecla errada)

Outras dicas

Eu experimentei um similar "Preenchimento é inválido e não pode ser removido." exceção, mas no meu caso o IV chave e estofamento estavam corretas.

Acontece que liberar o fluxo de criptografia é tudo o que estava faltando.

Como esta:

            MemoryStream msr3 = new MemoryStream();
            CryptoStream encStream = new CryptoStream(msr3, RijndaelAlg.CreateEncryptor(), CryptoStreamMode.Write);
            encStream.Write(bar2, 0, bar2.Length);
            // unless we flush the stream we would get "Padding is invalid and cannot be removed." exception when decoding
            encStream.FlushFinalBlock();
            byte[] bar3 = msr3.ToArray();

Se você quiser que o seu uso seja correta, você deve adicionar autenticação para seu texto cifrado assim que você pode verificar se ele é o pasword correta ou que o texto cifrado não foi modificado. O preenchimento você estiver usando ISO10126 só vai lançar uma exceção se a última byte não desencriptar como um dos 16 valores válidos para estofamento (0x01-0x10). Então você tem uma chance de ele não jogar a exceção com a senha errada, onde se você autenticá-lo você tem uma maneira determinista saber se o seu descriptografia é válido 1/16.

Usando API de criptografia é embora aparentemente fácil, na verdade é bastante é fácil cometer erros. Por exemplo, você usar um sal fixo para para você chave derivação e iv, isso significa que cada texto cifrado criptografado com a mesma senha irá reutilizá-lo do IV com essa chave, que quebra a segurança semântica com o modo CBC, os IV precisa ser imprevisível e única para uma determinada chave.

Por essa razão, de fácil cometer erros, eu tenho um trecho de código, que eu tento manter revisto e atualizado (comentários, questões bem-vindos):

exemplos modernos de Symmetric autenticados Encryption de uma string C #.

Se você usá-lo de AESThenHMAC.AesSimpleDecryptWithPassword(ciphertext, password) quando a senha errada é usado, null é devolvido, se o texto cifrado ou iv foi modificado pós null criptografia é retornado, você nunca vai voltar dados de lixo, ou uma exceção de preenchimento.

Se você já descartou key-incompatibilidade, então além FlushFinalBlock() (ver resposta de Yaniv), chamando Close() na CryptoStream também será suficiente.

Se você está limpeza de recursos estritamente com blocos using, certifique-se de ninho do bloco para o próprio CryptoStream:

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
{
  using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
  {
    encStream.Write(bar2, 0, bar2.Length);
  } // implicit close
  byte[] encArray = ms.ToArray();
}

Eu fui mordido por isso (ou similar):

using (MemoryStream ms = new MemoryStream())
using (var enc = RijndaelAlg.CreateEncryptor())
using (CryptoStream encStream = new CryptoStream(ms, enc, CryptoStreamMode.Write))
{
  encStream.Write(bar2, 0, bar2.Length);
  byte[] encArray = ms.ToArray();
} // implicit close -- too late!

Sim, isso é de se esperar, ou pelo menos, o seu exatamente o que acontece quando nossas rotinas criptográficas obter dados não-decryptable

Outra razão da exceção pode ser uma condição de corrida entre vários segmentos usando lógica de decodificação - implementações nativas de ICryptoTransform são não thread-safe (por exemplo SymmetricAlgorithm), por isso deve ser condenado à seção exclusiva, por exemplo usando Bloqueio . Por favor, consulte aqui para mais detalhes: http: / /www.make-awesome.com/2011/07/system-security-cryptography-and-thread-safety/

Pode haver alguns bytes não lidas na CryptoStream. Fechando antes de ler o fluxo completamente estava causando o erro no meu programa.

Eu tive um problema semelhante, o problema no método de descriptografar foi inicializar um fluxo de memória vazio. quando ele trabalhou quando eu inicializado com a matriz de bytes texto cifrado assim:

MemoryStream ms = new MemoryStream(cipherText)

A resposta atualizada pelo usuário "atconway" funcionou para mim.

O problema não era com o preenchimento mas a chave que foi diferente durante a criptografia e descriptografia. A chave e iv deve ser o mesmo durante encypting e descriptografar o mesmo valor.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top