Léster
Léster

Reputation: 1279

Bouncycastle does not decrypt correctly in C#

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

Answers (1)

Peter Dettman
Peter Dettman

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

Related Questions