I have a .pem file and I am trying to generate a PrivateKey out of it so I can create a JWT (JSON Web Token) that is signed by this private key. However, the code breaks when the KeyFactory.generatePrivate(keySpec) is executed. Below is the contents of the .pem file where the key is present and the java file
mysecret1.pem file
-----BEGIN PRIVATE KEY----- MIGkAgEBBDAz2He6GHzqEe+a2MAl/3QyuvnCsGQrFYKmP/F9mX7lhjYBqN/wJBQd ppqjhVPFiF+gBwYFK4EEACKhZANiAAQvEqdP8J+IkWrx2WA9EEblRiZPaRY9drWI aNFGI24rMBbx5SPd+OkLEOPx1Z5QOJlhdkKtwGsNZRklXGBwFwy/sYV+bRt/bt5O 6SfitF4XZvewKafZ8YaxPklhdloWe+I= -----END PRIVATE KEY-----
JwtSigner1.java:
public class JwtSigner1 {
private static final long EXPIRY_DURATION = 1000L * 60 * 60 * 1; // 1 hour
public static void main(String[] args) throws Exception {
File file = new File("mysecret1.pem");
System.out.println(JwtSigner1.generateTokenUsingPemFile(file));
}
public static String generateTokenUsingPemFile(File file) throws Exception {
Security.addProvider(new BouncyCastleProvider());
String key = new String(Files.readAllBytes(file.toPath()), Charset.defaultCharset());
String privateKeyPEM = key.replace("-----BEGIN PRIVATE KEY-----", "").replaceAll(System.lineSeparator(), "")
.replace("-----END PRIVATE KEY-----", "");
byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
Map<String, Object> claims = new HashMap<>();
claims.put("data", " app");
claims.put("namespace", "sample");
return Jwts.builder().setClaims(claims).setExpiration(new Date(System.currentTimeMillis() + EXPIRY_DURATION))
.setIssuedAt(new Date(System.currentTimeMillis())).signWith(privateKey).compact();
}
}
Exception:
Exception in thread "main" java.security.spec.InvalidKeySpecException:
java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 01
at sun.security.rsa.RSAKeyFactory.engineGeneratePrivate(RSAKeyFactory.java:217)
at java.security.KeyFactory.generatePrivate(KeyFactory.java:372)
at c.s.v.d.w.c.util.JwtSigner1.generateTokenUsingPemFile(JwtSigner1.java:41)
at c.s.v.d.w.c.util.JwtSigner1.main(JwtSigner1.java:28)
Caused by: java.security.InvalidKeyException: IOException : version mismatch: (supported: 00, parsed: 01
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:351)
at sun.security.pkcs.PKCS8Key.decode(PKCS8Key.java:356)
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
I can't use the OpenSSL command line, because I have to programmatically use this pem file and directly convert it to a PrivateKey using Java.
I expect a Private Key to be created and using that a JWT token is generated, which is signed by this key.
That's not a PKCS8-format key. The only correct format of the body in a PEM file labelled
BEGIN/END PRIVATE KEYis the PKCS8 unencrypted structurePrivateKeyInfoas defined in RFC5208 section 5 or possibly (but very rarely) itsOneAsymmetricKeyenhancement in RFC5958 section 2 as specified in RFC7468 section 10, so this key file is in violation of standards. And since the only correct format for JavaPKCS8EncodedKeySpecis also the PKCS8 unencrypted format (in binary i.e. not PEM) this file is unusable in Java crypto.And it's not an RSA key. It's an (X9-style) EC private key in the SEC1=RFC5915 structure so even if converted to PKCS8 it can't be handled by a
KeyFactoryfor RSA.However, since X9EC private scalars have length depending only on the curve-group order, and public points have length depending only on the underlying field order and the point format (compressed or not), the SEC1 structure has length depending only on the curve, which options are selected, and if the public-value option is selected which point format is used. As a result the corresponding PKCS8 structure in DER can consist of a prefix that depends only on the curve, SEC1 options, and conditionally point format, plus the SEC1 structure that you have. And you can do that in Java: