web-dev-qa-db-ja.com

パディングは無効であり、削除できませんか?

私はこの「例外」が私の「プログラム」に関して意味するものをオンラインで探しましたが、解決策や、それが私の特定のプログラムに起こっている理由を見つけることができないようです。 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);

}
103
Brown Love

Rijndael/AESはブロック暗号です。 128ビット(16文字)ブロックでデータを暗号化します。 暗号化パディング は、メッセージの最後のブロックが常に正しいサイズであることを確認するために使用されます。

復号化メソッドは、デフォルトのパディングが何であれ、それを検出していません。 @NetSquirrelが言うように、暗号化と復号化の両方にパディングを明示的に設定する必要があります。特に理由がない限り、PKCS#7パディングを使用してください。

64
rossum

encryptおよびdecryptに使用するキーが-であることを確認してください同じ。パディング方法は、明示的に設定されていなくても、適切な復号化/暗号化を許可する必要があります(設定されない場合は同じになります)。ただし、何らかの理由で暗号化に使用されるのとは異なるキーのセットを復号化に使用している場合、willは次のエラーを受け取ります:

パディングは無効であり、削除できません

何らかのアルゴリズムを使用して、機能しないキーを動的に生成する場合。暗号化と復号化の両方で同じである必要があります。一般的な方法の1つは、これらのアイテムの作成に暗号化/復号化プロセスが関与するのを防ぐために、呼び出し側に暗号化メソッドクラスのコンストラクターでキーを提供させることです。目の前のタスク(データの暗号化と復号化)に焦点を合わせ、呼び出し元がivkeyを提供する必要があります。

42
atconway

検索する人々の利益のために、復号化される入力をチェックする価値があるかもしれません。私の場合、復号化のために送信される情報は空の文字列として(間違って)入力されていました。パディングエラーが発生しました。

これは、ロスサムの答えに関連しているかもしれませんが、言及する価値があると考えました。

24
HockeyJ

同じキーと初期化ベクトルがエンコードとデコードに使用される場合、この問題はデータのデコードではなくデータのエンコードに起因します。

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が閉じられます。
[。除去される")。

これはおそらくSymmetricAlgorithm(Aes、DES、RC2、Rijndael、TripleDES)から派生したすべての暗号化アルゴリズムに当てはまりますが、AesManagedおよびMemoryStreamを出力ストリームとして検証したところです。

そのため、復号化でこのCryptographicException例外を受け取った場合、暗号化するデータを書き込んだ後に出力Stream Lengthプロパティ値を読み取り、FlushFinalBlockを呼び出してその値を再度読み取ります。変更されている場合、FlushFinalBlockの呼び出しはオプションではないことがわかります。

また、プログラムでパディングを実行したり、別のPaddingプロパティ値を選択したりする必要はありません。パディングはFlushFinalBlockメソッドのジョブです。

.........

ケビンへの追加コメント:

はい、CryptoStreamはCloseを呼び出す前にFlushFinalBlockを呼び出しますが、遅すぎます。CryptoStreamCloseメソッドが呼び出されると、出力ストリームも閉じられます。

出力ストリームがMemoryStreamの場合、閉じた後にデータを読み取ることはできません。したがって、MemoryStreamに書き込まれた暗号化データを使用する前に、CryptoStreamでFlushFinalBlockを呼び出す必要があります。

出力ストリームがFileStreamの場合、書き込みはバッファリングされるため、事態はさらに悪化します。その結果、FileStreamでFlushを呼び出す前に出力ストリームを閉じると、最後に書き込まれたバイトがファイルに書き込まれない可能性があります。したがって、CryptoStreamでCloseを呼び出す前に、まずCryptoStreamでFlushFinalBlockを呼び出し、次にFileStreamでFlushを呼び出す必要があります。

11
figolu

戦いのサーバル時代、私は最終的に問題を解決しました。
(注:対称アルゴリズムとして標準AESを使用しています。この回答は、すべての人に適しているわけではありません。)

  1. アルゴリズムクラスを変更します。 RijndaelManagedクラスをAESManagedクラスに置き換えます。
  2. アルゴリズムクラスのKeySizeを明示的に設定しないでください。デフォルトのままにしてください。
    (これは非常に重要なステップです。KeySizeプロパティにバグがあると思います。)

紛失した可能性のある引数を確認するリストを次に示します。

  • キー
    (バイト配列、長さは異なるキーサイズの場合、16、24、32バイトのいずれかでなければなりません。)
  • IV
    (バイト配列、16バイト)
  • CipherMode
    (CBC、CFB、CTS、ECB、OFBのいずれか)
  • PaddingMode
    (ANSIX923、ISO10126、なし、PKCS7、ゼロのいずれか)
11
Johnny

私の問題は、暗号化のpassPhraseが復号化のpassPhraseと一致しないということでした...このエラーがスローされました。

3
ecklerpa

私の問題を解決したのは、暗号化と復号化の方法に誤って異なるキーを適用していたことです。

1
Rondakay

ファイル内の暗号化された文字列を(メモ帳を使用して)手動で編集するときにこのパディングエラーが発生しました。暗号化されたコンテンツが手動で変更された場合の復号化機能の動作をテストするためです。

私にとっての解決策は、

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

私が言ったように、私のパディングエラーは、メモ帳を使用して復号化されたテキストを手動で入力していたためです。私の答えがあなたの解決策を導くかもしれません。

0
webzy

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;
}

パディングの問題はどのように説明できますか?暗号化の直前に、Goプログラムはパディングをチェックします。

// 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
}

重要な部分はこれです:

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

適切な長さで新しい配列が作成されるため、長さはブロックサイズの倍数になります。この新しい配列はゼロで埋められます。次に、copyメソッドは既存のデータをそこにコピーします。新しい配列が既存のデータよりも大きいことが保証されます。したがって、配列の最後にゼロがあります。

したがって、C#コードはPaddingMode.Zerosを使用できます。代替のPaddingMode.Noneはパディングを無視するだけで、これも機能します。 GoからC#などにコードを移植する必要がある人にとって、この回答が役立つことを願っています。

0

私はこのエラーが発生し、ブロックサイズを明示的に設定していました:aesManaged.BlockSize = 128;

それを削除すると、機能しました。

0
Barry Franklin

同じエラーが発生しました。私の場合、暗号化されたデータをSQLデータベースに保存したためです。データが保存されるテーブルは、binary(1000)データ型です。データベースからデータを取得すると、これらの1000バイトが復号化されますが、実際には400バイトです。したがって、結果から末尾のゼロ(600)を削除すると、問題が修正されました。

0
Martijn

暗号化されていないファイルパスをDecryptメソッドに渡そうとしたときにこのエラーが発生しました。

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

別のシナリオ、これもやはり検索する人々の利益のためです。

私にとって、このエラーは、暗号化に関係のない以前のエラーをマスクするDispose()メソッド中に発生しました。

他のコンポーネントが修正されると、この例外はなくなりました。

0
Clay Lenhart