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.
-
Also a similar function in _okhttp-tls_, written in Kotlin : https://github.com/square/okhttp/blob/parent-4.10.0/okhttp-tls/src/main/kotlin/okhttp3/tls/Certificates.kt#L67 – Guillaume Husta Sep 22 '22 at 11:35
12 Answers
This is not bad. Java doesn't provide any functions to write PEM files. What you are doing is the correct way. Even KeyTool does the same thing,
BASE64Encoder encoder = new BASE64Encoder();
out.println(X509Factory.BEGIN_CERT);
encoder.encodeBuffer(cert.getEncoded(), out);
out.println(X509Factory.END_CERT);
If you use BouncyCastle, you can use PEMWriter class to write out X509 certificate in PEM.

- 1,065
- 2
- 14
- 34

- 74,484
- 29
- 137
- 169
-
13PEMWriter is now deprecated. JcaPEMWriter is the newer class that replaces it. – rancidfishbreath Jul 21 '16 at 05:30
-
2
-
See [answer below](https://stackoverflow.com/a/53816183/1121497) about using BouncyCastle `PemWriter`. – Ferran Maylinch Mar 25 '22 at 09:46
Haven't seen anyone bring up Java 8's Base64.getMimeEncoder
method yet - actually allows you to specify both the line length and line separator like so:
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
I looked to see if there was any difference with this ^ vs the standard encoder, and I couldn't find anything. The javadoc cites RFC 2045 for both BASIC and MIME encoders, with the addition of RFC 4648 for BASIC. AFAIK both of these standards use the same Base64 alphabet (tables look the same), so you should fine to use MIME if you need to specify a line length.
This means that with Java 8, this can be accomplished with:
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.util.Base64;
...
public static final String BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
public static final String END_CERT = "-----END CERTIFICATE-----";
public final static String LINE_SEPARATOR = System.getProperty("line.separator");
...
public static String formatCrtFileContents(final Certificate certificate) throws CertificateEncodingException {
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
final byte[] rawCrtText = certificate.getEncoded();
final String encodedCertText = new String(encoder.encode(rawCrtText));
final String prettified_cert = BEGIN_CERT + LINE_SEPARATOR + encodedCertText + LINE_SEPARATOR + END_CERT;
return prettified_cert;
}
Previous answer gives compatibility problems with 3de party software (like PHP), because PEM cert is not correctly chunked.
Imports:
import org.apache.commons.codec.binary.Base64;
Code:
protected static String convertToPem(X509Certificate cert) throws CertificateEncodingException {
Base64 encoder = new Base64(64);
String cert_begin = "-----BEGIN CERTIFICATE-----\n";
String end_cert = "-----END CERTIFICATE-----";
byte[] derCert = cert.getEncoded();
String pemCertPre = new String(encoder.encode(derCert));
String pemCert = cert_begin + pemCertPre + end_cert;
return pemCert;
}
The following uses no big external libraries or possibly version-inconsistent sun.* libraries. It builds on judoman's answer, but it also chunks lines at 64 characters, as required by OpenSSL, Java, and others.
Import:
import javax.xml.bind.DatatypeConverter;
import java.security.cert.X509Certificate;
import java.io.StringWriter;
Code:
public static String certToString(X509Certificate cert) {
StringWriter sw = new StringWriter();
try {
sw.write("-----BEGIN CERTIFICATE-----\n");
sw.write(DatatypeConverter.printBase64Binary(cert.getEncoded()).replaceAll("(.{64})", "$1\n"));
sw.write("\n-----END CERTIFICATE-----\n");
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
return sw.toString();
}
(I would have just commented on judoman's answer, but I don't have enough reputation points to be allowed to comment, and my simple edit was rejected because it should have been a comment or an answer, so here's the answer.)
If you want to write straight to file, also import java.io.FileWriter
and:
FileWriter fw = new FileWriter(certFilePath);
fw.write(certToString(myCert));
fw.close();

- 1,149
- 13
- 13
-
Unfortunately javax.xml.bind has been removed in Java 11. The best option now seems to be to use`Base64.encodeBase64String` from `org.apache.commons.codec.binary.Base64` instead of `printBase64Binary`. – jtbr Jan 05 '21 at 16:06
If you have PEMWriter from bouncy castle, then you can do the following :
Imports :
import org.bouncycastle.openssl.PEMWriter;
Code :
/**
* Converts a {@link X509Certificate} instance into a Base-64 encoded string (PEM format).
*
* @param x509Cert A X509 Certificate instance
* @return PEM formatted String
* @throws CertificateEncodingException
*/
public String convertToBase64PEMString(Certificate x509Cert) throws IOException {
StringWriter sw = new StringWriter();
try (PEMWriter pw = new PEMWriter(sw)) {
pw.writeObject(x509Cert);
}
return sw.toString();
}

- 3,402
- 18
- 41

- 7,731
- 6
- 43
- 46
-
2It will return empty string now, to fix this add this after writing object "pw.flush()". – StalkAlex Jan 20 '17 at 10:58
-
I used the [answer](https://stackoverflow.com/a/53816183/1121497) with `PemWriter`, since `PEMWriter` is deprecated. – Ferran Maylinch Mar 25 '22 at 09:42
To build on ZZ Coder's idea, but without using the sun.misc
classes that aren't guaranteed to be consistent between JRE versions, consider this
Use Class:
import javax.xml.bind.DatatypeConverter;
Code:
try {
System.out.println("-----BEGIN CERTIFICATE-----");
System.out.println(DatatypeConverter.printBase64Binary(x509cert.getEncoded()));
System.out.println("-----END CERTIFICATE-----");
} catch (CertificateEncodingException e) {
e.printStackTrace();
}
-
None of the sun.* classes are intended to be stable: http://www.oracle.com/technetwork/java/faq-sun-packages-142232.html – pimlottc Dec 17 '12 at 22:17
-
Thanks pimlottc. I removed the sun.security.provider.X509Factory field references and replaced them with string values. – judoman Dec 19 '12 at 01:46
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.

- 11,766
- 2
- 42
- 61
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.

- 401
- 6
- 9
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());

- 11,983
- 5
- 41
- 36
-
Need to add the `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----` lines too. – AbdullahC Sep 23 '19 at 13:58
-
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();
}
}

- 344
- 4
- 11
-
2
-
1@RobAu I have taken that line from [this](https://github.com/SaitoLab-Nitech/ApkSign) – HemanthJabalpuri Oct 08 '20 at 13:09
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);

- 21
- 1
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();
}

- 24,677
- 9
- 99
- 108