Decode ASN1 sequence to RSA public key using Java

2019-02-15 12:41发布

问题:

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?

回答1:

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.



回答2:

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.