Meowntain
Meowntain

Reputation: 83

How to add BouncyCastleProvider in C#?

I have a Java encryption routine that I need to decrypt with C#. The Java routine is using a Bouncy Castle line that I've been unable to replicate in C#:

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

This is the only reference to Bouncy Castle in the Java code.

I have no control over the Java side, only the C# side. All of my C# decryption attempts give me junk data, and the only discrepancy I can spot in the Java code compared with my C# code is the lack of Bouncy Castle on the C# side. Does anyone know how I can assign BouncyCastle as the security provider in C#? I have looked through the Bouncy Castle source code, on their site, and online with no luck.


Edit: Given the responses thus far, I have updated my code to use Bouncy Castle. I am adding my C# decryption code and the Java encryption code below. While I am using Bouncy Castle, I still cannot get the decryption to work properly. I must be overlooking something simple but I cannot see it... Any thoughts are appreciated.

C# decryption code:

    public string Decrypt(string stringToDecrypt, string encryption_Key, string init_Vector, string salt)
{
    byte[] SALT = Convert.FromBase64String(salt);
    int iterations = 12345;
    var rfc2898 = new System.Security.Cryptography.Rfc2898DeriveBytes(encryption_Key, SALT, iterations);
    byte[] KEY = rfc2898.GetBytes(16);
    KeyParameter aesKeyParam = ParameterUtilities.CreateKeyParameter("AES", KEY);
    byte[] IV = Encoding.UTF8.GetBytes(init_Vector);
    ParametersWithIV aesIVKeyParam = new ParametersWithIV(aesKeyParam, IV);

    IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/PKCS5Padding");
    cipher.Init(false, aesIVKeyParam);

    //byte[] bytesToDecrypt = Convert.FromBase64String(stringToDecrypt);
        // Gives me "pad block corrupted" error
    //byte[] bytesToDecrypt = Encoding.UTF8.GetBytes(stringToDecrypt);
        // Gives me "last block incomplete in decryption" error
    byte[] bytesToDecrypt = Base64.Decode(Encoding.UTF8.GetBytes(stringToDecrypt));
        // Gives me "pad block corrupted" error
    byte[] output = cipher.DoFinal(bytesToDecrypt);

    return Convert.ToBase64String(output);
}

Java code:

