Friday, May 15, 2009

RSA Public Key Cryptography in Java

This content has moved permanently to:
https://blog.jonm.dev/posts/rsa-public-key-cryptography-in-java/

25 comments:

Nyuk Fah said...

Well Done!!

After months of searching, finally solve the openssl DER format.

Thank you very much

Anonymous said...

Thanks a lot!!!

towkach said...

You saved my job!!!

Fred Sauter said...

Had I had this advice some six years earlier! :-)

Anonymous said...

How we generate public privet key without oopenssl. can we use tynica as somekind of CA to generate keys.
thanks in advance.

Jon Moore said...

@Anonymous: Here you go: http://tinyurl.com/ygwvqow.

JetForMe said...

Thanks, Jon, for a clear and concise post. I'm trying to duplicate the functionality of this command in Java, and I've gotten most of the way there thanks to your help, but in the end, I get a different signature:

$ openssl dgst -sha1 -binary < SomeFileToSign | openssl dgst -dss1 -sign myprivatekey.pem > openssl.sig

I did the PEM->DER conversion as you suggest and am able to sign without exceptions, but the resulting bytes are different.

I've verified that I get the same SHA1 digest. I'm creating a KeyFactory of "DSA" type, and a Signature of "DSS" type. I figure I've left out some configuration of Signature, and I'm hoping you might know what it is off the top of your head. Thanks much!

JetForMe said...

Well, that was dumb. I realize now that the signature is different every time. Clearly, I need to figure out how to verify the signature generated by Java using OpenSSL, but I can't figure out the right command.

Thanks anyway!

Jon Moore said...

@JetForMe: Not being as familiar with the DSA-DSS signatures as I am with RSA-SHA1, I'm not sure I can directly help.

There are often configuration options you can pass to the Signature.getInstance after the normal algorithm name that affect things like padding schemes, etc. Perhaps you need to more specifically specify the variant of Signature algorithm you want so that it matches what OpenSSL is doing.

Federico said...

I think I love you! I wasted one week trying to understand the right key format to give to Java to get this stupid PublicKey object, and nothing was working!

By the way, can anyone explain me why if I use PublicKey.toString() I get the key in nice clear text, but not if I use new String(PublicKey.getEncoded())?
How to I decode a DER key to see it in clear text?(and where the hell is the toString function overridden in PublicKey?)

Jon Moore said...

@Federico: glad the article helped.

DER format is a binary format, which explains why you aren't seeing anything intelligible from new String(key.getEncoded()) -- you're basically saying "take this fairly random set of bytes and pretend it's a string", whereas PublicKey.toString() is explicitly meant to give you a proper String representation of the key.

toString() is probably being overridden in your cryptography provider (e.g. BouncyCastle or the like); that's where the object implementing the Key/PublicKey/RSAPublicKey interface hierarchy lives.

Federico said...

Thank you again for the explanation, and since you are so knowledgeable I would like to bother you with one more question if I may:
I tried to extract the public key from the CAcert root certificate in txt format (https://www.cacert.org/certs/root.txt), and use it in java, but of course without success.
Do you have any idea about how one might convert it into the correct DER format (since it is the only one Java understands)?

Thanks in advance, and again, great article :)

Jon Moore said...

@Federico: without having lots of time to debug, you may see a plaintext section of that cert URL that has "BEGIN CERTIFICATE" and "END CERTIFICATE" lines. This should be a PEM-format public key; you ought to be able to use an openssl command similar to the ones in the article here to convert that to DER format.

Federico said...

I see, so that was indeed a key.
Thank you for the help, but as I feared, there is no pure programmatic way to handle and convert these keys in java. One must always rely on openssl...

Anonymous said...

Hi, thank you for this great article.
I managed to load my openssl RSA keys into java, but the original openssl key is 1024 bits, java is creating a PrivateKey with 4096 bits, thus resulting in a padding error in the cipher algorithm.
Any suggestions, why I got a mismatch in the keylength?
Thank you very much

loreii said...
This comment has been removed by the author.
Anonymous said...

Here is a small piece that accepts the PEM files directly no need of conversion to DER

public static PrivateKey getPriv(String filename) throws Exception {
File f = new File(filename);
FileInputStream fis = new FileInputStream(f);
DataInputStream dis = new DataInputStream(fis);
byte[] keyBytes = new byte[(int) f.length()];
dis.readFully(keyBytes);
dis.close();

//byte[] temp = new byte[keyBytes.length];
String temp = new String(keyBytes);
String privKeyPEM = temp.replace("-----BEGIN RSA PRIVATE KEY-----\n", "");
privKeyPEM = privKeyPEM.replace("-----END RSA PRIVATE KEY-----", "");
//System.out.println("Private key\n"+privKeyPEM);

Base64 b64 = new Base64();
byte [] decoded = b64.decode(privKeyPEM);
//dumphex(decoded,"PrivateKey");


PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(decoded);
KeyFactory kf = KeyFactory.getInstance("RSA");
return kf.generatePrivate(spec);

}

Anonymous said...

When I tried to read private_key.pem from the code specified, I am getting
Exception in thread "main" java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException : algid parse error, not a sequence
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(Unknown Source)
at java.security.KeyFactory.generatePrivate(Unknown Source)
at AsymmetricCipherTest.getPriv(AsymmetricCipherTest.java:286)
at AsymmetricCipherTest.main(AsymmetricCipherTest.java:59)

Anonymous said...

can any one give me complete code to use openssl generated key in rsa and dsa encryption and decryption

Anonymous said...

You're a genius my friend. Saved us hours of stupid debugging!!! Thanks!

Amber Salm said...
This comment has been removed by a blog administrator.
Ken Conway said...

Thank you! After many hours of searching the web, finally a post that worked!

noble said...

I am signing a file with the command
openssl dgst -sha1 -sign mykey.pem -out my.jar.jar.sha1 my.jar

later on I used the following to verify the signature.
It returns false always. The publickey was created using the above code

public static boolean verify(PublicKey publicKey, byte[] sig, byte[] data) throws InvalidKeyException, SignatureException {
Signature signature= null;
try {
signature = Signature.getInstance("SHA1withRSA");
signature.initVerify(publicKey);
signature.update(data);
boolean verify = signature.verify(sig);
return verify;

} catch (NoSuchAlgorithmException e) {
//will not happen
}
return false;
}

noble said...

ignore my question. It failed because I sent the digest to the verify method instead of the whole file

Unknown said...

Really good!!! This article and this blog helped me making a program to create and verify Http-signature :-)
http://niels.nu/blog/2016/java-rsa.html