Como dividir um arquivo PEM

38

Nota: Esta não é realmente uma pergunta, porque eu já encontrei a resposta, mas como não a encontrei facilmente aqui, postarei para que possa beneficiar outras pessoas.

Pergunta: Como ler um arquivo PEM concatenado como aquele usado pela diretiva apache / mod_ssl SSLCACertificateFile ?

Resposta (original) ( fonte ):

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

Isso pode deixar um arquivo vazio se houver uma linha em branco no final, como com openssl pkcs7 -outform PEM -in my-chain-file -print_certs. Para evitar isso, verifique o comprimento da linha 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"}' 

Resposta 29/03/2016 :

Após a resposta @slugchewer , csplitpode ser uma opção mais clara com:

csplit -f cert- $file '/-----BEGIN CERTIFICATE-----/' '{*}'
Cerber
fonte
Essa pode ser uma pergunta idiota, mas por que eu precisaria dividir meu arquivo pem?
Ashwani Agarwal 26/03
6
@AshwaniAgarwal Você deseja dividir um arquivo PEM quando ele contém vários certificados e deseja examiná-los individualmente com ferramentas como as opensslque requerem um certificado para analisar.
Law29
Além disso, algumas ferramentas ou servidores desejam um arquivo combinado com cert e key, enquanto outros os desejam separados.
Captncraig 6/01/17
Eu tive que adicionar '% ----- BEGIN CERTIFICATE -----%' à linha de comando csplit para evitar um arquivo vazio. Parece coincidir com o que a página de manual especifica: csplit -f ./tmp/cert- $ file '% ----- COMEÇAR CERTIFICADO -----%' '/ ----- COMEÇAR CERTIFICADO ----- / '' {*} '
Craig Hicks
2
use "csplit -z" para não deixar arquivos vazios.
Paul M

Respostas:

23

O snippet awk funciona para extrair as diferentes partes, mas você ainda precisa saber qual seção é a chave / cert / chain. Eu precisava extrair uma seção específica e encontrei isso na lista de discussão do 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
Ziemke, peixe de Johannes
fonte
conjunto de comandos nice :) Vou mantê-lo para uso futuro, mas no meu caso de uso acima, estou trabalhando com um arquivo somente de certificado contendo mais de 50 certificados CA ==> sem pkey nem cadeia
Cerber
2
Eu acho que isso é superior à solução awk, deixe o openssl fazer a análise + você obtém a conversão.
Rusty
Sinto muito, mas apenas o comando pkey está correto. Segundo e terceiro não fazem o que você anuncia - eles fazem outra coisa. Em alguns casos, o resultado é bom; em alguns casos, pode gerar comportamentos misteriosos nos consumidores. Editado um pouco.
kubanczyk
Alguma idéia de como obter o terceiro certificado textual dessa maneira?
flickerfly 6/09
16

Isso foi respondido anteriormente no StackOverflow :

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

Editar 29/03/2016 : consulte a resposta @slugchewer

Cerber
fonte
Funciona apenas no Linux, falha no FreeBSD.
Michael-O
3
Inspirado por isso, criei um script awk que divide certs e chaves em arquivos separados: gist.github.com/jinnko/d6867ce326e8b6e88975
JinnKo 23/15
15

O splitcomando está disponível na maioria dos sistemas e é provável que seja mais fácil lembrar sua chamada.

Se você possui um arquivo collection.pemque deseja dividir em individual-*arquivos, use:

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

Se você não tiver split, tente csplit:

csplit -f individual- collection.pem '/-----BEGIN CERTIFICATE-----/' '{*}'
squidpickles
fonte
2
Desculpe, nenhum dos meus sistemas (busybox, fedora, centos) mostra uma -popção (nem as páginas de manual que li ) na divisão. Talvez você esteja usando um binário / pacote especial
Cerber
1
@Cerber poderia tentar csplitem vez disso ... (ver edição anterior)
squidpickles
1
funciona bem com csplit!
Cerber 29/03/16
No FreeBSD que recebo de csplit: csplit: *}: bad repetition count(mas dividida parece funcionar)
Gwyneth Llewelyn
4

