views:

1974

answers:

2

I'm trying to use a PEM(X.509) certificate (stored in a privateKey.pem file on disk) to sign messages sent via sockets in Java, but am having a lot of trouble finding an example that's close. I'm normally a C++ guy who's just stepping in to help on this project, so it's been a little difficult for me to put it all together into code that works when I'm unfamiliar with the APIs.

Unfortunately, I'm limited to methods that come standard with Java (1.6.0 Update 16), so although I found a similar example using BouncyCastle's PEMReader, it hasn't helped much on this particular project.

My privateKey.pem key is passphrase protected, in the form of:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED DEK-Info:
DES-EDE3-CBC,63A862F284B1280B

[...]
tsjQI4H8lhOBuk+NelHu7h2+uqbBQzwkPoA8IqbPXUz+B/MAGhoTGl4AKPjfm9gu OqEorRU2vGiSaUPgDaRhdPKK0stxMxbByUi8xQ2156d/Ipk2IPLSEZDXONrB/4O5
[...]
-----END RSA PRIVATE KEY-----

They key was generated using OpenSSL:

openssl.exe genrsa -out private_key.pem 4096

I am unable to convert this key to a DER or other format prior to runtime, any conversions necessary will need to be done internally in code, as the key needs to be easily replaceable and the format will remain PEM.

I've heard a mix of things which I'm not entirely sure about, and was hoping the collective minds here at SO could help pull the pieces together.



I've heard it said that the PEM certificate needs Base64 Decoded to convert it into a DER certificate that can be used. I have a Base64 Decoding tool called MiGBase64 but am not entirely sure how/when this decoding needs done.


I've gotten lost in the Java API docs trying to track down the 15 different types of Keys, KeyStores, KeyGenerators, Certificates, etc that exist, but I'm not familiar enough with any of them to properly identify which I need to be using, and how to use them together.


The basic algorithm seems pretty simple, which is why it has been particularly frustrating that I haven't been able to write an equally simple implementation:

1) Read the privateKey.pem from file
2) Load the private Key into XXX class, using the Passphrase to decrypt the key
3) Use the key object with the Signature class to sign the message



Help with this, especially example code, is greatly appreciated. I've been struggling to find useful examples for this problem, as most 'close' examples are generating new keys, using BouncyCastle, or just otherwise using different forms of keys/classes not applicable here.

This seems like a really simple problem but it's driving me crazy, any really simple answers?

+1  A: 

If you're using BouncyCastle, try the following:

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.security.KeyPair;
import java.security.Security;
import java.security.Signature;
import java.util.Arrays;

import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMReader;
import org.bouncycastle.openssl.PasswordFinder;
import org.bouncycastle.util.encoders.Hex;

public class SignatureExample {

    public static void main(String [] args) throws Exception {
        Security.addProvider(new BouncyCastleProvider());

        String message = "hello world";
        File privateKey = new File("private.pem");
        KeyPair keyPair = readKeyPair(privateKey, "password".toCharArray());

        Signature signature = Signature.getInstance("SHA256WithRSAEncryption");
        signature.initSign(keyPair.getPrivate());
        signature.update(message.getBytes());
        byte [] signatureBytes = signature.sign();
        System.out.println(new String(Hex.encode(signatureBytes)));

        Signature verifier = Signature.getInstance("SHA256WithRSAEncryption");
        verifier.initVerify(keyPair.getPublic());
        verifier.update(message.getBytes());
        if (verifier.verify(signatureBytes)) {
            System.out.println("Signature is valid");
        } else {
            System.out.println("Signature is invalid");
        }
    }

    private static KeyPair readKeyPair(File privateKey, char [] keyPassword) throws IOException {
        FileReader fileReader = new FileReader(privateKey);
        PEMReader r = new PEMReader(fileReader, new DefaultPasswordFinder(keyPassword));
        try {
            return (KeyPair) r.readObject();
        } catch (IOException ex) {
            throw new IOException("The private key could not be decrypted", ex);
        } finally {
            r.close();
            fileReader.close();
        }
    }

    private static class DefaultPasswordFinder implements PasswordFinder {

        private final char [] password;

        private DefaultPasswordFinder(char [] password) {
            this.password = password;
        }

        @Override
        public char[] getPassword() {
            return Arrays.copyOf(password, password.length);
        }
    } 
}
Kevin
Thanks for the time and effort that went into your answer. I had to make a few modifications to get it working with my keys, but you lead the way. Much appreciated!
KevenK
@KevenK Good to hear. What modifications were necessary?
Kevin
A: 

The OpenSSL command generate key-pair and encodes it in PKCS#1 format. If you don't use the encryption (didn't provide password for the command), the PEM is simply Base64-encoded DER for the PKCS#1 RSAPrivateKey.

Unfortunately, Sun's JCE doesn't provide a public interface to read the key in this format. You have 2 options,

  1. Import the key into keystore and you can read it from there. Keytool doesn't allow for importing of private keys. You can find other tools to do this.

  2. OAuth library has a function to handle this. Look at code here,

http://oauth.googlecode.com/svn/code/java/core/commons/src/main/java/net/oauth/signature/pem/PKCS1EncodedKeySpec.java

ZZ Coder
Useful information, thanks for taking the time to answer :) I went with BouncyCastle because it seemed to fit my particular use, but I appreciate the help. Thanks
KevenK