是否有一些高级方法可以将 X509Certificate 写入 PEM 格式的字符串?目前我正在执行 x509cert.encode() 将其写入 DER 格式的字符串,然后对其进行 base 64 编码并附加页眉和页脚以创建 PEM 字符串,但这似乎很糟糕。特别是因为我也必须换行。
12 回答
这还不错。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 证书。
还没有看到有人提出 Java 8 的Base64.getMimeEncoder
方法 - 实际上允许您指定行长和行分隔符,如下所示:
final Base64.Encoder encoder = Base64.getMimeEncoder(64, LINE_SEPARATOR.getBytes());
我查看了这个 ^ 与标准编码器是否有任何区别,但我找不到任何东西。javadoc为 BASIC 和 MIME 编码器引用了RFC 2045,并为 BASIC 添加了RFC 4648。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;
}
先前的答案给出了与 3de 方软件(如 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;
}
以下不使用大型外部库或可能版本不一致的 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();
}
(我本来只是对 judoman 的回答发表评论,但我没有足够的声望点可以发表评论,我的简单编辑被拒绝了,因为它应该是评论或答案,所以这里是答案。)
如果你想直接写入文件,还有import java.io.FileWriter
:
FileWriter fw = new FileWriter(certFilePath);
fw.write(certToString(myCert));
fw.close();
如果您有来自充气城堡的 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();
}
为了建立 ZZ Coder 的想法,但不使用sun.misc
不能保证在 JRE 版本之间保持一致的类,请考虑这个
使用类:
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();
}
几乎和@Andy Brown一样少了一行代码。
StringWriter sw = new StringWriter();
try (JcaPEMWriter jpw = new JcaPEMWriter(sw)) {
jpw.writeObject(cert);
}
String pem = sw.toString();
正如他所说,PEMWriter 已被弃用。
使用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());
在 BouncyCastle 中,1.60PEMWriter
已被弃用,取而代之的是PemWriter
.
StringWriter sw = new StringWriter();
try (PemWriter pw = new PemWriter(sw)) {
PemObjectGenerator gen = new JcaMiscPEMGenerator(cert);
pw.writeObject(gen);
}
return sw.toString();
PemWriter
被缓冲,因此您确实需要在访问构造它的编写器之前刷新/关闭它。
使用一些微小的 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();
}
}
这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();
}
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);