2010-07-22 17 views
34

¿Hay alguna forma de alto nivel para escribir un certificado X509 en una cadena con formato PEM? Actualmente estoy haciendo x509cert.encode() para escribirlo en una cadena con formato DER, luego base 64 codificándolo y agregando el encabezado y pie de página para crear una cadena PEM, pero parece malo. Especialmente porque tengo que lanzar pausas de línea también.¿Escribes el certificado x509 en una cadena con formato PEM en java?

Respuesta

47

Esto no está nada mal. Java no proporciona ninguna función para escribir archivos PEM. Lo que estás haciendo es la forma correcta. Incluso KeyTool hace lo mismo,

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

Si utiliza BouncyCastle, puede utilizar la clase PEMWriter para escribir certificado X509 en el PEM.

+4

PEMWriter ahora es obsoleto. JcaPEMWriter es la clase más nueva que lo reemplaza. – rancidfishbreath

7

Para construir sobre la idea de ZZ Coder, pero sin utilizar los sun.misc clases que no están garantizados para ser consistentes entre las versiones de JRE, considere esto

Uso Clase:

import javax.xml.bind.DatatypeConverter; 

Código:

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

Ninguna de las clases de sun. * Está pensada para ser estable: http://www.oracle.com/technetwork/java/faq-sun-packages-142232.html – pimlottc

+0

Gracias pimlottc. Eliminé las referencias de campo sun.security.provider.X509Factory y las reemplacé con valores de cadena. – judoman

15

La respuesta anterior ofrece problemas de compatibilidad con el software 3de party (como PHP), porque el certificado PEM no está fragmentado correctamente.

Importaciones:

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

Código:

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 = x509cert.getEncoded(); 
String pemCertPre = new String(encoder.encode(derCert)); 
String pemCert = cert_begin + pemCertPre + end_cert; 
return pemCert; 
} 
+1

Tiene que estar en líneas de 64 caracteres para OpenSSL en general. – Cathy

5

Si tiene PEMWriter del castillo hinchable, entonces usted puede hacer lo siguiente:

Importaciones:

import org.bouncycastle.openssl.PEMWriter; 

Código:

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

Devolverá cadena vacía ahora, para arreglar esto, agréguelo después de escribir el objeto "pw.flush()". – StalkAlex

10

Lo siguiente no usa grandes librerías externas o posiblemente bibliotecas incompatibles con la versión. *. Se basa en la respuesta de Judoman, pero también fragmenta líneas en 64 caracteres, tal como lo requieren OpenSSL, Java y otros.

importación:

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

Código:

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

(Me hubiera simplemente comentó sobre la respuesta de judoman, pero no tienen suficientes puntos de reputación que se permita a comentar, y mi sencilla La edición fue rechazada porque debería haber sido un comentario o una respuesta, así que aquí está la respuesta.)

Si desea escribir directamente a presentar, también import java.io.FileWriter y:

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

no he visto a nadie que aparezca Java 8 de Base64.getMimeEncoder método todavía - en realidad le permite especificar tanto la longitud de la línea y separador de línea, así:

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

miré para ver si había alguna diferencia con esta^vs el codificador estándar, y no pude encontrar nada. El javadoc cita RFC 2045 para codificadores BASIC y MIME, con la adición de RFC 4648 para BASIC. AFAIK ambos estándares usan el mismo alfabeto Base64 (las tablas tienen el mismo aspecto), por lo que debe usar MIME si necesita especificar una longitud de línea.

Esto significa que con Java 8, esto se puede lograr con:

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

La llamada requiere nivel de API 26. – Pei

0

Otra alternativa para codificar utilizando 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; 

Y luego:

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