[C#] 패딩이 유효하지 않아 제거 할 수 없습니까?

내 프로그램과 관련하여이 예외가 의미하는 바를 온라인에서 찾았지만 솔루션이나 특정 프로그램에서 발생하는 이유를 찾을 수없는 것 같습니다. Rijndael 알고리즘을 사용하여 XmlDocument를 암호화하고 해독하기 위해 msdn을 제공 한 예제를 사용하고 있습니다. 암호화는 잘 작동하지만 암호를 해독하려고하면 다음 예외가 발생합니다.

패딩이 잘못되었으며 제거 할 수 없습니다.

누구든지이 문제를 해결하기 위해 내가 무엇을 할 수 있는지 말해 줄 수 있습니까? 아래 코드는 키 및 기타 데이터를 얻는 곳입니다. cryptoMode가 false이면 예외가 발생하는 decrypt 메서드를 호출합니다.

public void Cryptography(XmlDocument doc, bool cryptographyMode)
{
    RijndaelManaged key = null;
    try
    {
    // Create a new Rijndael key.
    key = new RijndaelManaged();
    const string passwordBytes = "Password1234"; //password here 

    byte[] saltBytes = Encoding.UTF8.GetBytes("SaltBytes");
    Rfc2898DeriveBytes p = new Rfc2898DeriveBytes(passwordBytes, saltBytes);
    // sizes are devided by 8 because [ 1 byte = 8 bits ] 
    key.IV = p.GetBytes(key.BlockSize/8);
    key.Key = p.GetBytes(key.KeySize/8);

    if (cryptographyMode)
    {
        Ecrypt(doc, "Content", key);
    }
    else
    {
        Decrypt(doc, key);
    }

    }
    catch (Exception ex)
    {
    MessageBox.Show(ex.Message);
    }
    finally
    {
    // Clear the key.
    if (key != null)
    {
        key.Clear();
    }
    }

}

private void Decrypt(XmlDocument doc, SymmetricAlgorithm alg)
{
    // Check the arguments.  
    if (doc == null)
    throw new ArgumentNullException("Doc");
    if (alg == null)
    throw new ArgumentNullException("alg");

    // Find the EncryptedData element in the XmlDocument.
    XmlElement encryptedElement = doc.GetElementsByTagName("EncryptedData")[0] as XmlElement;

    // If the EncryptedData element was not found, throw an exception.
    if (encryptedElement == null)
    {
    throw new XmlException("The EncryptedData element was not found.");
    }


    // Create an EncryptedData object and populate it.
    EncryptedData edElement = new EncryptedData();
    edElement.LoadXml(encryptedElement);

    // Create a new EncryptedXml object.
    EncryptedXml exml = new EncryptedXml();


    // Decrypt the element using the symmetric key.
    byte[] rgbOutput = exml.DecryptData(edElement, alg); <----  I GET THE EXCEPTION HERE
    // Replace the encryptedData element with the plaintext XML element.
    exml.ReplaceData(encryptedElement, rgbOutput);

}



답변

Rijndael / AES는 블록 사이퍼입니다. 128 비트 (16 문자) 블록의 데이터를 암호화합니다. 암호화 패딩 은 메시지의 마지막 블록이 항상 올바른 크기인지 확인하는 데 사용됩니다.

암호 해독 방법은 기본 패딩이 무엇이든 예상하고 찾지 못합니다. @NetSquirrel이 말했듯이 암호화와 복호화 모두에 대해 명시 적으로 패딩을 설정해야합니다. 다른 이유가없는 한 PKCS # 7 패딩을 사용하십시오.


답변

확인 키 당신이 사용하는 것이 확인 암호화암호 해독이 되어 같은 . 패딩 방법은 명시 적으로 설정되지 않은 경우에도 적절한 암호 해독 / 암호화를 허용해야합니다 (설정하지 않으면 동일 함). 어떤 이유로 당신은 암호화에 사용보다 암호 해독 키의 다른 세트를 사용하지만 경우에 당신은 이 오류가 발생합니다 :

패딩이 잘못되었으며 제거 할 수 없습니다.

일부 알고리즘을 사용하여 작동하지 않는 키를 동적으로 생성하는 경우. 암호화와 복호화 모두 동일해야합니다. 한 가지 일반적인 방법은 호출자가 암호화 메서드 클래스의 생성자에 키를 제공하도록하여 암호화 / 복호화 프로세스가 이러한 항목을 생성하는 데 도움이되지 않도록하는 것입니다. 그것은 당면한 작업 (데이터 암호화 및 해독)에 초점을 맞추고 호출자 가 ivkey을 제공해야합니다.


답변

검색하는 사람들의 이익을 위해 암호가 해독되는 입력을 확인하는 것이 좋습니다. 제 경우에는 암호 해독을 위해 전송되는 정보가 (잘못) 빈 문자열로 들어갔습니다. 패딩 오류가 발생했습니다.

이것은 rossum의 답변과 관련이있을 수 있지만 언급 할 가치가 있다고 생각했습니다.


답변

인코딩 및 디코딩에 동일한 키와 초기화 벡터를 사용하는 경우이 문제는 데이터 디코딩이 아니라 데이터 인코딩에서 발생합니다.

CryptoStream 개체에 대해 Write 메서드를 호출 한 후에는 항상 Close 메서드보다 먼저 FlushFinalBlock 메서드를 호출해야합니다.

CryptoStream.FlushFinalBlock 메서드에 대한 MSDN 설명서에
Close 메서드를 호출하면 FlushFinalBlock이 호출됩니다 …
https://msdn.microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs .110) .aspx
이것은 잘못되었습니다. Close 메서드를 호출하면 CryptoStream과 출력 Stream이 닫힙니다.
암호화 할 데이터를 작성한 후 Close 전에 FlushFinalBlock을 호출하지 않으면 데이터를 해독 할 때 CryptoStream 개체에서 Read 또는 CopyTo 메서드를 호출하면 CryptographicException 예외가 발생합니다 (메시지 : “패딩이 유효하지 않으며 제거 할 수 없습니다”).

이것은 SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES)에서 파생 된 모든 암호화 알고리즘에 해당 될 수 있지만 AesManaged 및 MemoryStream에 대해 출력 스트림으로 확인했습니다.

