11

I have my pair of Public and Private keys and my certificate. Those where created in OpenSSL (why? because I was asked to do it in OpenSSL). Now I want to use those things to make a Java app that uses digital signatures. How can I use my private and public keys to encrypt/decrypt info (and use the certificate to see if data is valid? What are the classes in java that allow me to do that?

My keys are in plain text something like this:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDuyg3h0VbP9iZ6RCxSU6x4WX4
anAwedMVUTqF0WHlvHl1Kiqa6N6TiUk23uXAVUX8RwLFjXWHlG0xwW7mGByA2mX9
5oPQpQFu8C70aMuUotGv87iiLi0UKCZV+9wS9rMdg5LHu1mMPilwgOO6MlyTxKem
-----END PUBLIC KEY-----

UPDATE

I made this code but still can't use the private key to sign a string.

public void encryptHash(String hashToEncrypt, String pathOfKey, String Algorithm) {
    FileInputStream fis = null;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    int len;

        File f = new File(pathOfKey);

        fis = new FileInputStream(pathOfKey);
        len = 0;
        while((len = fis.read()) != -1){
            baos.write(len);
        }

        KeyFactory kf = KeyFactory.getInstance(Algorithm); //Algorithm = "RSA"
        KeySpec keySpec = new PKCS8EncodedKeySpec(baos.toByteArray());
        baos.close();
        PrivateKey privateKey = kf.generatePrivate(keySpec);  //Here's the exception thrown

        Signature rsaSigner = Signature.getInstance("SHA1withRSA");
        rsaSigner.initSign(privateKey);

        fis = new FileInputStream(hashToEncrypt);
        BufferedInputStream bis = new BufferedInputStream(fis);
        byte[] buffer = new byte[1024];
        len = 0;
        while((len = bis.read(buffer)) >= 0){
            try {
                rsaSigner.update(buffer, 0, len);
            } catch (SignatureException ex) {
                Logger.getLogger(DataEncryptor.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        bis.close();

        byte[] signature = rsaSigner.sign();

        System.out.println(new String(signature));
}

the exception I'm gettin' is

dic 09, 2011 12:49:02 PM firmaelectronica.DataEncryptor encryptHash
Grave: null
java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : DER input, Integer tag error
    at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)
    at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)
    at firmaelectronica.DataEncryptor.encryptHash(DataEncryptor.java:40)
    at firmaelectronica.FirmaElectronica.main(FirmaElectronica.java:39)
Caused by: java.security.InvalidKeyException: IOException : DER input, Integer tag error
    at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:361)
    at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:367)
    at sun.security.rsa.RSAPrivateCrtKeyImpl.<init>(RSAPrivateCrtKeyImpl.java:91)
    at sun.security.rsa.RSAPrivateCrtKeyImpl.newKey(RSAPrivateCrtKeyImpl.java:75)
    at sun.security.rsa.RSAKeyFactory.generatePrivate(RSAKeyFactory.java:316)
    at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:213)
    ... 3 more
BRabbit27
  • 275
  • 2
  • 3
  • 7
  • Is there a way to give you my key? It was generated just for test purposes so I don't care sharing it ! – BRabbit27 Dec 09 '11 at 19:28
  • Doing some research. It seems that KeyFactory is the one responsible of this exception, 'cause in fact the key is PKCS#8. – BRabbit27 Dec 09 '11 at 20:50
  • I have another question. My private key has a password, but nowhere in the code I put that password, could it be the problem of the exception? – BRabbit27 Dec 09 '11 at 22:48
  • I had the same question. That was the inspiration for this: http://orbaker.com/java/sslcontext.html –  Apr 22 '12 at 23:49

1 Answers1

10

If you have a public key in this form (and not within a certificate), I'd recommend using BouncyCastle's PEMReader. Its readObject() method can read a lot for formats: public keys, certificates, private keys (although you may need to use the method with a password)...

If you don't want to use BouncyCastle, you can read certificates using a CertificateFactory (see examples). With a certificate in PEM format in an InputStream:

CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(inputStream);

For private keys, if your private key is a PKCS#8 structure in DER format, you can read it directly using PKCS8EncodedKeySpec. For example:

