Записать сертификат x509 в строку формата PEM в Java? - PullRequest
43 голосов
/ 23 июля 2010

Есть ли какой-нибудь высокоуровневый способ записи сертификата X509 в строку в формате PEM?В настоящее время я делаю x509cert.encode (), чтобы записать его в строку в формате DER, затем в кодировке base 64 и добавив верхний и нижний колонтитулы для создания строки PEM, но это кажется плохим.Тем более, что мне приходится бросать и разрывы строк.

Ответы [ 8 ]

56 голосов
/ 23 июля 2010

Это не плохо.Java не предоставляет никаких функций для записи файлов PEM.То, что вы делаете, это правильный путь.Даже KeyTool делает то же самое,

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

Если вы используете BouncyCastle, вы можете использовать класс PEMWriter для записи сертификата X509 в PEM.

18 голосов
/ 21 апреля 2013

Предыдущий ответ дает проблемы совместимости с 3-сторонним программным обеспечением (таким как 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;
}
15 голосов
/ 24 ноября 2016

Еще не видел, чтобы кто-нибудь вызывал метод Base64.getMimeEncoder в Java 8 - на самом деле он позволяет вам указывать длину строки и разделитель строк, например:

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;
}
10 голосов
/ 15 апреля 2015

Следующее не использует большие внешние библиотеки или, возможно, несовместимые с версиями библиотеки Sun. *. Он основан на ответе дзюдоиста, но также разбивает строки на 64 символа, как того требуют OpenSSL, Java и другие.

Импорт:

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();
8 голосов
/ 13 января 2015

Если у вас есть 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();
}
8 голосов
/ 01 декабря 2012

Чтобы построить идею ZZ-кодера, но без использования классов 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();
}
0 голосов
/ 17 декабря 2018

В 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 голосов
/ 18 января 2018

Еще одна альтернатива для кодирования с использованием Базовая кодировка Guava :

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