Is it possible to read the RSA private key of format PKCS1 in JAVA without converting to PKCS8? if yes, sample code is appreciated.
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
Java does not come with out-of-the-box support for PKCS1 keys. You can however use Bouncycastle
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
PrivateKey privateKey = kp.getPrivate();
Related
I am trying to convert EC private key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIE2tzb8O0gBVw2IFOB/B8l1Ztjax3ut4DeNtuC3UMmZ6oAoGCCqGSM49
AwEHoUQDQgAEayT6Tv8zZlpIUOKHEYnmsKZyTaqOHajL0InS4c5tK4fhkHZDSWUa
3tPl1ibIXt0LvaxHk47h0Tc4SGr3Ex8Bhg==
-----END EC PRIVATE KEY-----
to Private Key
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgTa3Nvw7SAFXDYgU4
H8HyXVm2NrHe63gN4224LdQyZnqhRANCAARrJPpO/zNmWkhQ4ocRieawpnJNqo4d
qMvQidLhzm0rh+GQdkNJZRre0+XWJshe3Qu9rEeTjuHRNzhIavcTHwGG
-----END PRIVATE KEY-----
It's very easy when you execute openSsl command like this:
openssl pkcs8 -topk8 -nocrypt -in ec1.pem -out ec2.pem
But i want to do this in Java way and didn't find any solution (tried so many from stackoverflow).
So i have for now following class:
ECNamedCurveParameterSpec ecNamedCurveParameterSpec = ECNamedCurveTable.getParameterSpec("prime256v1");
KeyPairGenerator keyPair = KeyPairGenerator.getInstance("ECDSA", "BC");
// Create a secure random number generator using the SHA1PRNG algorithm
SecureRandom secureRandomGenerator = SecureRandom.getInstance("SHA1PRNG");
keyPair.initialize(ecNamedCurveParameterSpec, secureRandomGenerator);
Then i generate KeyPair and get PrivateKey in ECPrivateKey Object:
KeyPair pair =keyPair.generateKeyPair();
ECPrivateKey privateKey = (ECPrivateKey) pair.getPrivate();
StringWriter sw = new StringWriter();
try (JcaPEMWriter jcaPEMWriter = new JcaPEMWriter(sw);) {
jcaPEMWriter.writeObject(privateKey);
}
String pemFormat = sw.toString();
This string pemFormat is actually PEM format that starts with BEGIN EC PRIVATE KEY
How can i convert it just to BEGIN PRIVATE KEY?
I assume that should be a way if openSsl can do it.
A conversion from the SEC1/PEM (-----BEGIN EC PRIVATE KEY-----...) to the PKCS#8/PEM format (-----BEGIN PRIVATE KEY-----...) is not needed at all, because privateKey.getEncoded() returns the key already in PKCS#8 format. So it only needs to be exported as PEM e.g. with a PemWriter:
import org.bouncycastle.util.io.pem.PemWriter;
...
// Your code
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
...
ECPrivateKey privateKey = (ECPrivateKey)pair.getPrivate();
System.out.println(privateKey.getFormat()); // PKCS#8
// Export as PKCS#8 PEM encoded key via PemWriter
StringWriter stringWriter = new StringWriter();
try (PemWriter pemWriter = new PemWriter(stringWriter)){
pemWriter.writeObject((PemObjectGenerator)new PemObject("PRIVATE KEY", privateKey.getEncoded()));
}
String pkcs8PEM = stringWriter.toString();
System.out.println(pkcs8PEM); // -----BEGIN PRIVATE KEY-----MIGTAg...-----END PRIVATE KEY-----
You can check the format in an ASN.1 parser, e.g. https://lapo.it/asn1js.
However, if you are really looking for an explicit conversion of a SEC1/PEM key into a PKCS#8/PEM key, then the import of a SEC1/PEM key is described e.g. here. The imported key can then be exported as a PKCS#8/PEM key using a PemWriter as in the example above.
I can't generate private key with bouncycastle due to Unknown KeySpec type: java.security.spec.X509EncodedKeySpec. (However doing same for public key doesn't throw exception and works - why?)
java.security.spec.InvalidKeySpecException: Unknown KeySpec type:
java.security.spec.X509EncodedKeySpec at
org.bouncycastle.jcajce.provider.asymmetric.rsa.KeyFactorySpi.engineGeneratePrivate(Unknown
Source) at
java.security.KeyFactory.generatePrivate(KeyFactory.java:366)
PemReader pemReader = new PemReader(new InputStreamReader(new FileInputStream("private_unencrypted.pem")));
PemObject pemObject = pemReader.readPemObject();
pemReader.close();
KeyFactory keyFactory = KeyFactory.getInstance("RSA", "BC");
byte[] privateKeyBytes = pemObject.getContent();
X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(privateKeyBytes);
PrivateKey privateKey = keyFactory.generatePrivate(x509KeySpec);
For RSA private keys you should be using PKCS8EncodedKeySpec if your key is encoded in PKCS8 format. PKCS8 format usually looks like :
-----BEGIN PRIVATE KEY-----
base64 encoded der key
-----END PRIVATE KEY-----
If your key is in PKCS1 format and looks like :
-----BEGIN RSA RIVATE KEY-----
base64 der encoded key
-----END RSA PRIVATE KEY-----
you should first convert it to PKCS8 format and then use the class mentioned above.
However doing same for public key doesn't throw exception and works - why?
Because public keys, which are usually part of Certificates, are encoded in X509 format, however private keys are usually encoded in PKCS format.
I used java to generate public key out of private key as follows,
Security.addProvider(new BouncyCastleProvider());
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
kpg.initialize(2048);
KeyPair kp = kpg.generateKeyPair();
PrivateKey priv = kp.getPrivate();
RSAPrivateCrtKey rsaCrtKey = (RSAPrivateCrtKey) priv;
RSAPublicKeySpec keyspecPublic = new RSAPublicKeySpec(rsaCrtKey.getModulus(), rsaCrtKey.getPublicExponent());
KeyFactory kf = KeyFactory.getInstance("RSA");
PublicKey publicKey2 = kf.generatePublic(keyspecPublic);
String publicString = Base64.getMimeEncoder().encodeToString(publicKey2.getEncoded());
StringBuilder sb = new StringBuilder("");
sb.append("\"-----BEGIN PUBLIC KEY-----\\n")
.append(publicString)
.append("\\n-----END PUBLIC KEY-----");
And I generate public key using openssl as follows for same private key,
openssl rsa -in private.key -pubout -out public.key
But above two methods generate different lengths of strings as public key. Am I doing something wrong here?
public key from java code:
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UkqbaGp7wOx2vrtqxmX/22ixKZcZPfZ
xXAawIst+AsWyNS+99MxnilstLIBKd6BCzJJsAa2I0ks43mnNZbkx1f1um+tQpXuTMbTh5MRprPn
jmX6aF+JXJbXATKhabTIQcCdpnrMi6scp9nWYkFdxVMfvo1gyThzfPwPgCO4eRFo1IkwZuergkl7
e0+U7WonqzFEb0joy5P78U+K8HebDW7nbS8zliq3DH2FI9yvEK3LeEN+Sa5icMWlERGv+7FCJVIH
CjBYfzaRZD9qqld/AnAEkCVt38SfSqWJECsnJYUW90WuFd8IxUVPS0TMfFMQhJFMT3eGweBOUb/b
pDm/hQIDAQAB
-----END PUBLIC KEY-----
Public key from openssl :
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6UkqbaGp7wOx2vrtqxmX
/22ixKZcZPfZxXAawIst+AsWyNS+99MxnilstLIBKd6BCzJJsAa2I0ks43mnNZbk
x1f1um+tQpXuTMbTh5MRprPnjmX6aF+JXJbXATKhabTIQcCdpnrMi6scp9nWYkFd
xVMfvo1gyThzfPwPgCO4eRFo1IkwZuergkl7e0+U7WonqzFEb0joy5P78U+K8Heb
DW7nbS8zliq3DH2FI9yvEK3LeEN+Sa5icMWlERGv+7FCJVIHCjBYfzaRZD9qqld/
AnAEkCVt38SfSqWJECsnJYUW90WuFd8IxUVPS0TMfFMQhJFMT3eGweBOUb/bpDm/
hQIDAQAB
-----END PUBLIC KEY-----
The keys are identical, and will decode to the same byte content.
Formatting-wise, the Java one is wrapped at 76 columns and the OpenSSL one is wrapped at 64 columns.
If you want to make the wrapping consistent, use the Base64.getMimeEncoder(int lineLength, byte[] lineSeparator) overloaded method that lets you specify the line length and pass 64 as the lineLength parameter.
Base64.getMimeEncoder(64, new byte[] {'\r', '\n'});
This question already has answers here:
Decrypting an OpenSSL PEM Encoded RSA private key with Java?
(2 answers)
Closed 5 years ago.
Given this .pem file (generated with openssl and encrypted with a password):
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,AC009672952033EB
2wegzxf3MtncXS1CY3c.....
....
....
-----END RSA PRIVATE KEY-----
How do I get a PrivateKey object in Java? I wrote the following code but I cannot find the right way to get a KeySpec:
PrivateKey readFromPem(File keyFile, String password){
PemReader r = new PemReader(new InputStreamReader(new FileInputStream(keyFile)));
PemObject pemObject = r.readPemObject();
byte[] encodedKey = pemObject.getContent();
KeySpec keySpec = ???? // how to get this?
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey key = keyFactory.generatePrivate(keySpec);
return key;
}
I guess I should build a RSAPrivateKeySpec, but I don't know how. I tried the method from this answer and this other answer, but they both result in errors when parsing the byte array.
I'm using BouncyCastle 1.57 (bcprov-jdk15on, bcmail-jdk15on and bcpkix-jdk15on) and Java 7.
You can read the private key using the JcaPEMKeyConverter class.
The code below works for keys with and without a password:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMDecryptorProvider;
import org.bouncycastle.openssl.PEMEncryptedKeyPair;
import org.bouncycastle.openssl.PEMKeyPair;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
// don't forget to add the provider
Security.addProvider(new BouncyCastleProvider());
String password = "your password";
// reads your key file
PEMParser pemParser = new PEMParser(new FileReader(keyFile));
Object object = pemParser.readObject();
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
KeyPair kp;
if (object instanceof PEMEncryptedKeyPair) {
// Encrypted key - we will use provided password
PEMEncryptedKeyPair ckp = (PEMEncryptedKeyPair) object;
// uses the password to decrypt the key
PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
kp = converter.getKeyPair(ckp.decryptKeyPair(decProv));
} else {
// Unencrypted key - no password needed
PEMKeyPair ukp = (PEMKeyPair) object;
kp = converter.getKeyPair(ukp);
}
// RSA
KeyFactory keyFac = KeyFactory.getInstance("RSA");
RSAPrivateCrtKeySpec privateKey = keyFac.getKeySpec(kp.getPrivate(), RSAPrivateCrtKeySpec.class);
System.out.println(privateKey.getClass());
The privateKey's class will be java.security.spec.RSAPrivateCrtKeySpec (which extends RSAPrivateKeySpec).
Use Bouncy Castle's bcpkix dependency which knows how to handle OpenSSL keys.
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk14</artifactId>
<version>1.57</version>
</dependency>
and try it like this:
private PrivateKey readFromPem(File keyFile, String password) throws IOException {
Security.addProvider(new BouncyCastleProvider());
PEMParser pemParser = new PEMParser(new InputStreamReader(new FileInputStream(keyFile)));
PEMEncryptedKeyPair encryptedKeyPair = (PEMEncryptedKeyPair) pemParser.readObject();
PEMDecryptorProvider decryptorProvider = new JcePEMDecryptorProviderBuilder().build(password.toCharArray());
PEMKeyPair pemKeyPair = encryptedKeyPair.decryptKeyPair(decryptorProvider);
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
return converter.getPrivateKey(pemKeyPair.getPrivateKeyInfo());
}
Is it possible to read the RSA private key of format PKCS1 in JAVA without converting to PKCS8? if yes, sample code is appreciated.
-----BEGIN RSA PRIVATE KEY-----
BASE64 ENCODED DATA
-----END RSA PRIVATE KEY-----
Java does not come with out-of-the-box support for PKCS1 keys. You can however use Bouncycastle
PEMParser pemParser = new PEMParser(new FileReader(privateKeyFile));
JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
Object object = pemParser.readObject();
KeyPair kp = converter.getKeyPair((PEMKeyPair) object);
PrivateKey privateKey = kp.getPrivate();