Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decode ASN1 sequence to RSA public key using Java

I have the following RSA public key as ASN1 sequence:

SEQUENCE(2 elem)
  INTEGER (1024 bit) 14832…
  INTEGER 65537

How can I import this sequence as RSA public key in Java? The implemented KeySpecs (such as PKCS8EncodedKeySpec) do not work (obviously).

In addition I tried to use BouncyCastle and manually decode the sequence and initialize java.security.spec.RSAPublicKeySpec myself. However this approach seems rather clumsy.

Is there an easier way?

like image 437
Bob Avatar asked Feb 10 '23 11:02

Bob


2 Answers

You can use the Bouncycastle library as in the following code fragment:

import java.io.IOException;
import java.math.BigInteger;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DLSequence;

public class RsaAsn1Example {
// ...
    public static BigInteger [] parseASN1RsaPublicKey(byte [] encoded) throws IOException {
        ASN1InputStream asn1_is = new ASN1InputStream(encoded);
        DLSequence dlSeq = (DLSequence) asn1_is.readObject();
        ASN1Integer asn1_n = (ASN1Integer) dlSeq.getObjectAt(0);
        ASN1Integer asn1_e = (ASN1Integer) dlSeq.getObjectAt(1);
        asn1_is.close();
        return new BigInteger[]{ asn1_n.getPositiveValue(), asn1_e.getPositiveValue()};
    }
// ....
}

Or, as a less attractive alternative, you can roll you own ASN.1 decoder as in this example from one of my answers. But beware, ASN.1 decoding can be very tricky -- this very simple two element sequence is about the most complicated ASN.1 structure I would ever attempt to parse with my own code.

like image 80
President James K. Polk Avatar answered Feb 13 '23 00:02

President James K. Polk


ASN.1 parsing with BouncyCastle is not that clumsy.

String key =
"30818902818100cc61f9ef5ad0bc21de5b3ca69ee725d2c504edf99a6e97a0279d9" +
"59f2364ed21aaef8c704552d5d1a3deb2ee1a0be1558e3ca182b11a8c14392b6de5" +
"2346bc7c88bf75e3fb2b9f27fab21f5cf19934e3110ea4ad72a6f073f8ab38b9939" +
"bb639e78ad7f434119d8cc2b1bde4eaf9513b056508c908ed43c99b0ceb222cbbe1" +
"ef0203010001";
byte[] keyBytes = DatatypeConverter.parseHexBinary(key);

// These will throw exception in case of type mismatch
ASN1Sequence sequence = ASN1Sequence.getInstance(keyBytes);
ASN1Integer modulus = ASN1Integer.getInstance(sequence.getObjectAt(0));
ASN1Integer exponent = ASN1Integer.getInstance(sequence.getObjectAt(1));
RSAPublicKeySpec keySpec = new RSAPublicKeySpec(modulus.getPositiveValue(),
        exponent.getPositiveValue());
KeyFactory factory = KeyFactory.getInstance("RSA");
PublicKey publicKey = factory.generatePublic(keySpec);

System.out.println(publicKey.toString());

Another option is to build up your sequence up to X509 public key format and then use X509EncodedKeySpec. You will need to place encoded key as bit sequence together with algorithm identifier into ASN.1 sequence. However, this will result in approximately the same amount of code and is slower as there is more to parse. This answer has format nicely explained.

like image 23
divanov Avatar answered Feb 12 '23 23:02

divanov