53

How does one create a java.security.cert.X509Certificate instance from a PEM-formatted String? The PEM-formatted String is a HTTP request "SSL_CLIENT_CERT" header value.

ANSWER: Based on mgaert's answer, here's what I wrote in Scala:

val cert = factory.generateCertificate(
    new ByteArrayInputStream(
      Base64.decodeBase64(
        cert.stripPrefix("-----BEGIN CERTIFICATE-----").stripSuffix("-----END CERTIFICATE-----")
      )
    ).asInstanceOf[X509Certificate]
Jeffrey Chung
  • 19,319
  • 8
  • 34
  • 54
  • 6
    There is no need to decode it. The PEM base64 encoded format is directly supported, as in [this answer](http://stackoverflow.com/a/9739366/822870). Again: CertificateFactory cFactory = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) cFactory.generateCertificate(getInputStream(of_the_original_unmodified_certificate_file)); – David Balažic May 21 '15 at 15:08
  • 2
    it seems there is no need to strip the prefix/suffix as well – lznt Oct 18 '17 at 22:27
  • Doing Base64 decoding here can give you an Illegal Base64 character exception and so echoing the accepted answer and @DavidBalažic 's comment, there isn't a need to perform decoding here. – seriousgeek Jan 20 '21 at 16:09

5 Answers5

67

Decode the Base64 to binary, with some InputStream reading it, then try

CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate cert = cf.generateCertificate(is);
mgaert
  • 2,338
  • 21
  • 27
  • 2
    actually, there is no need to decode. @shuyu answer work like a charm – ursa Feb 11 '21 at 13:17
  • 2
    Oh, @ursa and @shuyu you are right - no decoding needed. My first thought was ... "and was that true in *March 2012* when I wrote that answer?" *Yes*! I dug up the JDK 7 source, and sure enough `generateCertificate` is *documented* to not only support binary, but Base64 as well. Plus, quote, "If the certificate is provided in Base64 encoding, it must be bounded at the beginning by -----BEGIN CERTIFICATE-----, and must be bounded at the end by -----END CERTIFICATE-----." Thanks for sending me on small time-travel trip here :-) – mgaert Mar 22 '21 at 14:04
26

I have a similar problem, I'm pasting also here the java code that worked for me in case anyone neaded it :

import java.util.Base64;

public static X509Certificate parseCertificate(String _headerName, HttpServletRequest _request) throws CertificateException {
    String certStr = _request.getHeader("x-clientcert");
    //before decoding we need to get rod off the prefix and suffix
    byte [] decoded = Base64.getDecoder().decode(certStr.replaceAll(X509Factory.BEGIN_CERT, "").replaceAll(X509Factory.END_CERT, ""));

    return (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(new ByteArrayInputStream(decoded));
}
Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78
simonC
  • 4,101
  • 10
  • 50
  • 78
  • You don't need to strip the header and footer for PEM encoded X509 certs, see [the javadoc](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/security/cert/CertificateFactory.html#generateCertificate(java.io.InputStream)). – Sotirios Delimanolis Apr 29 '20 at 14:01
14

Tried to follow @Balaji Boggaram Ramanarayan code but IDE keep on throwing Exception. Instead i convert the string to bytes and it works perfectly.

private X509Certificate convertStringToX509Cert(String certificate) throws Exception{
    InputStream targetStream = new ByteArrayInputStream(certificate.getBytes());
    return (X509Certificate) CertificateFactory
            .getInstance("X509")
            .generateCertificate(targetStream);
}

Not to mentions, this method doesn't need to remove .pem header and footer (-----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----)

shuyu
  • 444
  • 5
  • 7
11

The steps in conversion of PEM formatted String is opposite of how (x509 -> String) took place.

Sample PEM Formatted String :

-----BEGIN CERTIFICATE-----
MIIEczCCA1ugAwIBAgIBADANBgkqhkiG9w0BAQQFAD..AkGA1UEBhMCR0Ix
EzARBgNVBAgTClNvbWUtU3RhdGUxFDASBgNVBAoTC0..0EgTHRkMTcwNQYD
VQQLEy5DbGFzcyAxIFB1YmxpYyBQcmltYXJ5IENlcn..XRpb24gQXV0aG9y
aXR5MRQwEgYDVQQDEwtCZXN0IENBIEx0ZDAeFw0wMD..TUwMTZaFw0wMTAy
MDQxOTUwMTZaMIGHMQswCQYDVQQGEwJHQjETMBEGA1..29tZS1TdGF0ZTEU
MBIGA1UEChMLQmVzdCBDQSBMdGQxNzA1BgNVBAsTLk..DEgUHVibGljIFBy
aW1hcnkgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFD..AMTC0Jlc3QgQ0Eg
THRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCg..Tz2mr7SZiAMfQyu
vBjM9OiJjRazXBZ1BjP5CE/Wm/Rr500PRK+Lh9x5eJ../ANBE0sTK0ZsDGM
ak2m1g7oruI3dY3VHqIxFTz0Ta1d+NAjwnLe4nOb7/..k05ShhBrJGBKKxb
8n104o/5p8HAsZPdzbFMIyNjJzBM2o5y5A13wiLitE..fyYkQzaxCw0Awzl
kVHiIyCuaF4wj571pSzkv6sv+4IDMbT/XpCo8L6wTa..sh+etLD6FtTjYbb
rvZ8RQM1tlKdoMHg2qxraAV++HNBYmNWs0duEdjUbJ..XI9TtnS4o1Ckj7P
OfljiQIDAQABo4HnMIHkMB0GA1UdDgQWBBQ8urMCRL..5AkIp9NJHJw5TCB
tAYDVR0jBIGsMIGpgBQ8urMCRLYYMHUKU5AkIp9NJH..aSBijCBhzELMAkG
A1UEBhMCR0IxEzARBgNVBAgTClNvbWUtU3RhdGUxFD..AoTC0Jlc3QgQ0Eg
THRkMTcwNQYDVQQLEy5DbGFzcyAxIFB1YmxpYyBQcm..ENlcnRpZmljYXRp
b24gQXV0aG9yaXR5MRQwEgYDVQQDEwtCZXN0IENBIE..DAMBgNVHRMEBTAD
AQH/MA0GCSqGSIb3DQEBBAUAA4IBAQC1uYBcsSncwA..DCsQer772C2ucpX
xQUE/C0pWWm6gDkwd5D0DSMDJRqV/weoZ4wC6B73f5..bLhGYHaXJeSD6Kr
XcoOwLdSaGmJYslLKZB3ZIDEp0wYTGhgteb6JFiTtn..sf2xdrYfPCiIB7g
BMAV7Gzdc4VspS6ljrAhbiiawdBiQlQmsBeFz9JkF4..b3l8BoGN+qMa56Y
It8una2gY4l2O//on88r5IWJlm1L0oA8e4fR2yrBHX..adsGeFKkyNrwGi/
7vQMfXdGsRrXNGRGnX+vWDZ3/zWI0joDtCkNnqEpVn..HoX
-----END CERTIFICATE-----

Here are the steps :

1. Remove headers from PEM formatted String
Headers are : ---- BEGIN CERTIFICATE ----- and ----- END CERTIFICATE ------
2. Decode the rest of the part using Base64 to byte array
3. Then you can use CertificateFactory to convert byte stream to x509Certificate object

Sample Code to do above (with PEM Writer):

  /**
     * Converts a PEM formatted String to a {@link X509Certificate} instance.
     *
     * @param pem PEM formatted String
     * @return a X509Certificate instance
     * @throws CertificateException 
     * @throws IOException
     */
    public X509Certificate convertToX509Certificate(String pem) throws CertificateException, IOException {
        X509Certificate cert = null;
        StringReader reader = new StringReader(pem);
        PEMReader pr = new PEMReader(reader);
        cert = (X509Certificate)pr.readObject();
        return cert;
    }
  • 2
    Note that PEMReader is part of Bouncy Castle Crypto APIs, see https://www.bouncycastle.org/. The code by @mgaert works without an external library. – Michael Paesold Nov 16 '16 at 14:46
  • 1
    `PEMReader` was in Bouncy 'core' (bcprov) only until 1.46 (2011-02) and then bcpkix until 1.49 (2013-05); after that it's replaced by `PEMParser` (with `PemReader` note lowercase as a separable factor). – dave_thompson_085 Oct 19 '19 at 20:39
7

Another sample,

public static X509Certificate convertToX509Cert(String certificateString) throws CertificateException {
    X509Certificate certificate = null;
    CertificateFactory cf = null;
    try {
        if (certificateString != null && !certificateString.trim().isEmpty()) {
            certificateString = certificateString.replace("-----BEGIN CERTIFICATE-----\n", "")
                    .replace("-----END CERTIFICATE-----", ""); // NEED FOR PEM FORMAT CERT STRING
            byte[] certificateData = Base64.getDecoder().decode(certificateString);
            cf = CertificateFactory.getInstance("X509");
            certificate = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(certificateData));
        }
    } catch (CertificateException e) {
        throw new CertificateException(e);
    }
    return certificate;
}
Zeigeist
  • 3,755
  • 3
  • 20
  • 22
  • 6
    `Base64.getMimeDecoder()` can be a safer option, other wise you may see issues like `Illegal base64 character a` as discussed here https://bugs.openjdk.java.net/browse/JDK-8213780. – 34m0 Jun 03 '20 at 18:19
  • 1
    @34m0 that is exactly what happened to me. Thank you a lot for adding this comment! – Gozus19 Sep 01 '20 at 14:54