Phần đệm không hợp lệ và không thể xóa?


125

Tôi đã tìm kiếm trực tuyến xem ngoại lệ này có ý nghĩa gì liên quan đến chương trình của tôi nhưng dường như không thể tìm ra giải pháp hoặc lý do tại sao nó lại xảy ra với chương trình cụ thể của tôi. Tôi đã sử dụng ví dụ được cung cấp msdn của mình để mã hóa và giải mã một XmlDocument bằng cách sử dụng thuật toán Rijndael. Mã hóa hoạt động tốt nhưng khi tôi cố gắng giải mã, tôi nhận được ngoại lệ sau:

Phần đệm không hợp lệ và không thể xóa được

Bất cứ ai có thể cho tôi biết tôi có thể làm gì để giải quyết vấn đề này? Mã của tôi bên dưới là nơi tôi lấy khóa và các dữ liệu khác. Nếu cryptoMode là false, nó sẽ gọi phương thức giải mã, đây là nơi xảy ra ngoại lệ:

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

}

12
Bạn có thể thử bằng cách thiết lập rõ ràng chế độ đệm giống hệt nhau trên cả mã hóa và giải mã thành thụt lề. Ví dụ: alg.Padding = PaddingMode.NONE;
NetSquirrel

Phương thức Encrypt () trông như thế nào?
csharptest.net

1
Cảm ơn các bạn đã làm việc
Brown Love,

2
@NetSquirrel: cảm ơn bạn đã nhắc nhở PaddingMode.NONE. Nó giúp tôi thoát khỏi lỗi này (đến lỗi khác) ... Làm AES trong cả Java và C #, và bây giờ không biết tại sao C # phàn nàn về Java padding, mặc dù cả hai đều sử dụng PKCS # 7
Hoàng Long

Câu trả lời:


81

Rijndael / AES là một trình xử lý khối. Nó mã hóa dữ liệu trong các khối 128 bit (16 ký tự). Phần đệm mật mã được sử dụng để đảm bảo rằng khối cuối cùng của thông báo luôn có kích thước chính xác.

Phương pháp giải mã của bạn đang mong đợi bất kỳ vùng đệm mặc định của nó là gì và không tìm thấy nó. Như @NetSquirrel nói, bạn cần đặt đệm rõ ràng cho cả mã hóa và giải mã. Trừ khi bạn có lý do để làm khác, hãy sử dụng đệm PKCS # 7.


7
làm thế nào để đặt padding rõ ràng ??
Ahmad Hajjar

7
Cảm ơn bạn, tôi đã tìm thấy nó rj.Padding = PaddingMode.none; :)
Ahmad Hajjar

7
@AhmadHajjar Không có phần đệm nào có tác động bảo mật, đừng sử dụng nó.
devilantfan

1
Xin chào, tôi đã đặt đệm rõ ràng, nhưng không hoạt động. Tôi không biết mình đã làm sai những bước nào. Xin vui lòng giúp đỡ. alg.Padding = PaddingMode.PKCS7;
Johnny

21
Tôi nhận ra đây là một chủ đề cũ. Tuy nhiên, đối với những người truy cập, hãy đảm bảo rằng bạn xóa khối cuối cùng khi mã hóa dữ liệu.
Markus

52

Đảm bảo rằng các khóa bạn sử dụng để mã hóagiải mã đều giống nhau . Phương thức đệm ngay cả khi không được đặt rõ ràng vẫn phải cho phép giải mã / mã hóa thích hợp (nếu không được đặt thì chúng sẽ giống nhau). Tuy nhiên, nếu bạn vì lý do nào đó đang sử dụng một bộ khóa khác để giải mã chứ không phải dùng để mã hóa, bạn sẽ gặp lỗi này:

Phần đệm không hợp lệ và không thể xóa được

Nếu bạn đang sử dụng một số thuật toán để tạo động các khóa sẽ không hoạt động. Chúng cần phải giống nhau cho cả mã hóa và giải mã. Một cách phổ biến là yêu cầu người gọi cung cấp các khóa trong phương thức khởi tạo của lớp phương thức mã hóa, để ngăn quá trình mã hóa / giải mã có bất kỳ sự tiếp tay nào trong việc tạo ra các mục này. Nó tập trung vào nhiệm vụ trong tầm tay (mã hóa và giải mã dữ liệu) và yêu cầu ivkeyđược cung cấp bởi người gọi.


