Write x509 certificate into PEM formatted string in java?

Viewed 89731

Is there some high level way to write an X509Certificate into a PEM formatted string? Currently I'm doing x509cert.encode() to write it into a DER formatted string, then base 64 encoding it and appending the header and footer to create a PEM string, but it seems bad. Especially since I have to throw in line breaks too.

12 Answers

Almost the same as @Andy Brown one less line of code.

StringWriter sw = new StringWriter();

try (JcaPEMWriter jpw = new JcaPEMWriter(sw)) {
    jpw.writeObject(cert);
}

String pem = sw.toString();

As he said PEMWriter is deprecated.

In BouncyCastle 1.60 PEMWriter has been deprecated in favour of PemWriter.

StringWriter sw = new StringWriter();

try (PemWriter pw = new PemWriter(sw)) {
  PemObjectGenerator gen = new JcaMiscPEMGenerator(cert);
  pw.writeObject(gen);
}

return sw.toString();

PemWriter is buffered so you do need to flush/close it before accessing the writer that it was constructed with.

Yet another alternative for encoding using Guava's BaseEncoding:

import com.google.common.io.BaseEncoding;

public static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static final int LINE_LENGTH = 64;

And then:

String encodedCertText = BaseEncoding.base64()
                                     .withSeparator(LINE_SEPARATOR, LINE_LENGTH)
                                     .encode(cert.getEncoded());

Using some tiny Base64 I made below thing which does not depend on other things like base64 or bouncycastle.

import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
class Convert {
    private static byte[] convertToPem(X509Certificate cert)
        throws CertificateEncodingException {
        String cert_begin = "-----BEGIN CERTIFICATE-----\n";
        String end_cert = "-----END CERTIFICATE-----\n";
        String b64 = encode(cert.getEncoded()).replaceAll("(.{64})", "$1\n");
        if (b64.charAt(b64.length() - 1) != '\n') end_cert = "\n" + end_cert;
        String outpem = cert_begin + b64 + end_cert;
        return outpem.getBytes();
    }

    // Taken from https://gist.github.com/EmilHernvall/953733
    private static String encode(byte[] data) {
        String tbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
        StringBuilder buffer = new StringBuilder();
        int pad = 0;
        for (int i = 0; i < data.length; i += 3) {
            int b = ((data[i] & 0xFF) << 16) & 0xFFFFFF;
            if (i + 1 < data.length) b |= (data[i+1] & 0xFF) << 8;
            else pad++;
            if (i + 2 < data.length) b |= (data[i+2] & 0xFF);
            else pad++;
            for (int j = 0; j < 4 - pad; j++, b <<= 6) {
                int c = (b & 0xFC0000) >> 18;
                buffer.append(tbl.charAt(c));
            }
        }
        for (int j = 0; j < pad; j++) buffer.append("=");
        return buffer.toString();
    }
}

Java 11 update:

System.out.println(X509Factory.BEGIN_CERT);
System.out.println(java.util.Base64.getMimeEncoder(64, 
    new byte[] {'\r', '\n'}).encodeToString(cert.getEncoded()));
System.out.println(X509Factory.END_CERT);

With BouncyCastle this is one way to do it:

// You may need to add BouncyCastle as provider:
public static init() {
    Security.addProvider(new BouncyCastleProvider());
}

public static String encodePEM(Certificate certificate) {
    return encodePEM("CERTIFICATE", certificate.encoded);
}
public static String encodePEM(PrivateKey privateKey) {
    return encodePEM("PRIVATE KEY", privateKey.encoded);
}
public static String encodePEM(PublicKey publicKey) {
    return encodePEM("PUBLIC KEY", publicKey.encoded);
}    
/**
 * Converts byte array to PEM
 */
protected static String toPEM(String type, byte[] data) {
  final PemObject pemObject = new PemObject(type, data);
  final StringWriter sw = new StringWriter();
  try (final PemWriter pw = new PemWriter(sw)) {
    pw.writeObject(pemObject);
  }
  return sw.toString();
}
Related