3

I have the following public key, that is stored in the DB (PostgresSQL) as text. It's 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 has been generated, I'm sorry. I have been told to take this key and verify the signature of another string that I'll call "object". I have been told that the algorithm that I have to use to verify "object" is SHA256withRSA.

So, I have written the following java method to read the 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 have read plenty of qustions as mine in stackoverflow. The code written by other users is pretty similar (sometimes identical) to mine. So I definitely don't get why it doesn't work for me. Other developers (workmates) are doing the same in php and it works great, so I would discard the hypothesis of wrong public key. Maybe didn't I understood the process clearly? Do you have any clue, please?

I have also tried to cope with the problem using BouncyCastle library, as suggested here, but I get the same exception. The following is the code I have written:

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);
}
Peter
  • 399
  • 2
  • 6
  • 23
  • 1
    Possible duplicate of [Algid parse error, not a sequence](https://stackoverflow.com/questions/6559272/algid-parse-error-not-a-sequence) – Dorian Gray Mar 02 '19 at 11:28
  • Why is he trying to verify the signature with private key instead of public key? – Peter Mar 02 '19 at 11:56
  • I'm reading the question you cited. I'll fix and integrate. Give me some minutes, please. – Peter Mar 02 '19 at 12:23
  • The public key seems to be an RSA4096 bit key (One ASN.1 sequence containing the modulus and the exponent). Do you use a recent version of Java? Otherwise you may be limited by the old crypto restriction policy which forbids RSA keys larger than 2048 bit. – Robert Mar 02 '19 at 12:26
  • I'm using Java 1.8 – Peter Mar 02 '19 at 12:28
  • @DorianGray, I have edited my qustion. Could you please read my edits before closing it? Thank you – Peter Mar 02 '19 at 12:37
  • @Robert, I'm using java 1.8. A solution could be to find out a way to get modulus and exponent from the rsa text? – Peter Mar 02 '19 at 12:38
  • I asked for the exact version, not the main version. Just make sure you are using Java `1.8.0_201`. – Robert Mar 02 '19 at 13:04
  • @Robert, I'm using 1.8.0_171 – Peter Mar 02 '19 at 13:37
  • According to this [answer](https://stackoverflow.com/a/46883499/150978) 1.8.0_171 should be new enough to work with RSA 4096 bit keys. – Robert Mar 02 '19 at 13:49
  • Peter: it's not enough to use PemReader; you must use **PEMParser** as Robert's answer AND #6559272 did. @Robert: Sun/Oracle Java crypto policy (below 8u151) never limited RSA, only symmetric; java 5 supported RSA-4096 just fine. I _think_ IBM may have been different as they had, and still have, their own cryptoproviders, but in any case that would have given a totally different exception. (And OpenJDK once released mid-6 didn't have the policy at all.) – dave_thompson_085 Mar 06 '19 at 14:39
  • @dave_thomson_085: You are wrong RSA was limited, however it seems like this limitation was removed much earlier. At least in Java 1.4 it was limited: http://www.informit.com/articles/article.aspx?p=170967&seqNum=11 – Robert Mar 06 '19 at 16:33

1 Answers1

3

You can't load that key using an X509EncodedKeySpec. According to it's JavaDoc documentation it expects 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 hex data and posted it into the online ASN.1 decoder.

The output is this:

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

As you may recognize your key does not contain an AlgorithmIdentifier therefore it can not be loaded using X509EncodedKeySpec.

My suggestion would be to use the BouncyCastle library and it's PEMParser class for loading 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 manually convert the key to PKCS#8 format via openssl.

Robert
  • 39,162
  • 17
  • 99
  • 152
  • Thank you so much, @Robert. Why now I get "Signature length not correct: got 9575 but was expecting 5122", if I try to sign with that key? – Peter Mar 02 '19 at 14:09
  • Signature signature = Signature.getInstance("SHA256withRSA"); signature.initVerify(getPublicKey(publicKey)); signature.verify(object.getBytes()); – Peter Mar 02 '19 at 14:09
  • @Peter You forgot to pass the data to be verified to your `Signature` instance: https://stackoverflow.com/q/46225417/150978 – Robert Mar 02 '19 at 14:24
  • @Peter I solved your original question, the key is now loaded. Please stop modifying the question - I dislike "moving targets". Search Stackoverflow for similar problems and if you don't find a matching problem open a new question for a new problem instead. – Robert Mar 02 '19 at 16:28
  • This Q is for a public key; PKCS8 is for private key. OpenSSL 1.0.0 up (but not documented until 1.1.0) can convert this format to SPKI (what Java calls X509Encoded and OpenSSL calls PUBKEY) with `openssl rsa -RSAPublicKey_in out` – dave_thompson_085 Mar 06 '19 at 14:43