Mẹo này rất hữu ích, bởi vì, đôi khi các khóa được lưu trữ trên app.config và chúng ta phải luôn đảm bảo rằng các khóa được sử dụng để mã hóa cũng giống như các khóa được sử dụng để giải mã.
Mário Meyrelles

@atconway Bạn có vui lòng xem qua câu hỏi của tôi không? Tôi gặp sự cố tương tự nhưng trong C ++ / CLI: stackoverflow.com/questions/57139447/…
Đơn giản

Tôi đề nghị rằng trong việc sử dụng hàng ngày, đây có lẽ là lý do có nhiều khả năng nhất khiến mọi người gặp phải lỗi này. Đặc biệt nếu bạn không làm rối với cài đặt đệm.
Dan

28

Vì lợi ích của những người đang tìm kiếm, có thể nên kiểm tra thông tin đầu vào được giải mã. Trong trường hợp của tôi, thông tin được gửi để giải mã (sai) đi vào dưới dạng một chuỗi trống. Nó dẫn đến lỗi đệm.

Điều này có thể liên quan đến câu trả lời của rossum, nhưng nghĩ rằng nó đáng nói.


Tôi đồng ý, cũng xảy ra với tôi như vậy, kiểm tra đầu vào được giải mã TRƯỚC KHI thực hiện các kiểm tra khác. Tôi đã nhận được 1 byte nhiều hơn những gì tôi encypted ...
Andrea Antonangeli

Một chuỗi trống cũng là thủ phạm cho tôi.
dotNET

Trường hợp của tôi là cụm mật khẩu không được đặt (vâng tôi biết), nhưng câu trả lời này đã giúp tôi đi đúng hướng.
Jim

2
Vấn đề của tôi là chuỗi được giải mã đang được chuyển đổi thành chữ thường trước khi tôi cố gắng giải mã nó. Tôi đã bị ám ảnh với phần đệm và mật mã và tất cả những thứ đó, nhưng hóa ra đó chỉ là đầu vào tồi. Đôi khi bạn chỉ cần lùi lại một bước!
Tom Gerken,

15

Nếu cùng một khóa và vectơ khởi tạo được sử dụng để mã hóa và giải mã, thì vấn đề này không đến từ giải mã dữ liệu mà do mã hóa dữ liệu.

Sau khi bạn gọi phương thức Viết trên một đối tượng CryptoStream, bạn LUÔN phải gọi phương thức FlushFinalBlock trước phương thức Đóng.

Tài liệu MSDN về phương thức CryptoStream.FlushFinalBlock cho biết:
" Gọi phương thức Đóng sẽ gọi FlushFinalBlock ... "
https://msdn.microsoft.com/en-US/library/system.security.cryptography.cryptostream.flushfinalblock(v=vs .110) .aspx
Điều này sai. Phương thức Gọi Close chỉ đóng Dòng tiền điện tử và Dòng đầu ra.
Nếu bạn không gọi FlushFinalBlock trước khi Đóng sau khi bạn ghi dữ liệu cần mã hóa, thì khi giải mã dữ liệu, một lệnh gọi đến phương thức Read hoặc CopyTo trên đối tượng CryptoStream của bạn sẽ đưa ra ngoại lệ CryptographicException (thông báo: "Padding không hợp lệ và không thể xóa").

Điều này có lẽ đúng đối với tất cả các thuật toán mã hóa bắt nguồn từ SymmetricAlgorithm (Aes, DES, RC2, Rijndael, TripleDES), mặc dù tôi vừa xác minh điều đó cho AesManaged và MemoryStream làm Luồng đầu ra.

Vì vậy, nếu bạn nhận được ngoại lệ CryptographicException này khi giải mã, hãy đọc giá trị thuộc tính Độ dài luồng đầu ra của bạn sau khi bạn viết dữ liệu được mã hóa, sau đó gọi FlushFinalBlock và đọc lại giá trị của nó. Nếu nó đã thay đổi, bạn biết rằng việc gọi FlushFinalBlock KHÔNG phải là tùy chọn.

Và bạn không cần phải thực hiện bất kỳ phần đệm nào theo lập trình hoặc chọn một giá trị thuộc tính Padding khác. Padding là công việc của phương thức FlushFinalBlock.

.........

Nhận xét bổ sung cho Kevin:

