Reputation: 1279
I'm trying to encrypt text in Android and decrypt it in a C# application. My problem is that the title's error appears when I'm decrypting.
Android (encryption) side:
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();
}
}
Invoked as follows:
String message = "TEST-TEST-TEST-TEST-TEST-TEST-TEST-TEST";
cryptHelper = new AesHelper(key, message);
byte[] cipherMessage = cryptHelper.encrypt();
String finalMessage = bytesToHex(cipherMessage);
C# (decryption) side:
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);
}
}
}
Invoked as follows:
cryptoHelper = new AesHelper { Padding = new Pkcs7Padding() };
var decryptedMessage = cryptoHelper.Decrypt(message, _aesKey);
The thing is, if I encrypt the string
"TEST-TEST-TEST-TEST-TEST-TEST-TEST-TEST"
the decrypted string is
"�\a��{ٳ�]%'Ts�EST-TEST-TEST-TEST-TEST"
I really can't figure out what I'm doing wrong.
Upvotes: 1
Views: 1262
Reputation: 4022
It appears to be the first block of output (16 bytes) that is wrong, which for CBC mode implies a different "initialization vector" (IV) was used for encryption and decryption. The Android code is actually generating a (random) IV automatically (when you call Cipher.init()
with just a key), which you could retrieve using Cipher.getIV()
. Alternatively you can explicitly specify an IvParameterSpec
using a different init method.
However you do it, that same IV needs to be available to the decryption code, and you would then init _cipher like this in the C# code:
_cipher.Init(forEncrypt, new ParametersWithIV(new KeyParameter(key), iv));
Also please note that in the Android code:
plaintext.getBytes()
might cause you problems later. It's better to explicitly say UTF8:
plaintext.getBytes("UTF8")
Upvotes: 1