Se você deseja obter um único certificado de um pacote PEM com vários certificados, tente:

$ openssl crl2pkcs7 -nocrl -certfile INPUT.PEM | \
    openssl pkcs7 -print_certs | \
    awk '/subject.*CN=host.domain.com/,/END CERTIFICATE/'
  • Os dois primeiros opensslcomandos processam um arquivo PEM e devolvem-no com linhas "subject:"e pré-pendentes "issuer:"antes de cada certificado. Se o seu PEM já estiver formatado dessa maneira, tudo o que você precisa é o awkcomando final .
  • O comando awk cuspirá o PEM individual correspondente à string CN (nome comum).

source1 , source2

cmcginty
fonte
Não vejo isso na sua fonte. Ao lado, PEM são Base64 codificado você não vai encontrar texto como "sujeito", "CN", ... com awk
Cerber
1
Sim, isso não funciona para todos os tipos de PEM. Se você extrair um P7B para o PEM usando o openssl, ele terá uma linha de assunto listada antes de cada certificado. Ou você pode modificar para qualquer sequência com a qual segmentar seu arquivo PEM.
Cmcginty
Resposta atualizada para lidar quando o PEM não contém "assunto"
cmcginty
3

Também é importante notar que os arquivos PEM são apenas uma coleção de chaves / certificados dentro de BEGIN/ ENDblocks, por isso é muito fácil recortar / colar se for apenas um único arquivo com uma ou duas entidades interessantes ...

mgalgs
fonte
2

Se você estiver manipulando certificados de cadeia completa (ou seja, os gerados pelo letsencrypt / certbot etc.), que são uma concatenação do certificado e da cadeia de autoridade de certificação, você pode usar a manipulação de string bash.

Por exemplo:

# 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 extrair o certificado e a cadeia de autoridade de certificação em variáveis:

# 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')

Explicação:

Em vez de usar o awk ou o openssl (que são ferramentas poderosas, mas nem sempre disponíveis, ou seja, nas imagens do Docker Alpine), você pode usar a manipulação do bash string.

"${FULLCHAIN%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----": do final do conteúdo de FULLCHAIN, retorne a correspondência de substring mais longa e concorde à -----END CERTIFICATE-----medida que for removida. As *partidas todos os personagens depois-----END CERTIFICATE----- .

$(echo -e "${FULLCHAIN#*-----END CERTIFICATE-----}" | sed '/./,$!d'): desde o início do conteúdo de FULLCHAIN, retorne a menor correspondência de substring e retire novas linhas. Da mesma forma, *corresponde a todos os caracteres anteriores-----END CERTIFICATE----- .

Para uma referência rápida (enquanto você pode encontrar mais sobre manipulação de string no bash aqui ):

${VAR#substring}= a substring mais curta desde o início do conteúdo do VAR

${VAR%substring}= a substring mais curta do final do conteúdo do VAR

${VAR##substring}= a substring mais longa desde o início do conteúdo do VAR

${VAR%%substring}= a substring mais longa do final do conteúdo do VAR

Fabio
fonte
Para os menos exigentes, quando você faz o eco dessas variáveis, envolve a variável entre aspas para preservar as quebras de linha da maneira que você está acostumado a vê-las. Lembro-me de quando isso não era tão óbvio para mim. Fabio, uso doce da manipulação de cordas do bash!
flickerfly 6/09
0

Hmmm ... quase da mesma maneira que eu preparei a solução (como sugerido no @Cerber) sem perceber que essa situação parece que muitas pessoas têm. Minha solução segue quase a mesma lógica, mas usa alguns comandos mais básicos:

Todos os meus certificados estão em arquivo: 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

Isso basicamente continua gravando em um arquivo até encontrar "END" e depois começa a gravar em outro arquivo de maneira incrementada. Dessa forma, você terá o número "N" de arquivos de saída ( certout0.pem, certout1.pem e assim por diante ..) dependendo de quantos certificados existem no seu arquivo pem de entrada ( certin.pem ).

Ashish K Srivastava
fonte