따라서 암호 해독시이 CryptographicException 예외가 발생하면 암호화 할 데이터를 작성한 후 출력 Stream Length 속성 값을 읽은 다음 FlushFinalBlock을 호출하고 해당 값을 다시 읽습니다. 변경된 경우 FlushFinalBlock 호출이 선택 사항이 아님을 알고 있습니다.

프로그래밍 방식으로 패딩을 수행하거나 다른 패딩 속성 값을 선택할 필요가 없습니다. 패딩은 FlushFinalBlock 메서드 작업입니다.

………

Kevin에 대한 추가 설명 :

예, CryptoStream은 Close를 호출하기 전에 FlushFinalBlock을 호출하지만 너무 늦습니다. CryptoStream Close 메서드가 호출되면 출력 스트림도 닫힙니다.

출력 스트림이 MemoryStream 인 경우 닫힌 후 데이터를 읽을 수 없습니다. 따라서 MemoryStream에 기록 된 암호화 된 데이터를 사용하기 전에 CryptoStream에서 FlushFinalBlock을 호출해야합니다.

출력 스트림이 FileStream이면 쓰기가 버퍼링되기 때문에 상황이 더 나빠집니다. 결과적으로 FileStream에서 Flush를 호출하기 전에 출력 스트림을 닫으면 마지막으로 쓴 바이트가 파일에 기록되지 않을 수 있습니다. 따라서 CryptoStream에서 Close를 호출하기 전에 먼저 CryptoStream에서 FlushFinalBlock을 호출 한 다음 FileStream에서 Flush를 호출해야합니다.


답변

살벌한 싸움의 시간, 나는 마침내 문제를 해결했습니다.
(참고 : 대칭 알고리즘으로 표준 AES를 사용합니다.이 답변은 모든 사람에게 적합하지 않을 수 있습니다.)

  1. 알고리즘 클래스를 변경하십시오. RijndaelManaged클래스를 AESManaged하나로 바꿉니다 .
  2. KeySize알고리즘 클래스를 명시 적으로 설정하지 말고 기본값으로 두십시오.
    (이것은 매우 중요한 단계입니다. KeySize 속성에 버그가 있다고 생각합니다.)

놓친 인수를 확인하려는 목록은 다음과 같습니다.


  • (바이트 배열, 길이는 다른 키 크기에 대해 정확히 16, 24, 32 바이트 중 하나 여야합니다.)
  • IV
    (바이트 배열, 16 바이트)
  • CipherMode
    (CBC, CFB, CTS, ECB, OFB 중 하나)
  • PaddingMode
    (ANSIX923, ISO10126, None, PKCS7, Zeros 중 하나)

답변

내 문제는 암호화의 passPhrase가 decrypt의 passPhrase와 일치하지 않았기 때문에이 오류가 발생했습니다.


답변

내 문제를 해결 한 해결책은 실수로 암호화 및 암호 해독 방법에 다른 키를 적용한 것입니다.