Java – How to load an RSA public key from a String for signature verification in Java?

How to load an RSA public key from a String for signature verification in Java?… here is a solution to the problem.

How to load an RSA public key from a String for signature verification in Java?

I have the following public key, which is stored in text form in a database (PostgresSQL). It is a string, in java:

-----BEGIN RSA PUBLIC KEY-----     
MIICCgKCAgEA1ht0OqZpP7d/05373OE7pB7yCVGNGzkUEuCneyfOzps6iA03NbvI
1ZL0Jpp/N3AW73lGdhaoa3X3JE4GsI/bsToVLQwTKmIOC4yjTvBctmFEoyhhTfxW
s1UHZKl4XZ/7THbRlKHhRaTKyfDAbikkMAxNT/qutLAPjnN1qOwjb1oRq52NP6FJ
KWTTikz4UeOHroX+Xthn2fJSJDlQ4YMdBbgrZVx5JcHKNuPTKRf5gI8QQKMSA9Q9
QJRE5OGp7b6dG14ZmOUnUxb00Mp20LgcaGPcuWU+oFsbQaF6W4G4bdkSZRJJXhSg
d4Q7mahpar94/gnztJmth0GzqTWUYyZIWNqIFoMwuOgeaiDV43zb3uLsRVpRKYYy
esmzcOy/jTScVLRCD8QRyu9B2wgCkNAVztQOXPCOOa4O1LlVQWaecIs4WPhOqDhi
KTBhyVkpC1TrrBkp+QMqMqWll1OyVb6k/7uV0qE/i6rHJtjo5v9bcIgYzswyx9CD
9PKl2Q0L0Jg7TMG+yLDIrLfGeuSeEc4XYJzN7bJcCeiizzu5iU9dQUkrncOrq9jn
Ub2pM/+A+JqIsoPK3IY/pJKqH4JYpGKhO1iPQF6iXIZT1r3ZgJUSQtzSeyYqhkla
2uR2BsbPbDqebCuXm3lAsY5w+dujijcn96PKwYha1LsK5sACHuJ79AMCAwEAAQ==
-----END RSA PUBLIC KEY-----

I don’t know how this key is generated, sorry. I was told to take this key and verify the signature of another string I called an “object”. I was told that the algorithm I have to use to validate the “object” is SHA256withRSA.

So, I wrote the following java method to read key

private PublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException, UnsupportedEncodingException {
    publicKey = publicKey.replaceAll("\\n", "");
    publicKey = publicKey.replace("-----BEGIN RSA PUBLIC KEY-----", "");
    publicKey = publicKey.replace("-----END RSA PUBLIC KEY-----", "");
    publicKey = publicKey.trim();
    byte[] keyDecoded = Base64.getDecoder().decode(publicKey.getBytes());
    X509EncodedKeySpec publicSpec = new X509EncodedKeySpec(keyDecoded);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    PublicKey pubKey = kf.generatePublic(publicSpec);
    return pubKey;
}

The point is that I get the following exception:

java.security.InvalidKeyException: IOException: algid parse error, not
a sequence

I read a lot of questions in stackoverflow. Other users write code that is very similar (sometimes the same) to mine. So I absolutely don’t understand why it doesn’t work for me. Other developers (colleagues) are doing the same thing in php and it works great, so I’ll give up the assumption of a wrong public key. Maybe I don’t have a clear understanding of the process? Do you have any clues?

I also tried using the BouncyCaSTLe library to solve this problem, as suggested here , but I get the same exception. Here is the code I wrote:

private static PublicKey getPublicKey(String publicKey)
        throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
    Security.addProvider(new BouncyCastleProvider());
    PemReader pp = new PemReader(new StringReader(publicKey));
    PemObject pem = pp.readPemObject();
    byte[] content = pem.getContent();
    pp.close();

X509EncodedKeySpec spec = new X509EncodedKeySpec(content);
    KeyFactory kf = KeyFactory.getInstance("RSA");
    return kf.generatePublic(spec);
}

Solution

You cannot load the key using X509EncodedKeySpec. According to its JavaDoc documentation It requires the following format:

SubjectPublicKeyInfo ::= SEQUENCE {
   algorithm AlgorithmIdentifier,
   subjectPublicKey BIT STRING }

Instead, your key looks different. I used the data from your post, converted it to hexadecimal data and posted it onlineASN.1 decoder .

The output is this:

SEQUENCE (2 elem)
  INTEGER (4096 bit) 873481340827968071893572683200799871431146795599597693981565010037737...
  INTEGER 65537

As you probably recognize, your key does not contain an AlgorithmIdentifier, so it cannot be loaded using X509EncodedKeySpec.

My suggestion is to use the BouncyCaSTLe library and its PEMParser class to load this key:

File pemFile = new File("test.pem");
try (PEMParser pp = new PEMParser(new InputStreamReader(new FileInputStream(pemFile)))) {
    SubjectPublicKeyInfo subjPubKeyInfo = (SubjectPublicKeyInfo) pp.readObject();
    RSAKeyParameters rsa = (RSAKeyParameters) PublicKeyFactory.createKey(subjPubKeyInfo);

RSAPublicKeySpec rsaSpec = new RSAPublicKeySpec(rsa.getModulus(), rsa.getExponent());
    KeyFactory kf = KeyFactory.getInstance("RSA");
    java.security.PublicKey publicKey = kf.generatePublic(rsaSpec);
    System.out.println(publicKey);
}

Or you can manually convert the key to PKCS#8 format via openssl.

Related Problems and Solutions