15

Have a String being sent from in the below format:

-----BEGIN RSA PUBLIC KEY-----
MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY
mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma
XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED
-----END RSA PUBLIC KEY-----

How do i construct a PublicKey Object from this string ? Have tried the below Remove the header and footer and base64 decode the buffer

public static PublicKey getFromString(String keystr) throws Exception
  {
  //String S1= asciiToHex(keystr);
   byte[] keyBytes = new sun.misc.BASE64Decoder().decodeBuffer(keystr);
   X509EncodedKeySpec spec =
       new X509EncodedKeySpec(keyBytes);
     KeyFactory kf = KeyFactory.getInstance("RSA");
     return kf.generatePublic(spec);

  }

This fails either as an invalid key format or will get below error

java.security.spec.InvalidKeySpecException: java.security.InvalidKeyException: IOException: algid parse error, not a sequence
 at sun.security.rsa.RSAKeyFactory.engineGeneratePublic(RSAKeyFactory.java:188)
 at java.security.KeyFactory.generatePublic(KeyFactory.java:304)
 at PublicKeyReader.getFromString(PublicKeyReader.java:30)
 at Tst.main(Tst.java:36)

The Key is being generated thro the API of openSSL PEM_write_bio_RSAPublicKey(bio, rsa);

Yuval Adam
  • 161,610
  • 92
  • 305
  • 395
MSSV
  • 171
  • 1
  • 1
  • 7
  • Have used the link .But will not do the converstion to der format – MSSV Oct 27 '10 at 12:20
  • Note that what you're trying to do isn't really a "conversion to DER". Converting to DER is just decoding what's base64 here and output it as a sequence of bytes. You're trying to decode the ASN.1 structure. – Bruno Nov 03 '10 at 16:34

3 Answers3

9

by calling PEM_write_bio_RSAPublicKey only the key modulus and public exponent are encoded into the output PEM data. However the X509EncodedKeySpec is expected this ASN.1 key format:

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

You should use the PEM_write_bio_PUBKEY function which encodes the public key using the SubjectPublicKeyInfo structure which as expected by X509EncodedKeySpec

An other possible solution to decode the key. Unfortunately I don't think it is possible to do only with the standard JDK API but it can be done with the Bouncycastle library

import org.bouncycastle.asn1.*;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;