public class testaes {
/**
 * The iteration count for key generation algorithm.
 */
private static final int KEY_ITERATION_COUNT = 12345;

/**
 * The key length in bits.
 */
private static final int KEY_LENGTH = 128;

/**
 * The algorithm for cipher initialization.
 */
private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";

/**
 * The algorithm for key factory selection.
 */
private static final String KEY_FACTORY_ALGORITHM = "PBKDF2WithHmacSHA1";

/**
 * The algorithm for key generation.
 */
private static final String KEY_ALGORITHM = "AES";

/**
 * The byte encoding.
 */
private static final String BYTE_ENCODING = "UTF-8";

private static testaes instance = null;


public testaes() {
    super();
}

public static testaes getInstance() {
    if (instance == null) {
        instance = new testaes();
    }
    Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
    return instance;
}

/**
 * Instantiates the cipher.
 */
private Cipher initCipher(int opmode) throws Exception, NoSuchAlgorithmException, InvalidKeySpecException,
        NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, UnsupportedEncodingException {
    String access = "XXXXX";

    byte[] salt = "XXXXX".getBytes();

    String ivString = "XXXXX";


    // Build the key from password and salt.
    char[] accessCharArray = access.toCharArray();
    byte[] saltByteArray = Base64.decodeBase64(salt);
    SecretKeyFactory factory = SecretKeyFactory.getInstance(KEY_FACTORY_ALGORITHM);
    KeySpec spec = new PBEKeySpec(accessCharArray, saltByteArray, KEY_ITERATION_COUNT, KEY_LENGTH);
    SecretKey tmp = factory.generateSecret(spec);
    SecretKey secretKey = new SecretKeySpec(tmp.getEncoded(), KEY_ALGORITHM);

    // Create a cipher based on AES transformation.
    Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
    // Initialize cipher to with Secret Key and IV.
    cipher.init(opmode, secretKey, new IvParameterSpec(ivString.getBytes(BYTE_ENCODING)));
    return cipher;
}

/**
 * Encrypts the data.
 * 
 * @param originalString
 *            The data to be encrypted.
 * @return The encrypted data as String.
 */
public String encryptAES(String originalString) {
    String encryptedString = null;
    try {
        Cipher cipher = initCipher(Cipher.ENCRYPT_MODE);
        byte[] encryptedBytes = cipher.doFinal(originalString.getBytes());
        String base64Encoded = new String(Base64.encodeBase64(encryptedBytes), Charset.forName(BYTE_ENCODING));
        String urlEncoded = URLEncoder.encode(base64Encoded, BYTE_ENCODING);
        encryptedString = urlEncoded;
    }  catch (Exception e) {
        e.printStackTrace();
    }
    return encryptedString;
}

/**
 * Decrypts the data.
 * 
 * @param encryptedString
 *            The encrypted data that is to be decrypted.
 * @return The decrypted (original) data as string.
 */
public String decryptAES(String encryptedString) {
    String decryptedString = null;

    try {
        Cipher cipher = initCipher(Cipher.DECRYPT_MODE);
        String urlDecoded = URLDecoder.decode(encryptedString, BYTE_ENCODING);
        byte[] encryptedBytes = Base64.decodeBase64(urlDecoded.getBytes(Charset.forName(BYTE_ENCODING)));
        byte[] originalBytes = cipher.doFinal(encryptedBytes);
        decryptedString = new String(originalBytes);
    }  catch (Exception e) {
        e.printStackTrace();
    }
    return decryptedString;
}

Upvotes: 5

Views: 6752

Answers (3)

C0D3
C0D3

Reputation: 6559

I had to do something similar before. My application had to Encrypt data in C# then decrypt it in Java. However, I wrote something that Decrypts data in C# side as well. Here is my code:

public static byte[] Decrypt3(byte[] data, string pemFilename)
{
    try {
        AsymmetricKeyParameter key = readPrivateKey(pemFilename);

        RsaEngine e = new RsaEngine();

        e.Init(false, key);

        byte[] cipheredBytes = e.ProcessBlock(data, 0, data.Length);
        return cipheredBytes;

    } catch (Exception e) {
        Debug.Log ("Exception in Decrypt3: " + e.Message);
        return GetBytes(e.Message);
    }
}

EDIT:

Adding the libraries I have used:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using UnityEngine;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Engines;

**EDIT 2: ** Adding the readPrivateKey function:

static AsymmetricKeyParameter readPrivateKey(string privateKeyFileName)
{
    AsymmetricCipherKeyPair keyPair;

    using (var reader = File.OpenText(privateKeyFileName))
        keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();

    return keyPair.Private;
}

I also asked a few questions around this matter too, if you look at the questions I asked through my profile or searching the site, you might find some help as well.

**Edit 3: ** //Adding Encryption function

public static byte[] Encrypt3(byte[] data, string pemFilename)
    {
        byte[] cipheredBytes = null;
        try {
            AsymmetricKeyParameter key = ReadAsymmetricKeyParameter(pemFilename);

            RsaEngine e = new RsaEngine();

            e.Init(true, key);

            //Debug.Log ("Encryption msg: " + inputMessage);
            //cipheredBytes = GetBytes(inputMessage);
            //Debug.Log ("bytes: " + GetString(cipheredBytes));
            cipheredBytes = e.ProcessBlock(data, 0, data.Length);
        }
        catch (Exception e) {
            Debug.Log (e.Message);  
        }
        return cipheredBytes;
    }

Upvotes: 2

rossum
rossum

Reputation: 15685

In crypto everything needs to match exactly on both sides. That means checking that everything matches, byte for byte. Do your IV's match byte for byte? Do your final keys (after the KDFs) match byte for byte? Do the two cyphertexts match byte for byte? There may have been transmission problems between the two systems.

Do you get any error messages when trying to decrypt, such as a "Bad Padding" error? If you do not, then the padding is being decrypted correctly, even if the bulk of the message is not. That may indicate that the problem is not with the decryption as such, but elsewhere in the system.

Have you tried passing a very simple message, "Hello World!" through the process? How did that do?

Upvotes: 1

Maarten Bodewes
Maarten Bodewes

Reputation: 93958

As codes comments: "In C# you either use the BC library directly, or you don't. This kind of "provider" concept doesn't exist." This is similar to using the "lightweight API" in Java. There are no equivalents to the Java JCE API defined by Oracle / OpenJDK.

Upvotes: 2

Related Questions