Jak podzielić plik PEM

38

Uwaga: To nie jest tak naprawdę pytanie, ponieważ już znalazłem odpowiedź, ale ponieważ nie znalazłem jej tutaj łatwo, opublikuję ją, aby mogła przynieść korzyści innym.

Pytanie: Jak odczytać skonkatowany plik PEM jako plik używany przez dyrektywę apache / mod_ssl SSLCACertificateFile ?

Odpowiedź (oryginał) ( źródło ):

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

Może to pozostawić pusty plik, jeśli na końcu jest pusty wiersz, na przykład with openssl pkcs7 -outform PEM -in my-chain-file -print_certs. Aby temu zapobiec, sprawdź długość linii przed drukowaniem:

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

Odpowiedź 29.03.2016 :

W następstwie @slugchewer odpowiedź , csplitmoże być wyraźniejszy opcja z:

csplit -f cert- $file '/-----BEGIN CERTIFICATE-----/' '{*}'
Cerber
źródło
To może być głupie pytanie, ale dlaczego miałbym dzielić plik pem?
Ashwani Agarwal
6
@AshwaniAgarwal Chcesz podzielić plik PEM, gdy zawiera on kilka certyfikatów, i chcesz sprawdzić certyfikaty indywidualnie za pomocą narzędzi takich jak opensslten, którego analizowanie wymaga jednego certyfikatu.
Ustawa 29
Ponadto niektóre narzędzia lub serwery chcą połączyć plik z certyfikatem i kluczem, a inne chcą je osobno.
captncraig
Musiałem dodać „% ----- ROZPOCZNIJ CERTYFIKAT -----%” w wierszu polecenia csplit, aby zapobiec pustemu plikowi. Wydaje się pasować do tego, co określa strona podręcznika: csplit -f ./tmp/cert- $ plik '% ----- ROZPOCZNIJ CERTYFIKAT -----%' '/ ----- ROZPOCZNIJ CERTYFIKAT ----- / '' {*}
Craig Hicks
2
użyj „csplit -z”, aby nie zostawiać pustych plików.
Paul M

Odpowiedzi:

23

Fragment awk działa w celu wyodrębnienia różnych części, ale nadal musisz wiedzieć, która sekcja to klucz / cert / chain. Musiałem wyodrębnić określoną sekcję i znalazłem ją na liście mailingowej 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 „fish” Ziemke
źródło
fajny zestaw poleceń :) Zatrzymam go do przyszłego użytku, ale w powyższym przypadku użycia pracuję z plikiem zawierającym tylko certyfikaty zawierające ponad 50 certyfikatów CA ==> bez klucza ani łańcucha
Cerber
2
Myślę, że jest to lepsze niż rozwiązanie awk, pozwól openssl wykonać parsowanie + otrzymasz konwersję.
Rusty
Przykro mi, ale tylko polecenie pkey jest poprawne. Drugi i trzeci nie robią tego, co reklamujesz - robią coś innego. W niektórych przypadkach wynik jest dobry, w niektórych przypadkach może generować tajemnicze zachowania konsumentów. Trochę edytowane.
kubańczyk
Masz pomysł, jak uzyskać tekstowo trzeci certyfikat w ten sposób?
flickerfly
16

Na to wcześniej odpowiedziano na StackOverflow :

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

Edytuj 29.03.2016: Zobacz odpowiedź @slugchewer

Cerber
źródło
Działa tylko w systemie Linux, awaria we FreeBSD.
Michael-O
3
Zainspirowany tym stworzyłem skrypt awk, który dzieli certyfikaty i klucze na osobne pliki: gist.github.com/jinnko/d6867ce326e8b6e88975
JinnKo
15

splitPolecenie jest dostępne w większości systemów, a jego wywołanie jest prawdopodobnie łatwiejsze do zapamiętania.

Jeśli masz plik collection.pem, który chcesz podzielić na individual-*pliki, użyj:

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

Jeśli nie masz split, możesz spróbować csplit:

csplit -f individual- collection.pem '/-----BEGIN CERTIFICATE-----/' '{*}'
kalmary
źródło
2
Niestety, żaden z moich systemów (busybox, fedora, centos) nie wyświetla -popcji (ani stron, które czytam ) przy podziale. Być może używasz specjalnego pliku binarnego / pakietu
Cerber
1
@Cerber Może csplitzamiast tego spróbować ... (patrz edycja powyżej)
squidpickles
1
działa dobrze z csplit!
Cerber
Na FreeBSD dostaję z csplit: csplit: *}: bad repetition count(ale split wydaje się działać)
Gwyneth Llewelyn
4