Đúng, CryptoStream gọi FlushFinalBlock trước khi gọi Close, nhưng đã quá muộn: khi phương thức CryptoStream Close được gọi, luồng đầu ra cũng bị đóng.

Nếu luồng đầu ra của bạn là Dòng nhớ, bạn không thể đọc dữ liệu của luồng sau khi đóng. Vì vậy, bạn cần gọi FlushFinalBlock trên CryptoStream của mình trước khi sử dụng dữ liệu mã hóa được ghi trên MemoryStream.

Nếu luồng đầu ra của bạn là FileStream, mọi thứ còn tồi tệ hơn vì việc ghi được lưu vào bộ đệm. Hệ quả là các byte được ghi cuối cùng có thể không được ghi vào tệp nếu bạn đóng luồng đầu ra trước khi gọi Flush trên FileStream. Vì vậy, trước khi gọi Close trên CryptoStream, trước tiên bạn cần gọi FlushFinalBlock trên CryptoStream của mình sau đó gọi Flush trên FileStream của bạn.


1
Tại sao bạn nói nó sai? Mã cho Stream.Close()cuộc gọi this.Dispose(true). Mã cho CryptoStream.Dispose(bool)là:if (disposing) { if (!this._finalBlockTransformed) { this.FlushFinalBlock(); } this._stream.Close(); }
Kevin Doyon

1
Điều này đã giải quyết vấn đề của tôi. Tôi đã định loại cryptoStream một cách chính xác, nhưng cuộc gọi hủy bỏ đã xảy ra "quá muộn", như bạn nói. Điều này dẫn đến lỗi "phần đệm không hợp lệ", như được mô tả. Bằng cách thêm cryptoStream.FlushFinalBlock (), lỗi đệm không hợp lệ đã được giải quyết. Cảm ơn!
Daniel Lambert

14

Một thời gian đấu tranh, cuối cùng tôi đã giải quyết được vấn đề.
(Lưu ý: Tôi sử dụng AES tiêu chuẩn làm thuật toán đối xứng. Câu trả lời này có thể không phù hợp với tất cả mọi người.)

  1. Thay đổi lớp thuật toán. Thay thế RijndaelManagedlớp thành AESManagedmột.
  2. Không đặt rõ ràng KeySizelớp thuật toán, để chúng mặc định.
    (Đây là bước rất quan trọng. Tôi nghĩ rằng có một lỗi trong thuộc tính KeySize.)

Đây là danh sách bạn muốn kiểm tra xem bạn có thể đã bỏ qua đối số nào:

  • Khóa
    (mảng byte, độ dài phải chính xác là một trong 16, 24, 32 byte đối với kích thước khóa khác nhau.)
  • IV
    (mảng byte, 16 byte)
  • CipherMode
    (Một trong các CBC, CFB, CTS, ECB, OFB)
  • PaddingMode
    (Một trong ANSIX923, ISO10126, Không có, PKCS7, Zeros)

