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")
我试图在 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")