Cómo dividir un archivo PEM

38

Nota: Esta no es realmente una pregunta porque ya encontré la respuesta, pero como no la encontré fácilmente aquí, la publicaré para que pueda beneficiar a otros.

Pregunta: ¿Cómo leer un archivo PEM concatenado como el utilizado por la directiva apache / mod_ssl SSLCACertificateFile ?

Respuesta (original) ( fuente ):

cat $file|awk 'split_after==1{n++;split_after=0} /-----END CERTIFICATE-----/ {split_after=1} {print > "cert" n ".pem"}'

Esto puede dejar un archivo vacío si hay una línea en blanco al final, como con openssl pkcs7 -outform PEM -in my-chain-file -print_certs. Para evitar eso, verifique la longitud de la línea antes de imprimir:

cat $file|awk 'split_after==1{n++;split_after=0}
   /-----END CERTIFICATE-----/ {split_after=1}
   {if(length($0) > 0) print > "cert" n ".pem"}' 

Respuesta 29/03/2016 :

Siguiendo la respuesta de @slugchewer , csplitpodría ser una opción más clara con:

csplit -f cert- $file '/-----BEGIN CERTIFICATE-----/' '{*}'
Cerber
fuente
Esta puede ser una pregunta tonta, pero ¿por qué necesitaría dividir mi archivo pem?
Ashwani Agarwal
66
@AshwaniAgarwal Desea dividir un archivo PEM cuando contiene varios certificados y desea examinar los certificados individualmente con herramientas tales como openssltomar un certificado para analizar.
Law29
Además, algunas herramientas o servidores desean un archivo combinado con cert y key, mientras que otros quieren que estén separados.
captncraig
Tuve que agregar '% ----- BEGIN CERTIFICATE -----%' a la línea de comando csplit para evitar un archivo vacío. Parece coincidir con lo que especifica la página de manual: csplit -f ./tmp/cert- $ file '% ----- BEGIN CERTIFICATE -----%' '/ ----- BEGIN CERTIFICATE ----- / '' {*} '
Craig Hicks
2
use "csplit -z" para no dejar archivos vacíos.
Paul M

Respuestas:

23

El fragmento awk funciona para extraer las diferentes partes, pero aún necesita saber qué sección es la clave / certificado / cadena. Necesitaba extraer una sección específica, y encontré esto en la lista de correo de OpenSSL: http://openssl.6102.n7.nabble.com/Convert-pem-to-crt-and-key-files-tp47681p47697.html

# Extract key
openssl pkey -in foo.pem -out foo-key.pem

# Extract all the certs
openssl crl2pkcs7 -nocrl -certfile foo.pem |
  openssl pkcs7 -print_certs -out foo-certs.pem

# Extract the textually first cert as DER
openssl x509 -in foo.pem -outform DER -out first-cert.der
Johannes 'pez' Ziemke
fuente
bonito conjunto de comandos :) Lo guardaré para uso futuro, pero en mi caso de uso anterior, estoy trabajando con un archivo de solo certificado que contiene más de 50 certificados de CA ==> sin clave ni cadena
Cerber
2
Creo que esto es superior a la solución awk, deja que el openssl haga el análisis + obtienes la conversión.
Rusty
Lo siento, pero solo el comando pkey es correcto. Segundo y tercero no hacen lo que anuncian, hacen otra cosa. En algunos casos, el resultado es bueno, en otros casos puede generar comportamientos misteriosos en los consumidores. Editado un poco.
kubanczyk
¿Alguna idea de cómo obtener el tercer certificado textualmente de esta manera?
flickerfly
15

El splitcomando está disponible en la mayoría de los sistemas, y es probable que su invocación sea más fácil de recordar.

Si tiene un archivo collection.pemque desea dividir en individual-*archivos, use:

split -p "-----BEGIN CERTIFICATE-----" collection.pem individual-

Si no tienes split, puedes probar csplit:

csplit -f individual- collection.pem '/-----BEGIN CERTIFICATE-----/' '{*}'
calamares
fuente
2
Lo sentimos, ninguno de mis sistemas (busybox, fedora, centos) muestra una -popción (ni las páginas de manual que leí ) en split. Tal vez esté utilizando un paquete / binario especial
Cerber
1
@Cerber podría intentar en su csplitlugar ... (ver edición anterior)
squidpickles
1
funciona bien con csplit!
Cerber
En FreeBSD obtengo de csplit: csplit: *}: bad repetition count(pero la división parece funcionar)
Gwyneth Llewelyn
4

Si desea obtener un solo certificado de un paquete PEM de múltiples certificados, intente:

