Reputation: 2516
I use openssl to generate public and private DSA
keys and run the following commands:
openssl dsaparam -out dsaparam.pem 1024
openssl gendsa -out dsaprivkey.pem dsaparam.pem
openssl req -new -x509 -key dsaprivkey.pem -out dsacert.pem
And use the following two methods to load these keys:
public static PrivateKey loadPrivateKey() throws Exception {
String privateKeyPEM = FileUtils.readFileToString(new File("/Keys/dsaprivkey.pem"), StandardCharsets.UTF_8);
// strip of header, footer, newlines, whitespaces
privateKeyPEM = privateKeyPEM
.replace("-----BEGIN DSA PRIVATE KEY-----", "")
.replace("-----END DSA PRIVATE KEY-----", "")
.replaceAll("\\s", "");
// decode to get the binary DER representation
byte[] privateKeyDER = Base64.getDecoder().decode(privateKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PrivateKey privateKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyDER));
return privateKey;
}
public static PublicKey loadPublicKey() throws Exception {
String publicKeyPEM = FileUtils.readFileToString(new File("/Keys/dsacert.pem"), StandardCharsets.UTF_8);
// strip of header, footer, newlines, whitespaces
publicKeyPEM = publicKeyPEM
.replace("-----BEGIN CERTIFICATE-----", "")
.replace("-----END CERTIFICATE-----", "")
.replaceAll("\\s", "");
// decode to get the binary DER representation
byte[] publicKeyDER = Base64.getDecoder().decode(publicKeyPEM);
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyDER));
return publicKey;
}
However, I can't get read neither public nor private key.
When I try to read public key, I get:
java.security.spec.InvalidKeySpecException: Inappropriate key specification: IOException: ObjectIdentifier() -- data isn't an object ID (tag = -96)
And when I try to read private key, I get:
Exception in thread "main" java.security.spec.InvalidKeySpecException: Inappropriate key specification: IOException : algid parse error, not a sequence
If someone faced the same situation, I will really appreciate any help with regards to this problem
Upvotes: 2
Views: 1758
Reputation: 38771
You aren't generating private and public key, you're generating private key and (X.509) certificate. A certificate contains a public key but is not the same as a public key.
To read an X.509 certificate in Java, use CertificateFactory
(usually directly from a file) (see javadoc online) not KeyFactory
(on a key-dependent *Spec
class). Unlike KeyFactory
(s), CertificateFactory
can handle either PEM or DER input, so you don't need to do the strip-BEGIN/END and de-base64 yourself (unless you want to).
For the private key you have a problem similar to but not the same as the one jww commented about. For a public key file (which you don't have) OpenSSL does use by default the SubjectPublicKeyInfo format that Java calls X509EncodedKeySpec
and OpenSSL internally calls PUBKEY
. However for a private key file OpenSSL uses both its own 'legacy' formats and the standardized PKCS8 format, but Java's PKCS8EncodedKeySpec
supports only the second of these, your commands are using the first, and converting between them in code is not trivial. You have three or maybe four choices:
use openssl to convert to PKCS8-unencrypted:
openssl pkcs8 -topk8 -nocrypt -in dsaprivkey.pem -out dsaprivfixed.pem
# or in 1.0.0 up
openssl pkey -in dsaprivkey.pem -out dsaprivfixed.pem
Now you can read this with the posted code except the lines to remove are -----BEGIN PRIVATE KEY-----
and -----END PRIVATE KEY-----
without DSA
. As a small improvement, if you use getMimeDecoder
you don't need to strip the whitespace yourself.
generate PKCS8 (unencrypted) to start with, in openssl 1.0.0 up:
# you can generate the key within req instead of separately:
openssl dsaparam -out params size
openssl req -new -newkey dsa:params -x509 -nodes -keyout private.pem \
-out cert.pem
# -nodes really means "don't encrypt key" for hysterical raisins
# or in config file set encrypt_rsa_key=no (yes even for other algos!)
# or omit it and set encrypt_key=no (more sensible)
#
# or you can use (new) genpkey instead of (old) gendsa:
openssl dsaparam -out params size
openssl genpkey -paramfile params -out private.pem
openssl req -new -x509 -key private.pem -out cert.pem
Now you're in the same situation as above.
use the PEM functionality in BouncyCastle to read the legacy format.
See Reading elliptic curve private key from file with BouncyCastle
and How to Load RSA Private Key From File
read the legacy format (as you're doing now) and rebuild the PKCS8 ASN.1 encoding for it and then use KeyFactory
on the PKCS8. This is a good deal more complicated and I don't recommend it. I do have a worked example for RSA (which is simpler) somewhere but I'm having trouble finding it; will add later if possible.
Upvotes: 3