code

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

codestyles 2020. 8. 13. 23:23
반응형

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


내 프로그램과 관련하여이 예외가 의미하는 바를 온라인에서 찾았지만 솔루션이나 내 특정 프로그램에서 발생하는 이유를 찾을 수없는 것 같습니다. 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와 일치하지 않았기 때문에이 오류가 발생했습니다.


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


검색하는 사람들의 이익을위한 또 다른 시나리오입니다.

나를 위해이 오류는 암호화와 관련이없는 이전 오류를 마스킹 한 Dispose () 메서드 중에 발생했습니다.

다른 구성 요소가 수정되면이 예외가 사라졌습니다.


암호화 된 콘텐츠가 수동으로 변경된 경우 암호 해독 기능이 어떻게 작동하는지 테스트하고 싶었 기 때문에 파일 (메모장 사용)에서 암호화 된 문자열을 수동으로 편집 할 때이 패딩 오류가 발생했습니다.

나를위한 해결책은

        try
            decryption stuff....
        catch
             inform decryption will not be carried out.
        end try

내가 말했듯이 패딩 오류는 메모장을 사용하여 해독 된 텍스트 위에 수동으로 입력했기 때문입니다. 내 대답이 귀하의 솔루션을 안내 할 수 있습니다.


암호화되지 않은 파일 경로를 Decrypt 메서드에 전달하려고 시도하는 동안이 오류가 발생했습니다. 해결 방법은 암호 해독을 시도하기 전에 전달 된 파일이 먼저 암호화되었는지 확인하는 것입니다.

if (Sec.IsFileEncrypted(e.File.FullName))
{
    var stream = Sec.Decrypt(e.File.FullName);
} 
else
{
    // non-encrypted scenario  
}

나는 같은 오류가 있었다. 제 경우에는 암호화 된 데이터를 SQL 데이터베이스에 저장했기 때문입니다. 데이터가 저장되는 테이블은 binary (1000) 데이터 유형입니다. 데이터베이스에서 데이터를 검색 할 때이 1000 바이트의 암호를 해독하는 반면 실제로는 400 바이트입니다. 따라서 결과에서 후행 0 (600)을 제거하면 문제가 해결되었습니다.


이 오류가 있었고 블록 크기를 명시 적으로 설정했습니다. aesManaged.BlockSize = 128;

그것을 제거하면 작동했습니다.


Go 프로그램을 C #으로 이식하는 데 동일한 문제가 발생했습니다. 이것은 많은 데이터가 이미 Go 프로그램으로 암호화되었음을 의미합니다. 이 데이터는 이제 C #으로 해독해야합니다.

최종 해결책은 PaddingMode.None또는 오히려 PaddingMode.Zeros.

Go의 암호화 방법 :

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha1"
    "encoding/base64"
    "io/ioutil"
    "log"

    "golang.org/x/crypto/pbkdf2"
)

func decryptFile(filename string, saltBytes []byte, masterPassword []byte) (artifact string) {

    const (
        keyLength         int = 256
        rfc2898Iterations int = 6
    )

    var (
        encryptedBytesBase64 []byte // The encrypted bytes as base64 chars
        encryptedBytes       []byte // The encrypted bytes
    )

    // Load an encrypted file:
    if bytes, bytesErr := ioutil.ReadFile(filename); bytesErr != nil {
        log.Printf("[%s] There was an error while reading the encrypted file: %s\n", filename, bytesErr.Error())
        return
    } else {
        encryptedBytesBase64 = bytes
    }

    // Decode base64:
    decodedBytes := make([]byte, len(encryptedBytesBase64))
    if countDecoded, decodedErr := base64.StdEncoding.Decode(decodedBytes, encryptedBytesBase64); decodedErr != nil {
        log.Printf("[%s] An error occur while decoding base64 data: %s\n", filename, decodedErr.Error())
        return
    } else {
        encryptedBytes = decodedBytes[:countDecoded]
    }

    // Derive key and vector out of the master password and the salt cf. RFC 2898:
    keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
    keyBytes := keyVectorData[:keyLength/8]
    vectorBytes := keyVectorData[keyLength/8:]

    // Create an AES cipher:
    if aesBlockDecrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
        log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
        return
    } else {

        // CBC mode always works in whole blocks.
        if len(encryptedBytes)%aes.BlockSize != 0 {
            log.Printf("[%s] The encrypted data's length is not a multiple of the block size.\n", filename)
            return
        }

        // Reserve memory for decrypted data. By definition (cf. AES-CBC), it must be the same lenght as the encrypted data:
        decryptedData := make([]byte, len(encryptedBytes))

        // Create the decrypter:
        aesDecrypter := cipher.NewCBCDecrypter(aesBlockDecrypter, vectorBytes)

        // Decrypt the data:
        aesDecrypter.CryptBlocks(decryptedData, encryptedBytes)

        // Cast the decrypted data to string:
        artifact = string(decryptedData)
    }

    return
}

... 그리고 ...

import (
    "crypto/aes"
    "crypto/cipher"
    "crypto/sha1"
    "encoding/base64"
    "github.com/twinj/uuid"
    "golang.org/x/crypto/pbkdf2"
    "io/ioutil"
    "log"
    "math"
    "os"
)

