web-dev-qa-db-ja.com

JavaでPEM形式の文字列にx509証明書を書き込みますか?

X509CertificateをPEM形式の文字列に書き込む高度な方法はありますか?現在、x509cert.encode()を実行してDER形式の文字列に書き込み、それをBase 64でエンコードし、ヘッダーとフッターを追加してPEM文字列を作成していますが、それは悪いようです。特に改行も入れなければならないので。

44
pizzathehut

これは悪くありません。 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証明書を書き出すことができます。

56
ZZ Coder

前の回答では、PEM証明書が正しくチャンクされていないため、3deパーティソフトウェア(PHPなど)との互換性の問題が発生します。

輸入:

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

Java 8's Base64.getMimeEncoderメソッドはまだ-実際には、行の長さ行セパレーターの両方を次のように指定できます。

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

この^対標準エンコーダとの間に違いがあるかどうかを確認しましたが、何も見つかりませんでした。 javadoc 引用 RFC 2045 BASICおよびMIMEエンコーダーの両方に、 RFC 4648 をBASICに追加これらの標準はどちらも同じ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;
}
16
josh-cain

以下は、大きな外部ライブラリを使用していないか、バージョンに一貫性のないSun. *ライブラリを使用しています。柔道家の答えに基づいて構築されていますが、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();
}

(私は柔道の答えにコメントしたばかりでしたが、コメントを許可するのに十分な評判ポイントがなく、私の単純な編集はコメントまたは回答であるはずだったため拒否されましたので、ここに答えがあります。)

ファイルに直接書き込みたい場合は、import Java.io.FileWriterおよび:

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

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

弾力がある城の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();
}

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はバッファリングされているため、作成されたライターにアクセスする前にフラッシュ/クローズする必要があります。

0
Andy Brown

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;

その後:

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