在java中将x509证书写入PEM格式的字符串?

发布于 09-11 04:46 字数 150 浏览 12 评论 0原文

是否有一些高级方法可以将 X509Certificate 写入 PEM 格式的字符串? 目前我正在执行 x509cert.encode() 将其写入 DER 格式的字符串,然后对其进行 Base 64 编码并附加页眉和页脚以创建 PEM 字符串,但这似乎很糟糕。特别是因为我也必须换行。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

夏九2024-09-18 04:46:47

这还不错。 Java不提供任何写入PEM文件的函数。你正在做的事情是正确的方法。即使 KeyTool 也做同样的事情,

BASE64Encoder encoder = new BASE64Encoder();
out.println(X509Factory.BEGIN_CERT);
encoder.encodeBuffer(cert.getEncoded(), out);
out.println(X509Factory.END_CERT);

如果你使用 BouncyCastle,你可以使用 PEMWriter 类在 PEM 中写出 X509 证书。

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.

温柔一刀2024-09-18 04:46:47

还没有看到有人提出 Java 8 的 Base64.getMimeEncoder 方法 - 实际上允许您指定行长度 行分隔符,如下所示:

final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());

我看看是否有这个 ^ 与标准编码器有什么区别,但我找不到任何东西。 javadoc 引用 RFC 2045 适用于 BASIC 和 MIME 编码器,并添加了 RFC 4648 适用于 BASIC。 AFAIK 这两个标准都使用相同的 Base64 字母表(表格看起来相同),因此如果您需要指定行长度,则应该使用 MIME。

这意味着使用 Java 8,可以通过以下方式完成

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

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;
}
也只是曾经2024-09-18 04:46:47

之前的答案给出了与 3de party 软件(如 PHP)的兼容性问题,因为 PEM 证书未正确分块。

进口:

import org.apache.commons.codec.binary.Base64;

代码:

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

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;
}
夜深人未静2024-09-18 04:46:47

以下内容没有使用大型外部库或可能版本不一致的 sun.* 库。它建立在 judoman 的答案的基础上,但它也根据 OpenSSL、Java 和其他语言的要求,将行分成 64 个字符。

导入:

import javax.xml.bind.DatatypeConverter;
import java.security.cert.X509Certificate;
import java.io.StringWriter;

代码:(

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

我刚刚对柔道曼的答案发表了评论,但我没有足够的声誉点来允许发表评论,并且我的简单编辑被拒绝,因为它应该是评论或答案,所以这里是答案.)

如果您想直接写入文件,还需要导入 java.io.FileWriter 并:

FileWriter fw = new FileWriter(certFilePath);
fw.write(certToString(myCert));
fw.close();

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();
云柯2024-09-18 04:46:47

如果您有来自充气城堡的 PEMWriter,那么您可以执行以下操作:

导入:

import org.bouncycastle.openssl.PEMWriter;

代码:

/**
 * 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();
}

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();
}
与君绝2024-09-18 04:46:47

要基于 ZZ Coder 的想法构建,但不使用不能保证 JRE 版本之间一致的 sun.misc 类,请考虑以下

使用类:

import javax.xml.bind.DatatypeConverter;

代码:

try {
    System.out.println("-----BEGIN CERTIFICATE-----");
    System.out.println(DatatypeConverter.printBase64Binary(x509cert.getEncoded()));
    System.out.println("-----END CERTIFICATE-----");
} catch (CertificateEncodingException e) {
    e.printStackTrace();
}

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();
}
彩扇题诗2024-09-18 04:46:47

在 BouncyCastle 1.60 中,PEMWriter 已被弃用,取而代之的是 PemWriter

StringWriter sw = new StringWriter();

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

return sw.toString();

PemWriter 是缓冲的,因此您需要在访问构建它的编写器之前刷新/关闭它。

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.

ㄟ。诗瑗2024-09-18 04:46:47

几乎和@Andy Brown一样,少了一行代码。

StringWriter sw = new StringWriter();

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

String pem = sw.toString();

正如他所说,PEMWriter 已被弃用。

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.

天生の放荡2024-09-18 04:46:47

使用 进行编码的另一种选择Guava 的 BaseEncoding

import com.google.common.io.BaseEncoding;

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

然后:

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

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());
药祭#氼2024-09-18 04:46:47

使用一些微小的 Base64,我制作了下面的东西,它不依赖于其他东西,如 Base64 或 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();
    }
}

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();
    }
}
梦境2024-09-18 04:46:47

Java 11 更新:

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

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);
梦年海沫深2024-09-18 04:46:47

对于 BouncyCastle 来说,这是一种实现方法:

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

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();
}
蓝海似她心2024-09-18 04:46:47

使用 okhttp3-tls 可以在一行中

import okhttp3.tls.certificatePem

String certPem = cert.certificatePem()

With okhttp3-tls it is possible in one line

import okhttp3.tls.certificatePem

String certPem = cert.certificatePem()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文