func encryptFile(filename, artifact string, masterPassword []byte) (status bool) {

    const (
        keyLength         int = 256
        rfc2898Iterations int = 6
    )

    status = false
    secretBytesDecrypted := []byte(artifact)

    // Create new salt:
    saltBytes := uuid.NewV4().Bytes()

    // Derive key and vector out of the master password and the salt cf. RFC 2898:
    keyVectorData := pbkdf2.Key(masterPassword, saltBytes, rfc2898Iterations, (keyLength/8)+aes.BlockSize, sha1.New)
    keyBytes := keyVectorData[:keyLength/8]
    vectorBytes := keyVectorData[keyLength/8:]

    // Create an AES cipher:
    if aesBlockEncrypter, aesErr := aes.NewCipher(keyBytes); aesErr != nil {
        log.Printf("[%s] Was not possible to create new AES cipher: %s\n", filename, aesErr.Error())
        return
    } else {

        // CBC mode always works in whole blocks.
        if len(secretBytesDecrypted)%aes.BlockSize != 0 {
            numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
            enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
            copy(enhanced, secretBytesDecrypted)
            secretBytesDecrypted = enhanced
        }

        // Reserve memory for encrypted data. By definition (cf. AES-CBC), it must be the same lenght as the plaintext data:
        encryptedData := make([]byte, len(secretBytesDecrypted))

        // Create the encrypter:
        aesEncrypter := cipher.NewCBCEncrypter(aesBlockEncrypter, vectorBytes)

        // Encrypt the data:
        aesEncrypter.CryptBlocks(encryptedData, secretBytesDecrypted)

        // Encode base64:
        encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(encryptedData)))
        base64.StdEncoding.Encode(encodedBytes, encryptedData)

        // Allocate memory for the final file's content:
        fileContent := make([]byte, len(saltBytes))
        copy(fileContent, saltBytes)
        fileContent = append(fileContent, 10)
        fileContent = append(fileContent, encodedBytes...)

        // Write the data into a new file. This ensures, that at least the old version is healthy in case that the
        // computer hangs while writing out the file. After a successfully write operation, the old file could be
        // deleted and the new one could be renamed.
        if writeErr := ioutil.WriteFile(filename+"-update.txt", fileContent, 0644); writeErr != nil {
            log.Printf("[%s] Was not able to write out the updated file: %s\n", filename, writeErr.Error())
            return
        } else {
            if renameErr := os.Rename(filename+"-update.txt", filename); renameErr != nil {
                log.Printf("[%s] Was not able to rename the updated file: %s\n", fileContent, renameErr.Error())
            } else {
                status = true
                return
            }
        }

        return
    }
}

이제 C #에서 암호 해독 :

public static string FromFile(string filename, byte[] saltBytes, string masterPassword)
{
    var iterations = 6;
    var keyLength = 256;
    var blockSize = 128;
    var result = string.Empty;
    var encryptedBytesBase64 = File.ReadAllBytes(filename);

    // bytes -> string:
    var encryptedBytesBase64String = System.Text.Encoding.UTF8.GetString(encryptedBytesBase64);

    // Decode base64:
    var encryptedBytes = Convert.FromBase64String(encryptedBytesBase64String);
    var keyVectorObj = new Rfc2898DeriveBytes(masterPassword, saltBytes.Length, iterations);
    keyVectorObj.Salt = saltBytes;
    Span<byte> keyVectorData = keyVectorObj.GetBytes(keyLength / 8 + blockSize / 8);
    var key = keyVectorData.Slice(0, keyLength / 8);
    var iv = keyVectorData.Slice(keyLength / 8);

    var aes = Aes.Create();
    aes.Padding = PaddingMode.Zeros;
    // or ... aes.Padding = PaddingMode.None;
    var decryptor = aes.CreateDecryptor(key.ToArray(), iv.ToArray());
    var decryptedString = string.Empty;

    using (var memoryStream = new MemoryStream(encryptedBytes))
    {
        using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
        {
            using (var reader = new StreamReader(cryptoStream))
            {
                decryptedString = reader.ReadToEnd();
            }
        }
    }

    return result;
}

How can the issue with the padding be explained? Just before encryption the Go program checks the padding:

// CBC mode always works in whole blocks.
if len(secretBytesDecrypted)%aes.BlockSize != 0 {
    numberNecessaryBlocks := int(math.Ceil(float64(len(secretBytesDecrypted)) / float64(aes.BlockSize)))
    enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
    copy(enhanced, secretBytesDecrypted)
    secretBytesDecrypted = enhanced
}

The important part is this:

enhanced := make([]byte, numberNecessaryBlocks*aes.BlockSize)
copy(enhanced, secretBytesDecrypted)

A new array is created with an appropriate length, so that the length is a multiple of the block size. This new array is filled with zeros. The copy method then copies the existing data into it. It is ensured that the new array is larger than the existing data. Accordingly, there are zeros at the end of the array.

Thus, the C# code can use PaddingMode.Zeros. The alternative PaddingMode.None just ignores any padding, which also works. I hope this answer is helpful for anyone who has to port code from Go to C#, etc.

참고URL : https://stackoverflow.com/questions/8583112/padding-is-invalid-and-cannot-be-removed

반응형