KeyFactory kf = KeyFactory.getInstance("RSA");
// Read privateKeyDerByteArray from DER file.
KeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyDerByteArray);
PrivateKey key = kf.generatePrivate(keySpec);

You can convert your private key into PKCS#8 using openssl pkcs8 -topk8 (remember -outform DER, you may also want to check the cipher suites as not all might be commonly supported by both Java and OpenSSL).

  • From a keystore usage point of view:

If you don't want to do much programming for handling the keys, to go between Java and OpenSSL, it's convenient to use the PKCS#12 format.

If the keys and certs you have produced with OpenSSL are not already in a p12 container:

openssl pkcs12 -export -in cert.pem -inkey key.pem -out store.p12

In general, you can make use of the directly, using Java's "PKCS12" keystore type (instead of "JKS" by default).

If needed, you can convert this PKCS12 keystore into another format (e.g. JKS) using keytool (Java 6+):

keytool -importkeystore -srckeystore store.p12 -srcstoretype PKCS12 \
     -destkeystore store.jks -deststoretype JKS

(Essentially, the opposite operation as the one described in this question.)

Either way, whether from using PEMReader or by loading your key/cert from a KeyStore, you should be able to get instances of PrivateKey and Certificate (or PublicKey directly).

You can verify the signature of a Certificate has been done using by the private key matching a given public key using its verify(PublicKey) method.

With them, you can also use the digital signature API. It's a more general API for any document signature, and I wouldn't necessarily verify a certificate signature with it (I'd rather use the certification path API for this, since it will also build the chain).

Bruno
  • 10,765
  • 1
  • 39
  • 59
  • Yep thanks ! And If I don't want to use third party APIs? Is there a native Java API to use private/public RSA keys and certificates? Should I create new keys in PKCS#12 format? How can I check if my keys are in that format? – BRabbit27 Dec 09 '11 at 00:16
  • I've edited my answer for direct usage of the API (but BC makes the whole process a bit easier). I gave you the `openssl` command to build a `p12` file from a cert and key in PEM format (if you have those in `.pem` or `.crt` file for example). Extensions are just a convention, so it depends on how you actually created the key/cert. What you've pasted in your question looks like PEM (or a variant thereof), but that's just a public key, not a certificate. – Bruno Dec 09 '11 at 00:45
  • Thanks. I've done a more deep research and actually the key and cert are in DER format, so they are in pkcs8 arent they? So if I understand well the info you gave me I can use PKCS8EncodedKeySpec, am I right? (The key I posted was actually a PEM one but the original file is in DER, and all the other keys and certs are in DER) Thanks I'll try this out ! thanks ! – BRabbit27 Dec 09 '11 at 16:55
  • No, DER is essentially a binary version of PEM (which is base64-encoded of DER, within `----` separators). They're more different encodings of the same structures, rather than different data structures. Most OpenSSL commands allow you to choose between PEM and DER using `-inform` and `-outform`. DER doesn't imply PKCS#8 at all. One way of finding out which formats are used is to try to read your files using `openssl x509` or `openssl rsa` (with `-text` option for example). Check their man-pages. – Bruno Dec 09 '11 at 17:04
  • Ok. I'll check it and thanks for correcting me. I'm sure they are in PKCS#8 but I did a big mistake saying that DER implies PKCS#8. Anyway I'll try what you said and just to confirm that they are in PKCS#8. Thanks. – BRabbit27 Dec 09 '11 at 17:41
  • Is a PEM public key the same as X.509? (It looks like that in your answer.) – miho Mar 29 '14 at 20:26
  • 1
    @miho, no, I was just talking of ways to read them. `PEMReader` will try its best to read anything it can. A certificate has more information than just a public key (it binds a public key to an identity and other attributes). – Bruno Mar 30 '14 at 11:19
  • 1
    Mostly concur, but: `openssl pkcs8 -topk8` supports several PBE ciphers (not cipher suites) to encrypt PKCS8, but standard Java crypto (non-BC) can't read _any_ encrypted PKCS8, only clear; use `-nocrypt` for that, or in 1.0.0 up use `pkey` which _defaults_ to PKCS8 unencrypted. Or as you say instead use PKCS12 as keystore, which Java does handle encrypted; here again there are several PBE options but OpenSSL and Java defaults are compatible. – dave_thompson_085 Sep 16 '16 at 04:11