3
Không thiết lập rõ ràng đã KeySizesửa nó ngay lập tức cho tôi. Ôi những điều kỳ quặc của .NET :-(
John

Lưu ý rằng đây dường như là một hồi quy trong chính .NET Framework. Tôi có mã từng hoạt động với RijndaelManaged, nhưng đã ngừng hoạt động và chỉ cần thay đổi nó thành AesManaged / AesCryptoServiceProvider, nó hoạt động trở lại. Tôi thậm chí không có bất kỳ mã nào thiết lập KeySize một cách rõ ràng. Vì vậy, nếu bạn bị cắn bởi điều này, hãy cảm thấy tốt hơn - lỗi có thể không nằm ở bạn mà là do chính .NET Framework.
Usas

6

Vấn đề của tôi là mật khẩu của mã hóa không khớp với mật khẩu của giải mã ... vì vậy nó đã gây ra lỗi này .. hơi gây hiểu lầm.


Trên thực tế, đúng là chúng tôi sử dụng PaddingMode.PKCS7 để Mã hóa và Giải mã nhưng tôi nhận được thông báo lỗi tương tự. Ngoài ra, chúng tôi có môi trường Stage và Dev với các giá trị chính khác nhau. Khi tôi sử dụng khóa-môi trường cụ thể thích hợp - ngoại lệ này đã được giải quyết ...
Major

Mặc dù tất cả các câu trả lời trên đều tốt và bạn phải sử dụng cùng một phần đệm cho Mã hóa và Giải mã (không câu trả lời nào KHÔNG được khuyến khích!) Nhưng thực tế câu trả lời này cũng có thể đúng. Khi tôi sử dụng -en môi trường cụ thể thích hợp - hãy nhập ngoại lệ "System.Security.Cryptography.CryptographicException: Padding không hợp lệ và không thể xóa được." đã được giải quyết. Vì vậy, có nó có thể gây hiểu lầm.
Major

Nếu bằng "passPhrase" bạn đang nói về giá trị chính xác để mã hóa / giải mã (không phải là vấn đề với việc sử dụng sai khóa), thì có, đây là vấn đề của tôi. Trường hợp của tôi là giá trị được mã hóa ban đầu dài hơn trường bảng cơ sở dữ liệu của tôi cho phép nên nó đang bị cắt bớt để vừa với tôi mà tôi không nhận ra. Sau đó, khi giải mã giá trị bị cắt ngắn đó, ngoại lệ này đã được ném ra.
David Gunderson

2

Giải pháp đã khắc phục lỗi của tôi là tôi đã vô tình áp dụng các khóa khác nhau cho các phương pháp Mã hóa và Giải mã.


1

Tôi đã gặp lỗi này khi cố gắng chuyển một đường dẫn tệp không được mã hóa đến phương pháp Giải mã. Giải pháp là kiểm tra xem tệp đã chuyển có được mã hóa hay không trước khi cố gắng giải mã

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

1
Tôi thách thức bất kỳ Hit and Run Coward nào về tính hợp lệ của giải pháp này.
hữu íchXem

+1 vì ngoại lệ này được đưa ra khi bạn giải mã hai lần hoặc bạn giải mã thứ gì đó không được mã hóa. Vì vậy, tôi đọc câu trả lời này là "bạn có chắc rằng dữ liệu thực sự được mã hóa?".
Gerardo Grignoli

0

Một kịch bản khác, một lần nữa vì lợi ích của những người đang tìm kiếm.

Đối với tôi, lỗi này xảy ra trong phương thức Dispose () che đi một lỗi trước đó không liên quan đến mã hóa.

Khi thành phần khác đã được sửa, ngoại lệ này sẽ biến mất.


3
Lỗi trước đó không liên quan đến mã hóa là gì?
NStuke

0

Tôi gặp phải lỗi đệm này khi tôi chỉnh sửa thủ công các chuỗi được mã hóa trong tệp (bằng cách sử dụng notepad) vì tôi muốn kiểm tra chức năng giải mã sẽ hoạt động như thế nào nếu nội dung được mã hóa của tôi bị thay đổi theo cách thủ công.

Giải pháp cho tôi là đặt một

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

Giống như tôi đã nói lỗi đệm của tôi là do tôi đang gõ thủ công văn bản được giải mã bằng notepad. Có thể là câu trả lời của tôi có thể hướng dẫn bạn giải pháp của bạn.


0

Tôi đã có những lỗi giống nhau. Trong trường hợp của tôi, đó là vì tôi đã lưu trữ dữ liệu được mã hóa trong Cơ sở dữ liệu SQL. Bảng mà dữ liệu được lưu trữ, có kiểu dữ liệu nhị phân (1000). Khi truy xuất dữ liệu từ cơ sở dữ liệu, nó sẽ giải mã 1000 byte này, trong khi đó thực sự là 400 byte. Vì vậy, loại bỏ số 0 ở cuối (600) khỏi kết quả nó đã khắc phục được sự cố.


0

Tôi đã gặp lỗi này và đã đặt kích thước khối một cách rõ ràng: aesManaged.BlockSize = 128;

Sau khi tôi xóa nó, nó đã hoạt động.


0

Tôi đã gặp sự cố tương tự khi cố gắng chuyển chương trình Go sang C #. Điều này có nghĩa là rất nhiều dữ liệu đã được mã hóa bằng chương trình Go. Dữ liệu này bây giờ phải được giải mã bằng C #.

Giải pháp cuối cùng là PaddingMode.Nonehay đúng hơn PaddingMode.Zeros.

Các phương pháp mật mã trong 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
}

... và ...

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

Bây giờ, giải mã trong 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;
}

Có thể giải thích vấn đề với phần đệm như thế nào? Ngay trước khi mã hóa, chương trình Go sẽ kiểm tra phần đệm:

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

