How do we convert a String from PEM to DER format
Asked Answered
G

3

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);

Gestalt answered 27/10, 2010 at 12:10 Comment(2)
Have used the link .But will not do the converstion to der formatGestalt
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.Rathskeller
D
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);
}
Darrickdarrill answered 27/10, 2010 at 12:55 Comment(8)
how or where should the SubjectPublicKeyInfo structure be specified?Gestalt
The SubjectPublicKeyInfo structure is specified in the RFC 5280 (tools.ietf.org/html/rfc5280) and the public key structures are specified in the RFC 3279 (tools.ietf.org/html/rfc3279)Darrickdarrill
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 .Gestalt
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_RSAPublicKeyGestalt
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.Darrickdarrill
Can you suggest the change that needs to be done in the Java key generation such that PEM_read_bio_RSAPublicKey worksGestalt
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 bouncycastleGestalt
Code doesn't compile. ASNASN1InputStream doesn't exist.Cosmopolite
R
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.)

Rathskeller answered 3/11, 2010 at 16:18 Comment(2)
I am doing this in android. I am unable to import PEMReader. Have added compile 'org.bouncycastle:bcprov-jdk15on:1.56' in dependency.Waldon
@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 #41421654 or Alain ODea's answer (including my comment).Jacinda
U
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);
        }
    }
}
Unwinking answered 19/9, 2017 at 15:51 Comment(7)
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.Jacinda
Wow. This is much cleaner. Thank you. Editing shortly.Cosmopolite
return type of this method pemKeyConverter.getPublicKey(subjectPublicKeyInfo) is DER?Labored
@Labored 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 bouncycastle.org/docs/pkixdocs1.8on/org/bouncycastle/openssl/…. See source here github.com/bcgit/bc-java/blob/…Cosmopolite
Let me know how to convert to DER format?Labored
@Labored while DER is in the question title, it's not what the OP was asking for in the question body. I was answering the detailed question not the title. I do not know how to convert to DER.Cosmopolite
github.com/bcgit/bc-java/blob/main/core/src/main/java/org/… is a starting point. If you can get the ASN1Components from the PEMParser you can likely iterate them and write to a DER file. BouncyCastle takes a bit to put together. It's more of a hardware store where you get fundamental parts than a consumer store where you get a working solution.Cosmopolite

© 2022 - 2024 — McMap. All rights reserved.