Zapisanie certyfikatu x509 do sformatowanego łańcucha PEM w Javie?

Czy Jest jakiś sposób na zapisanie X509certyfikatu w łańcuchu sformatowanym w PEM? Obecnie robię x509cert.encode (), aby zapisać go do sformatowanego łańcucha DER, a następnie zakodować go base 64 i dodać nagłówek i stopkę, aby utworzyć łańcuch PEM, ale wydaje się to złe. Zwłaszcza, że ja też muszę dorzucić przerwy w kolejce.

Author: Hendrik Brummermann, 2010-07-23

7 answers

Nie jest źle. Java nie udostępnia żadnych funkcji do zapisu plików PEM. To, co robisz, jest właściwym sposobem. Nawet KeyTool robi to samo,

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

Jeśli używasz BouncyCastle, możesz użyć klasy PEMWriter do wypisania certyfikatu X509 w PEM.

 52
Author: ZZ Coder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-11 13:20:03

Poprzednia odpowiedź daje problemy ze zgodnością z 3DE party software (np. PHP), ponieważ pem cert nie jest poprawnie chunted.

IMPORT:

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

Kod:

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;
}
 17
Author: kthomeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-26 00:10:34

Nie widziałem jeszcze nikogo, kto przywołuje metodę Java 8 Base64.getMimeEncoder - w rzeczywistości pozwala określić zarówno długość linii , jak i separator linii w następujący sposób:

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

Spojrzałem, czy jest jakaś różnica z tym ^ vs standardowego enkodera, i nie mogłem znaleźć nic. Javadoc cytuje RFC 2045 zarówno dla koderów BASIC, jak i MIME, z dodatkiem RFC 4648 dla BASIC. AFAIK oba te standardy używają tego samego alfabetu Base64 (tabele wyglądają to samo), więc powinieneś używać MIME, jeśli chcesz określić długość linii.

Oznacza to, że w Javie 8 można to osiągnąć za pomocą:

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;
}
 11
Author: JoshC13,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-12 15:55:40

Poniższe nie używa dużych bibliotek zewnętrznych lub ewentualnie niespójnych wersji sun.* biblioteki. Opiera się on na odpowiedzi judomana, ale również dzieli linie na 64 znaki, zgodnie z wymaganiami OpenSSL, Javy i innych.

Import:

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

Kod:

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

(ja bym po prostu skomentował odpowiedź judomana, ale nie mam wystarczającej ilości punktów reputacji, aby móc komentować, a moja prosta edycja została odrzucona, ponieważ powinna być to komentarz lub odpowiedź, więc oto odpowiedz.)

Jeśli chcesz zapisać bezpośrednio do pliku, również import java.io.FileWriter i:

FileWriter fw = new FileWriter(certFilePath);
fw.write(certToString(myCert));
fw.close();
 10
Author: jtbr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-16 10:58:14

Jeśli masz Pemwritera z bouncy castle, możesz wykonać następujące czynności:

IMPORT:

import org.bouncycastle.openssl.PEMWriter;

Kod:

/**
 * 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
Author: Balaji Boggaram Ramanarayan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-17 17:24:05

Aby bazować na pomyśle ZZ Coder, ale bez użycia klas sun.misc, które nie mają gwarancji spójności pomiędzy wersjami JRE, rozważ to

Klasa Użycia:

import javax.xml.bind.DatatypeConverter;

Kod:

try {
    System.out.println("-----BEGIN CERTIFICATE-----");
    System.out.println(DatatypeConverter.printBase64Binary(x509cert.getEncoded()));
    System.out.println("-----END CERTIFICATE-----");
} catch (CertificateEncodingException e) {
    e.printStackTrace();
}
 8
Author: judoman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-11 13:20:32

Yet another alternative for encoding using ' 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;

A następnie:

String encodedCertText = BaseEncoding.base64()
                                     .withSeparator(LINE_SEPARATOR, LINE_LENGTH)
                                     .encode(cert.getEncoded());
 0
Author: Ahmad Abdelghany,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-05 12:45:22