Phần quan trọng là:

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

Một mảng mới được tạo với độ dài thích hợp, sao cho độ dài là bội số của kích thước khối. Mảng mới này chứa đầy các số không. Sau đó, phương pháp sao chép sẽ sao chép dữ liệu hiện có vào đó. Nó được đảm bảo rằng mảng mới lớn hơn dữ liệu hiện có. Theo đó, có các số không ở cuối mảng.

Vì vậy, mã C # có thể sử dụng PaddingMode.Zeros. Phương án thay thế PaddingMode.Nonechỉ bỏ qua bất kỳ phần đệm nào cũng hoạt động. Tôi hy vọng câu trả lời này hữu ích cho bất kỳ ai phải chuyển mã từ Go sang C #, v.v.


0

Tôi được báo cáo lỗi tương tự bởi khách hàng. Cá nhân tôi không thể repro nó. Nhìn vào mã của các phương thức EncryptDecrypt , cả hai đều có Padding được đặt thành PaddingMode.PKCS7 . Giải mã trông giống như thế này và tôi không thể thấy vấn đề với nó đối với ' FlushFinalBlock '. Ai đó có thể vui lòng làm sáng tỏ về nó không?

public string Decrypt(string cipherText)
{
  if (string.IsNullOrEmpty(cipherText))
    return "";
  string result;
  Encoding byteEncoder = Encoding.Default;

  byte[] rijnKey = byteEncoder.GetBytes(Password);
  byte[] rijnIv = byteEncoder.GetBytes(InitialVector);
  RijndaelManaged rijn = new RijndaelManaged { Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };

  using (MemoryStream msDecrypt = new MemoryStream(Convert.FromBase64String(cipherText)))
  {
    using (ICryptoTransform decryptor = rijn.CreateDecryptor(rijnKey, rijnIv))
    {
      using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
      {
                    using (StreamReader swDecrypt = new StreamReader(csDecrypt))
                    {
                        result = swDecrypt.ReadToEnd();
                    }
                }
    }
  }
  rijn.Clear();      
  return result.Replace("\0", "");
}

0

Tôi đã có những lỗi giống nhau. Trong trường hợp của tôi, mật khẩu đã cho lớn hơn 16 có nghĩa là nó được mã hóa nhưng trong khi giải mã, tôi gặp lỗi này. Mã hóa:

string keyString = "CDFUYP@ssw0rd123";
            var key = Encoding.UTF8.GetBytes(keyString);            
            using (var aesAlg = Aes.Create())
            {
                using (var encryptor = aesAlg.CreateEncryptor(key, aesAlg.IV))
                {
                    using (var msEncrypt = new MemoryStream())
                    {
                        using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                        using (var swEncrypt = new StreamWriter(csEncrypt))
                        {
                            swEncrypt.Write(text);
                        }                          
                        var iv = aesAlg.IV;

                        var decryptedContent = msEncrypt.ToArray();

                        var result = new byte[iv.Length + decryptedContent.Length];

                        Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
                        Buffer.BlockCopy(decryptedContent, 0, result, iv.Length, decryptedContent.Length);

                        var encryptedString = Convert.ToBase64String(result);
                        var decryptedString = Decrypt(encryptedString);
                        if (decryptedString == null)
                        {
                            return null;
                        }
                        return encryptedString;

                    }
                }

Giải mã:

 string keyString = "CDFUYP@ssw0rd123";
            var fullCipher = Convert.FromBase64String(cipherText);
            var iv = new byte[16];
            var cipher = new byte[16];
            Buffer.BlockCopy(fullCipher, 0, iv, 0, iv.Length);
            Buffer.BlockCopy(fullCipher, iv.Length, cipher, 0, iv.Length);
            var key = Encoding.UTF8.GetBytes(keyString);

            using (var aesAlg = Aes.Create())
            {
                using (var decryptor = aesAlg.CreateDecryptor(key, iv))
                {
                    string result;
                    using (var msDecrypt = new MemoryStream(cipher))
                    {
                        using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                        {
                            using (var srDecrypt = new StreamReader(csDecrypt))
                            {
                                result = srDecrypt.ReadToEnd();
                            }
                        }
                    }

                    return result;
                }
            }

Xin chào @sundarraj, đây có phải là một câu hỏi không?
Tiago Martins Peres 李大仁
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.