Jeśli chcesz uzyskać pojedynczy certyfikat z pakietu PEM z wieloma certyfikatami, spróbuj:

$ openssl crl2pkcs7 -nocrl -certfile INPUT.PEM | \
    openssl pkcs7 -print_certs | \
    awk '/subject.*CN=host.domain.com/,/END CERTIFICATE/'
  • Pierwsze dwa opensslpolecenia przetworzą plik PEM i wyplują go z poprzednimi wierszami "subject:"i "issuer:"wierszami przed każdym certyfikatem. Jeśli twój PEM jest już sformatowany w ten sposób, wszystko czego potrzebujesz to ostatnie awkpolecenie.
  • Komenda awk wypluje indywidualny PEM pasujący do łańcucha CN (nazwa zwyczajowa).

źródło1 , źródło2

cmcginty
źródło
Nie widzę tego w twoim źródle. Poza tym PEM są zakodowane w standardzie Base64, nie znajdziesz tekstu takiego jak „temat”, „CN”, ... z awk
Cerber
1
Tak, to nie działa dla każdego typu PEM. Jeśli wyodrębnisz P7B do PEM za pomocą openssl, będzie on zawierał wiersz tematu przed każdym certyfikatem. Możesz też zmodyfikować dowolny ciąg, za pomocą którego segmentujesz plik PEM.
cmcginty
Zaktualizowana odpowiedź do obsługi, gdy PEM nie zawiera „podmiotu”
cmcginty
3

Warto również zauważyć, że pliki PEM są tylko zbiorem kluczy / certyfikatów wewnątrz BEGIN/ ENDbloków, więc dość łatwo jest wyciąć / wkleić, jeśli jest to pojedynczy plik z jednym lub dwoma interesującymi elementami ...

mgalgs
źródło
2

Jeśli masz do czynienia z certyfikatami pełnego łańcucha (tj. Wygenerowanymi przez letsencrypt / certbot itp.), Które są połączeniem certyfikatu i łańcucha urzędu certyfikacji, możesz użyć manipulacji ciągiem bash.

Na przykład:

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

Aby wyodrębnić certyfikat i łańcuch urzędu certyfikacji do zmiennych:

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

Wyjaśnienie:

Zamiast używać awk lub openssl (które są potężnymi narzędziami, ale nie zawsze dostępne, tj. W obrazach Docker Alpine), możesz użyć manipulacji ciągiem bash.

"${FULLCHAIN%%-----END CERTIFICATE-----*}-----END CERTIFICATE-----": od końca zawartości FULLCHAIN, zwróć najdłuższe dopasowanie podłańcucha, a następnie konkat, -----END CERTIFICATE-----gdy zostanie usunięte. Te *wszystkie mecze znaki po -----END CERTIFICATE-----.

$(echo -e "${FULLCHAIN#*-----END CERTIFICATE-----}" | sed '/./,$!d'): od początku zawartości FULLCHAIN, zwróć najkrótsze dopasowanie podłańcucha, a następnie usuń wiodące nowe wiersze. Podobnie, *pasuje do wszystkich wcześniejszych postaci -----END CERTIFICATE-----.

Dla szybkiego odniesienia (chociaż można znaleźć więcej na temat manipulacji ciągami w bash tutaj ):

${VAR#substring}= najkrótszy podciąg od początku zawartości VAR

${VAR%substring}= najkrótszy podciąg od końca zawartości VAR

${VAR##substring}= najdłuższy podciąg od początku zawartości VAR

${VAR%%substring}= najdłuższy podciąg od końca zawartości VAR

Fabio
źródło
Dla mniej znających się na bashu, kiedy echo tych zmiennych zostanie otoczone cudzysłowem, aby zachować podział wierszy w sposób, w jaki jesteś przyzwyczajony do ich oglądania. Pamiętam, kiedy nie było to dla mnie takie oczywiste. Fabio, słodkie użycie manipulacji sznurkiem!
flickerfly
0

Hmmm ... prawie w ten sam sposób, w jaki przygotowałem rozwiązanie (zgodnie z sugestią @Cerber), nie zdając sobie sprawy z tego, że wiele osób ma taką sytuację. Moje rozwiązanie działa zgodnie z tą samą logiką, ale używa bardziej podstawowych poleceń:

Moje wszystkie certyfikaty są w pliku: 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

Zasadniczo kontynuuj pisanie w pliku, aż napotka „END”, a następnie zacznij pisać do innego pliku w sposób przyrostowy. W ten sposób będziesz mieć liczbę „N” plików wyjściowych ( certout0.pem, certout1.pem itd.) W zależności od liczby certyfikatów w wejściowym pliku pem ( certin.pem ).

Ashish K Srivastava
źródło