$ openssl crl2pkcs7 -nocrl -certfile INPUT.PEM | \
    openssl pkcs7 -print_certs | \
    awk '/subject.*CN=host.domain.com/,/END CERTIFICATE/'
  • Los primeros dos opensslcomandos procesarán un archivo PEM y lo volverán a escupir con líneas "subject:"y "issuer:"líneas antes de cada certificado. Si su PEM ya está formateado de esta manera, todo lo que necesita es el awkcomando final .
  • El comando awk escupirá el PEM individual que coincida con la cadena CN (nombre común).

Source1 , source2

cmcginty
fuente
No veo esto en tu fuente. Además, los PEM están codificados en Base64, no encontrará texto como "asunto", "CN", ... con awk
Cerber
1
Sí, esto no funciona para todos los tipos de PEM. Si extrae un P7B a PEM usando openssl, tendrá una línea de asunto antes de cada certificado. O puede modificar a cualquier cadena con la que segmente su archivo PEM.
cmcginty
Respuesta actualizada para manejar cuando PEM no contiene "asunto"
cmcginty
3

También vale la pena señalar que los archivos PEM son solo una colección de claves / certificados dentro BEGIN/ ENDbloques, por lo que es bastante fácil simplemente cortar / pegar si se trata de un solo archivo con una o dos entidades interesantes ...

mgalgs
fuente
2

Si está manejando certificados de cadena completa (es decir, los generados por letsencrypt / certbot, etc.), que son una concatenación del certificado y la cadena de autoridad de certificación, puede utilizar la manipulación de cadenas bash.

Por ejemplo:

# content of /path/to/fullchain.pem
-----BEGIN CERTIFICATE-----
some long base64 string containing
the certificate
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
another base64 string
containing the first certificate
in the authority chain
-----END CERTIFICATE-----

-----BEGIN CERTIFICATE-----
another base64 string
containing the second certificate
in the authority chain
(there might be more...)
-----END CERTIFICATE-----

Para extraer el certificado y la cadena de autoridad de certificación en variables:

# load the certificate into a variable
FULLCHAIN=$(</path/to/fullchain.pem)
CERTIFICATE="${FULLCHAIN%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----"
CHAIN=$(echo -e "${FULLCHAIN#*-----END CERTIFICATE-----}" | sed '/./,$!d')

Explicación:

En lugar de usar awk o openssl (que son herramientas poderosas pero no siempre disponibles, es decir, en las imágenes de Docker Alpine), puede usar la manipulación de cadenas bash.

"${FULLCHAIN%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----": desde el final del contenido de FULLCHAIN, devuelve la coincidencia de subcadena más larga, luego concat a -----END CERTIFICATE-----medida que se elimina. El *coincide con todos los personajes después -----END CERTIFICATE-----.

$(echo -e "${FULLCHAIN#*-----END CERTIFICATE-----}" | sed '/./,$!d'): desde el comienzo del contenido de FULLCHAIN, devuelva la coincidencia de subcadena más corta y luego elimine las líneas nuevas. Del mismo modo, *coincide con todos los personajes anteriores -----END CERTIFICATE-----.

Para una referencia rápida (mientras puede encontrar más sobre manipulación de cadenas en bash aquí ):

${VAR#substring}= la subcadena más corta desde el principio del contenido de VAR

${VAR%substring}= la subcadena más corta desde el final del contenido de VAR

${VAR##substring}= la subcadena más larga desde el comienzo del contenido de VAR

${VAR%%substring}= la subcadena más larga desde el final del contenido de VAR

Fabio
fuente
Para los menos expertos, cuando haces eco de estas variables, rodea la variable con comillas para preservar los saltos de línea de la forma en que estás acostumbrado a verlos. Recuerdo cuando eso no era tan obvio para mí. Fabio, dulce uso de la manipulación de bash string!
flickerfly
0

Hmmm ... casi de la misma manera que preparé la solución (como se sugirió y @Cerber) sin darme cuenta de que esta situación parece que mucha gente lo ha hecho. Mi solución sigue casi la misma lógica pero uso algunos comandos más básicos:

Mis todos los certificados están en el archivo: certin.pem

c=0
while read line
  do
    if echo $line | grep END; then
    echo $line >> certout$c.pem
    c=`expr $c + 1`
    else
     echo $line
     echo $line >> certout$c.pem
    fi
done < /tmp/certin.pem

Básicamente, esto sigue escribiendo en un archivo hasta que se encuentra "FIN" y luego comienza a escribir en otro archivo de manera incremental. De esta manera, tendrá un número "N" de archivos de salida ( certout0.pem, certout1.pem, etc.), dependiendo de cuántos certificados haya en su archivo pem de entrada ( certin.pem ).

Ashish K Srivastava
fuente