public static PublicKey getFromString(String keystr) throws Exception
{
  //String S1= asciiToHex(keystr);
   byte[] keyBytes = new sun.misc.BASE64Decoder().decodeBuffer(keystr);
   ASN1InputStream in = new ASN1InputStream(keyBytes);
   DERObject obj = in.readObject();
   RSAPublicKeyStructure pStruct = RSAPublicKeyStructure.getInstance(obj);
   RSAPublicKeySpec spec = new RSAPublicKeySpec(pStrcut.getModulus(), pStruct.getPublicExponent());
   KeyFactory kf = KeyFactory.getInstance("RSA");
   return kf.generatePublic(spec);
}
Jcs
  • 13,279
  • 5
  • 53
  • 70
  • how or where should the SubjectPublicKeyInfo structure be specified? – MSSV Oct 27 '10 at 18:59
  • The SubjectPublicKeyInfo structure is specified in the RFC 5280 (http://tools.ietf.org/html/rfc5280) and the public key structures are specified in the RFC 3279 (http://tools.ietf.org/html/rfc3279) – Jcs Oct 27 '10 at 21:26
  • The Client key is received so will not be able to change the format.Can you suggest any other way of loading this key and getting the details thro JCE example . – MSSV Oct 29 '10 at 04:25
  • Can you please suggest if it is possible to use the publickey generated using keypair.getPublic.getEncoded() with the openSSL api call BIO_write and PEM_read_bio_RSAPublicKey – MSSV Nov 03 '10 at 01:08
  • I don't think so. getEncoded() on PublicKey returns an encoded SubjectPublicKeyInfo and PEM_read_bio_RSAPublicKey is expecting a PKCS#1 RSAPublicKey structure (i.e. only the modulus and public exponent: The same structure as the key you tried to convert). According to OpenSSL documentation PEM_read_bio_RSA_PUBKEY may work. – Jcs Nov 03 '10 at 08:54
  • Can you suggest the change that needs to be done in the Java key generation such that PEM_read_bio_RSAPublicKey works – MSSV Nov 03 '10 at 11:30
  • will not be able to change anything in the client side please suggest the change that will work with keyformat change in java have tried even with the bouncycastle – MSSV Nov 03 '10 at 11:32
  • Code doesn't compile. ASNASN1InputStream doesn't exist. – Alain O'Dea May 23 '17 at 14:02
6

BouncyCastle's PEMReader will do this for you:

String pemKey = "-----BEGIN RSA PUBLIC KEY-----\n"
            + "MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY\n"
            + "mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma\n"
            + "XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED\n"
            + "-----END RSA PUBLIC KEY-----\n";
PEMReader pemReader = new PEMReader(new StringReader(pemKey));
RSAPublicKey rsaPubKey = (RSAPublicKey) pemReader.readObject();
System.out.println("Public key: "+rsaPubKey);

(Note that you may need Security.addProvider(new BouncyCastleProvider()); somewhere before.)

Bruno
  • 119,590
  • 31
  • 270
  • 376
  • I am doing this in android. I am unable to import PEMReader. Have added compile 'org.bouncycastle:bcprov-jdk15on:1.56' in dependency. – madhuri H R Aug 09 '18 at 06:26
  • @madhuriHR: BouncyCastle refactored some of their architecture between 1.47 and 1.50 (2012-2013) and now this functionality is split between `PEMParser` and `JcaPEMKeyConverter` -- see https://stackoverflow.com/questions/41421154/unable-to-verify-string-signed-by-openssl-with-dsa-key or Alain ODea's answer (including my comment). – dave_thompson_085 Dec 29 '18 at 11:13
1

UPDATE: greatly simplified process and code thanks to @dave_thompson_085

You can construct a PublicKey Object from the string you provided as follows:

  1. Reading the Subject Public Key Information (SPKI) from binary DER (using Bouncy Castle's PEMParser)
  2. Feeding the SPKI into a converter to get the PublicKey (Bouncy's Castle's JcaPEMKeyConverter works)

Pre-reqs for my solution:

  1. Java 7+ (or you'll need to manually unroll the try-with-resources)
  2. Bouncy Castle bcprov-jdk15on 1.51 or later (does NOT run on 1.50 or earlier, does not compile on 1.47 or earlier)

Full working Java 7+ example:

import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;

import java.io.IOException;
import java.io.StringReader;
import java.security.PublicKey;

public interface PemToDer
{
    static void main(String[] args) throws IOException {
        createRsaPublicKey(
                "-----BEGIN RSA PUBLIC KEY-----\n" +
                "MIGHAoGBANAahj75ZIz9nXqW2H83nGcUao4wNyYZ9Z1kiNTUYQl7ob/RBmDzs5rY\n" +
                "mUahXAg0qyS7+a55eU/csShf5ATGzAXv+DDPcz8HrSTcHMEFpuyYooX6PrIZ07Ma\n" +
                "XtsJ2J4mhlySI5uOZVRDoaFY53MPQx5gud2quDz759IN/0gnDEEVAgED\n" +
                "-----END RSA PUBLIC KEY-----"
        );
    }

    static PublicKey createRsaPublicKey(String keystr) throws IOException {
        try (StringReader reader = new StringReader(keystr);
             PEMParser pemParser = new PEMParser(reader)) {
            SubjectPublicKeyInfo subjectPublicKeyInfo = (SubjectPublicKeyInfo) pemParser.readObject();
            JcaPEMKeyConverter pemKeyConverter = new JcaPEMKeyConverter();
            return pemKeyConverter.getPublicKey(subjectPublicKeyInfo);
        }
    }
}
Alain O'Dea
  • 21,033
  • 1
  • 58
  • 84
  • 1
    If you use subclass `PEMParser` instead of `PemReader` it returns a `SubjectPublicKeyInfo` structure whose encoding you can pass as an `X509EncodedKeySpec` to the key factory -- or you can use `JcaPEMKeyConverter` to do the latter for you. – dave_thompson_085 Dec 29 '18 at 11:12
  • Wow. This is much cleaner. Thank you. Editing shortly. – Alain O'Dea Jan 07 '19 at 01:52
  • return type of this method pemKeyConverter.getPublicKey(subjectPublicKeyInfo) is DER? – Shadow May 09 '23 at 05:07
  • @Shadow the return type of pemKeyConverter.getPublicKey(subjectPublicKeyInfo) is **PublicKey** so the return type of createRsaPublicKey is also **PublicKey**. Otherwise there would be a type error. See Javadoc here https://www.bouncycastle.org/docs/pkixdocs1.8on/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.html#getKeyPair-org.bouncycastle.openssl.PEMKeyPair-. See source here https://github.com/bcgit/bc-java/blob/9032821b326fba9fec7528899dfffa79f9f19aef/pkix/src/main/java/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.java#L71 – Alain O'Dea May 10 '23 at 10:19
  • Let me know how to convert to DER format? – Shadow May 11 '23 at 09:44