Java Encryption with OpenSSL key

aesencryptionjavarsa

I have situation where I have generated a public/private key pair using OpenSSL for use with gdcmanon following the instructions listed on their website. Specifically, I used the following commands to generate my keys for gdcmanon

$ openssl genrsa -out CA_key.pem
$ openssl req -new -key CA_key.pem -x509 -days 365 -out CA_cert.cer

I was then able to follow their instructions and encrypt a file using

gdcmanon -c CA_cert.cer -e original.dcm original_anonymized.dcm

and decrpyt a file using

gdcmanon -k CA_key.pem -d original_anonymized.dcm orginal_deanonymized.dcm

I then want to use that key to decode some information in the corresponding DICOM file in Java. After struggling with even getting the key in Java, I found this page and was able to make a key that won't cause my Java program to crash with the following call

openssl pkcs8 -topk8 -inform PEM -outform DER -in CA_key.pem -out CA_key.pkcs8.pem -nocrypt

After all of that, and a lot of reading, I have produced the following Java code

public static String decode(byte[] encryptedData) {
    Key key = readPEMKey(new File("CA_key.pkcs8.pem"));
    //Key key = readPEMKey(new File("CA_key.pem"));
    try {
        Cipher c = Cipher.getInstance("RSA");
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decValue = c.doFinal(encryptedData);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

private static Key readPEMKey(File key) {
    DataInputStream dis = null;
    BufferedReader reader = null;
    try {
        /*
        reader = new BufferedReader(new FileReader(key));
        String privKeyPEM = "";
        String line;
        while ((line = reader.readLine()) != null) {
            if (!line.equals("-----BEGIN RSA PRIVATE KEY-----") && !line.equals("-----END RSA PRIVATE KEY-----"))
            privKeyPEM += line + "\n";
        }
        byte[] encoded = new BASE64Decoder().decodeBuffer(privKeyPEM);
        */

        dis = new DataInputStream(new BufferedInputStream(new FileInputStream(key)));
        byte[] encoded = new byte[(int) key.length()];
        dis.readFully(encoded);

        // PKCS8 decode the encoded RSA private key
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PrivateKey privKey = kf.generatePrivate(keySpec);
        return privKey;
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        if (reader != null) {try {reader.close();} catch (Exception e) {e.printStackTrace();}}
        if (dis != null) {try {dis.close();} catch (Exception e) {e.printStackTrace();}}
    }
    return null;
}

I have tried a number of ways to read and deal with the private keys for decoding in various ways (as you can tell from the commented out sections), however, I have yet to find a solution that will decrypt by encryptedData. If I use a Cipher with the RSA algorithm I get the following stack trace

javax.crypto.IllegalBlockSizeException: Data must not be longer than 256 bytes
at com.sun.crypto.provider.RSACipher.a(DashoA13*..)
at com.sun.crypto.provider.RSACipher.engineDoFinal(DashoA13*..)
at javax.crypto.Cipher.doFinal(DashoA13*..)
at my.app.decode(Application.java:124)

This fails at the line byte[] decValue = c.doFinal(encryptedData);

and if I use a Cipher with the AES algorithm I get the following stack trace

java.security.InvalidKeyException: No installed provider supports this key: sun.security.rsa.RSAPrivateCrtKeyImpl
at javax.crypto.Cipher.a(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at javax.crypto.Cipher.init(DashoA13*..)
at my.app.Application.decode(Application.java:123)

This fails at the line c.init(Cipher.DECRYPT_MODE, key);

I have already installed the JCE for Java 6 (which I'm using). I don't know what I'm doing wrong. Can someone please point me in the right direction.

Thanks

UPDATE

I've been working on this a lot and have tried Bouncy Castle as well with no results yet. Here is my first attempt while using Bouncy Castle.

BufferedReader reader = new BufferedReader(new FileReader(new File("CA_key.pem")));
PEMParser parser = new PEMParser(reader);
PEMKeyPair keyPair = (PEMKeyPair)parser.readObject();
AsymmetricKeyParameter privKeyParams = PrivateKeyFactory.createKey(keyPair.getPrivateKeyInfo());
AsymmetricKeyParameter publicKeyParams = PublicKeyFactory.createKey(keyPair.getPublicKeyInfo());
parser.close();

RSAEngine e = new RSAEngine();
e.init(false, publicKeyParams);
byte[] decValue = e.processBlock(encryptedData, 0, encryptedData.length);

Again I get an exception with the processBlock method
org.bouncycastle.crypto.DataLengthException: input too large for RSA cipher.
I assume this is the same problem that I had before. I'm really at a loss here, because the gdcmanon tool can clearly decrypt this string with just the CA_key.pem file as that is the only input (other than the file to decrypt). I've been poking around a lot in the gdcmanon source and it looks like it's somehow using an AES 256 key, but I just don't see how I get that from the RSA key that I have generated. Can someone please point me in the right direction?

EDIT

I've been going through the gdcmanon source even more, and it looks like their using Cryptographic Message Syntax (CMS). This is all new to me, but it would appear that BC has a bunch of classes related to CMS. Specifically, gdcmanon is using an OpenSSL method called PKCS7_dataDecode to decrypt the data. I've not found good examples on how to do this in Java. Any ideas?

Thanks

Best Answer

After a TON of struggling, I finally have the following code which appears to work. If someone can tell me a way to do this without BC, that would be awesome, but for now I'm just glad that it works.

public static String decode(byte[] encryptedData, String keyFile) {
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(new File(keyFile)));
        PEMParser parser = new PEMParser(reader);
        PEMKeyPair keyPair = (PEMKeyPair)parser.readObject();
        AsymmetricKeyParameter privKeyParams = PrivateKeyFactory.createKey(keyPair.getPrivateKeyInfo());
        parser.close();

        CMSEnvelopedData data = new CMSEnvelopedData(encryptedData);
        RecipientInformationStore recipients = data.getRecipientInfos();

        Iterator it = recipients.getRecipients().iterator();

        if (it.hasNext()) {
            RecipientInformation recipient = (RecipientInformation)it.next();
            byte[] recData = recipient.getContent(new BcRSAKeyTransEnvelopedRecipient(privKeyParams));
            String decryptedValue = new String(recData);
            return decryptedValue;
        }
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    finally {
        if (reader != null) {try {reader.close();} catch (Exception e) {e.printStackTrace();}}
    }
    return null;
}