agne
agne

Reputation: 65

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format

I am trying to get RSA PublicKey for jwt token validation in Java but my attempts fail with this exception: java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: invalid key format.

My code:

    String publicKeyString = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMk5pKytLeUpVaDhLYklLVU51OG0KUk5lRCtrU2tXZEh6MjBvSFZYK0g0cVd4WHlJSTk0MVdJUFU2WFpPc3lMeE9qZU1hb0ZRanJ6bDFwYnZQekUyRQpwMmhlK1BnQ1JteDNqOFlMVVd3dGpuQTVwTTFLWDhpNG5vTUw4RmlWY1U2NkE5RjRkZmRQR2MzY0tQQ2ZPbnorCmtBbW5qRllzajYzRCsrTThYSDRWaS9Vc0V3T1lzU05FR2RncUd2OTlTNHpVRzFzd2FqZ1NnODhvbTVaOC9Ja1AKY01LT3cvWkpvVHNDN3l1VlJnTC9xa3EwaDVkM2lXVXNNdXl1K0xoblRhTko4bW9WQmpJT2lQQkR0cEQyN1lzNgpCSGs1dEdBa3ZHZDg0N3c4SjVEeTFzYWlQS0pxelltcUx5akg3b3VlcERFczdEZ2UxZUlJeno5a1RnSkhKZHVzCnd3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";
    PublicKey publicKey = getPublicKey(Base64.getDecoder().decode(publicKeyString), "RSA");

And getPublicKey function:

private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) {
    PublicKey publicKey = null;
    try {
        KeyFactory kf = KeyFactory.getInstance(algorithm);
        EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
        System.out.println(keySpec.getFormat());

        publicKey = kf.generatePublic(keySpec);
    } catch (NoSuchAlgorithmException e) {
        System.out.println("Could not reconstruct the public key, the given algorithm could not be found.");
    } catch (InvalidKeySpecException e) {
        e.printStackTrace();
        System.out.println("Could not reconstruct the public key");
    }

    return publicKey;
}

I know that there are many similar questions dealing with the same exception being thrown and I've read many of them and tried the solutions but nothing worked. I would appreciate any help.

Upvotes: 6

Views: 6426

Answers (1)

dave_thompson_085
dave_thompson_085

Reputation: 39000

Your data is not the base64 encoding of an actual X.509 publickey block (SubjectPublicKeyInfo) as required by Java crypto. It actually is the base64 encoding of a PEM file which is itself structured and encoded. Moreover this PEM file is labelled as containing 'RSA PUBLIC KEY' which would be the 'raw' PKCS1 form and wrong for Java, but fortunately whatever created this PEM file was defective and the body of the PEM file actually is an X.509 SubjectPublicKeyInfo which should have PEM label 'PUBLIC KEY' per RFC7468 #13. (RFC5280 is a profile of X.509.)

Thus you need to decode your string to PEM, then parse the PEM to get X.509:

        String orig = "LS0tLS1CRUdJTiBSU0EgUFVCTElDIEtFWS0tLS0tCk1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBMk5pKytLeUpVaDhLYklLVU51OG0KUk5lRCtrU2tXZEh6MjBvSFZYK0g0cVd4WHlJSTk0MVdJUFU2WFpPc3lMeE9qZU1hb0ZRanJ6bDFwYnZQekUyRQpwMmhlK1BnQ1JteDNqOFlMVVd3dGpuQTVwTTFLWDhpNG5vTUw4RmlWY1U2NkE5RjRkZmRQR2MzY0tQQ2ZPbnorCmtBbW5qRllzajYzRCsrTThYSDRWaS9Vc0V3T1lzU05FR2RncUd2OTlTNHpVRzFzd2FqZ1NnODhvbTVaOC9Ja1AKY01LT3cvWkpvVHNDN3l1VlJnTC9xa3EwaDVkM2lXVXNNdXl1K0xoblRhTko4bW9WQmpJT2lQQkR0cEQyN1lzNgpCSGs1dEdBa3ZHZDg0N3c4SjVEeTFzYWlQS0pxelltcUx5akg3b3VlcERFczdEZ2UxZUlJeno5a1RnSkhKZHVzCnd3SURBUUFCCi0tLS0tRU5EIFJTQSBQVUJMSUMgS0VZLS0tLS0K";

        String pem = new String(Base64.getDecoder().decode(orig)); // default cs okay for PEM
        String[] lines = pem.split("\n"); lines[0] = lines[lines.length-1] = ""; String body = String.join("", lines);
        // in general split on "\r?\n" (or delete "\r" and split on "\n")
        //or instead:
        //String body = pem.replaceAll("-----(BEGIN|END) RSA PUBLIC KEY-----\n","").replaceAll("\n", ""); // or "\r?\n"
        
        KeyFactory kf = KeyFactory.getInstance("RSA");
        PublicKey key = kf.generatePublic(new X509EncodedKeySpec(Base64.getDecoder().decode(body)));
        // for test only:
        System.out.println ( ((java.security.interfaces.RSAPublicKey)key).getModulus() );

Note your specific data has PEM using single-character (NL aka \n) linebreaks, but PEM in general can use either NL or CRLF (\r\n); see my comments.

Upvotes: 5

Related Questions