Search code examples
javaopensslrsabouncycastleprivate-key

RSA - bouncycastle PEMReader returning PEMKeyPair instead of AsymmetricCipherKeyPair for reading private key


I have a function that successfully reads a openssl formatted private key:

static AsymmetricKeyParameter readPrivateKey(string privateKeyFileName)
{
    AsymmetricCipherKeyPair keyPair;

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

    return keyPair.Private;
}

and returns an AsymmetricKeyParameter which is then used to decrypt an encrypted text.

Below is the decrypt code:

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

        RsaEngine e = new RsaEngine();

        e.Init(false, key);
        //byte[] cipheredBytes = GetBytes(encryptedMsg);

        //Debug.Log (encryptedMsg);

        byte[] cipheredBytes = e.ProcessBlock(data, 0, data.Length);
        //result = Encoding.UTF8.GetString(cipheredBytes);
        //return result;
        return cipheredBytes;

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

These work in C# using bouncy castle library and I get the correct decrypted text. However, when I added this to Java, the PEMParser.readObject() returns an object of type PEMKeyPair instead of AsymmetricCipherKeyPair and java throws an exception trying to cast it. I checked in C# and it is actually returning AsymmetricCipherKeyPair.

I don't know why Java behaves differently but I hope someone here can help how to cast this object or read the privatekey file and decrypt successfully. I used the same public and privatekey files in both C# and Java code so I don't think the error is from them.

Here for reference the Java version of how I'm reading the privatekey:

public static String readPrivateKey3(String pemFilename) throws FileNotFoundException, IOException
{
    AsymmetricCipherKeyPair keyParam = null;
    AsymmetricKeyParameter keyPair = null;
    PEMKeyPair kp = null;
    //PrivateKeyInfo pi = null;

    try {
        //var fileStream = System.IO.File.OpenText(pemFilename);
        String absolutePath = "";
        absolutePath = Encryption.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        absolutePath = absolutePath.substring(0, (absolutePath.lastIndexOf("/")+1));
        String filePath = "";
        filePath = absolutePath + pemFilename;

        File f = new File(filePath);
        //return filePath;

        FileReader fileReader  = new FileReader(f);
        PEMParser r = new PEMParser(fileReader);

        keyParam = (AsymmetricCipherKeyPair) r.readObject();

        return keyParam.toString();

    }
    catch (Exception e) {
        return "hello: " + e.getMessage() + e.getLocalizedMessage() + e.toString();
        //return e.toString();
        //return pi;
    }
}

Solution

  • The Java code has been updated to a new API, which is yet to be ported across to C#. You could try the equivalent (but now deprecated) Java PEMReader class. It will return a JCE KeyPair though (part of the reason for the change was because the original version worked only with JCE types, not BC lightweight classes).

    If using PEMParser, and you get back a PEMKeyPair, you can use org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter.getKeyPair to get a JCE KeyPair from it. Ideally there would be a BCPEMKeyConverter, but it doesn't appear to have been written yet. In any case, it should be easy to make an AsymmetricCipherKeyPair:

    PEMKeyPair kp = ...;
    AsymmetricKeyParameter privKey = PrivateKeyFactory.createKey(kp.getPrivateKeyInfo());
    AsymmetricKeyParameter pubKey = PublicKeyFactory.createKey(kp.getPublicKeyInfo());
    new AsymmetricCipherKeyPair(pubKey, privKey);
    

    Those factory classes are in the org.bouncycastle.crypto.util package.