Elist
Elist

Reputation: 5533

RSA - Java's KeyFactory fails where bouncycastle's PEM parser succeeds

I have a PEM formatted public key (received from an iOS device):

String pemPubKey = ""+
"-----BEGIN RSA PUBLIC KEY-----\n"+
"MIIBCgKCAQEA07ACQHTTrgX7ddNtyamh58xwD+S+pSrJz/Rah4zj0HIg4V/Ok5vk\n"+
"Wx6y4UyuKLCtefeiB2ipg/n1ZZ0eRac1B4UwPhAtILGQzgIUgOp0cQ3Cb94ugq92\n"+
"wxkxeEdWmIFIlXgWOf6I8yWp9DZaigrRhA2kPbY01zKxCsX1ZxKMVu2sU/HM1hJy\n"+
"aebLLND002yLzuRDLXbacmCt5U6vDQDjBmm3uZ26fEMF+GTCnn6fJBq5RDfRKjpS\n"+
"fVM0mCePO9RHiwu3oHfqoyLA2QGlCexXcIYq7KbJjC9vcamAWRqQdHlsSj5ezDTR\n"+
"GofA6HtQ+zNdGHOvqsYtbN8MJSlUXXy39wIDAQAB\n"+
"-----END RSA PUBLIC KEY-----";

If I try to parse it into a PublicKey using KeyFactory like that:

KeyFactory kf = KeyFactory.getInstance("RSA");
Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
String encoded = parse.matcher(pemPubKey).replaceFirst("$1");
byte[] pem = Base64.getMimeDecoder().decode(encoded);
PublicKey pubKey = kf.generatePublic(new X509EncodedKeySpec(pem));

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

But when I use bouncycastle like that:

SubjectPublicKeyInfo subjectPublicKeyInfo =
    (SubjectPublicKeyInfo) new PEMParser(new StringReader(pemPubKey)).readObject();

PublicKey pubKey;

if (PKCSObjectIdentifiers.rsaEncryption == subjectPublicKeyInfo.getAlgorithm().getAlgorithm()) {
    DLSequence der = (DLSequence) subjectPublicKeyInfo.parsePublicKey().toASN1Primitive();
    ASN1Object modulus, exponent;
    modulus = (ASN1Object) der.getObjectAt(0);
    exponent = (ASN1Object) der.getObjectAt(1);
    RSAPublicKeySpec spec =
            new RSAPublicKeySpec(new BigInteger(modulus.toString()), new BigInteger(exponent.toString()));
    KeyFactory factory = KeyFactory.getInstance("RSA");
    pubKey = factory.generatePublic(spec);
} else {
    // Throw some exception
}

I get a valid PublicKey and the algorithm is identified correctly.

Why does java's parser fails here? And am I doing the migration from SubjectPublicKeyInfo to PublicKey correctly?

Update:

I've tried to verify the key using openssl:

$ openssl rsa -inform PEM -pubin -in pub.pem -text -noout unable to load Public Key 140735659656136:error:0906D06C:PEM routines:PEM_read_bio:no start line:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pem/pem_lib.c:704:Expecting: PUBLIC KEY

And after removing RSA from the header / footer:

$ openssl rsa -inform PEM -pubin -in pub.pem -text -noout unable to load Public Key 140735659656136:error:0D0680A8:asn1 encoding routines:ASN1_CHECK_TLEN:wrong tag:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/asn1/tasn_dec.c:1164: 140735659656136:error:0D07803A:asn1 encoding routines:ASN1_ITEM_EX_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/asn1/tasn_dec.c:314:Type=X509_ALGOR 140735659656136:error:0D08303A:asn1 encoding routines:ASN1_TEMPLATE_NOEXP_D2I:nested asn1 error:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/asn1/tasn_dec.c:653:Field=algor, Type=X509_PUBKEY 140735659656136:error:0906700D:PEM routines:PEM_ASN1_read_bio:ASN1 lib:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.50.2/libressl/crypto/pem/pem_oth.c:84:

Upvotes: 0

Views: 369

Answers (1)

President James K. Polk
President James K. Polk

Reputation: 41958

Java's parser didn't fail, your public key is not an instance of an encoded SubjectPublicKeyInfo structure that Java's X509EncodedKeySpec is expecting. I haven't gone through the Bouncycastle routines to see why it succeeded, but PEMParser is designed to parse many different kinds of so-called "PEM" files.

A SubjectPublicKeyInfo is defined in RFC 5280 as:

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

Your public key is a simple PKCS#1 RSAPublicKey, defined in RFC 8017 as:

RSAPublicKey ::= SEQUENCE {
             modulus           INTEGER,  -- n
             publicExponent    INTEGER   -- e
         }

And just a final word about "PEM" files. You need to be careful when describing something as "PEM" format because PEM is not really a specific format. "PEM" is little more than encoding a true format in base64, then wrapping the base64 in "-----BEGIN -----" and "-----END -----" lines, where hopefully uniquely describes what the base64-encoded data is.

Upvotes: 1

Related Questions