Bouncycastle 无法在 C# 中正确解密

Bouncycastle does not decrypt correctly in C#

我试图在 Android 中加密文本并在 C# 应用程序中解密。我的问题是解密时出现标题错误

Android(加密)方:

import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.Security;

import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.SecretKeySpec;

import org.spongycastle.jce.provider.BouncyCastleProvider;

public class AesHelper {

    private SecretKeySpec key;
    private byte[] input;
    private ByteArrayOutputStream output;
    private CipherOutputStream cipherOutput;
    private Cipher encrypt;

    public AesHelper(byte[] chosenKey, String plaintext) {
        Security.addProvider(new BouncyCastleProvider());
        key = new SecretKeySpec(chosenKey, "AES");
        try {
            encrypt = Cipher.getInstance("AES/CBC/PKCS7Padding", "BC");
            encrypt.init(Cipher.ENCRYPT_MODE, key);
            input = plaintext.getBytes();
        } catch (Exception e) {
            Log.d("testclient", e.getMessage());
        }
    }

    public byte[] encrypt() {
        output = new ByteArrayOutputStream();
        cipherOutput = new CipherOutputStream(output, encrypt);
        try {
            cipherOutput.write(input);
            cipherOutput.close();
        } catch (IOException e) {
            Log.d("testclient", e.getMessage());
        }
        return output.toByteArray();
    }
}

调用如下:

String message = "TEST-TEST-TEST-TEST-TEST-TEST-TEST-TEST";
cryptHelper = new AesHelper(key, message);
byte[] cipherMessage = cryptHelper.encrypt();
String finalMessage = bytesToHex(cipherMessage);

C#(解密)端:

public class AesHelper
{
    private readonly Encoding _encoding;
    private readonly IBlockCipher _blockCipher;
    private PaddedBufferedBlockCipher _cipher;

    public IBlockCipherPadding Padding { get; set; }

    public AesHelper()
    {
        _blockCipher = new AesEngine();
        _encoding = Encoding.UTF8;
    }

    public string Encrypt(string plain, byte[] key)
    {
        var result = BouncyCastleCrypto(true, _encoding.GetBytes(plain), key);
        return result.AsHex();
    }

    public string Decrypt(string cipher, byte[] key)
    {
        var result = BouncyCastleCrypto(false, cipher.AsByteArray(), key);
        return _encoding.GetString(result);
    }

    private byte[] BouncyCastleCrypto(bool forEncrypt, byte[] input, byte[] key)
    {
        try
        {
            _cipher = Padding == null
                ? new PaddedBufferedBlockCipher(new CbcBlockCipher(_blockCipher))
                : new PaddedBufferedBlockCipher(new CbcBlockCipher(_blockCipher), Padding);
            _cipher.Init(forEncrypt, new KeyParameter(key));
            return _cipher.DoFinal(input);
        }
        catch (CryptoException ex)
        {
            throw new CryptoException(ex.Message);
        }
    }
}

调用如下:

cryptoHelper = new AesHelper { Padding = new Pkcs7Padding() };
var decryptedMessage = cryptoHelper.Decrypt(message, _aesKey);

问题是,如果我加密字符串

"TEST-TEST-TEST-TEST-TEST-TEST-TEST-TEST" 

解密后的字符串是

"�\a��{ٳ�]%'Ts�EST-TEST-TEST-TEST-TEST"

我真的不知道我做错了什么。

它似乎是第一个输出块(16 字节)是错误的,对于 CBC 模式,这意味着不同的 "initialization vector" (IV) 用于加密和解密。 Android 代码实际上是自动生成一个(随机)IV(当你只用一个键调用 Cipher.init() 时),你可以使用 Cipher.getIV() 检索它。或者,您可以使用不同的 init 方法显式指定 IvParameterSpec

无论您如何操作,解密代码都需要使用相同的 IV,然后您将在 C# 代码中像这样初始化 _cipher:

_cipher.Init(forEncrypt, new ParametersWithIV(new KeyParameter(key), iv));

另请注意,在 Android 代码中:

plaintext.getBytes()

以后可能会给您带来麻烦。最好明确地说 UTF8:

plaintext.getBytes("UTF8")