Вопрос или проблема
У меня есть пакет сертификатов в файле .crt.
При выполнении команды openssl x509 -in bundle.crt -text -noout
отображается только корневой сертификат.
Как увидеть остальные сертификаты?
http://comments.gmane.org/gmane.comp.encryption.openssl.user/43587 предлагает следующую однострочную команду:
openssl crl2pkcs7 -nocrl -certfile CHAINED.pem | openssl pkcs7 -print_certs -text -noout
Это действительно сработало для меня, но я не понимаю деталей, поэтому не могу сказать, есть ли какие-то подводные камни.
обновлено 22 июня:
для openssl 1.1.1 и выше: ответ в одну команду можно найти здесь serverfault.com/a/1079893 (openssl storeutl -noout -text -certs bundle.crt
)
Чтобы просто перечислить субъекты:
openssl storeutl -noout -text -certs \
$(openssl version -d | sed -E 's/OPENSSLDIR: "([^"]*)"/\1/')/cert.pem | \
grep Subject:
Команда keytool
из Java выполняет эту задачу:
keytool -printcert -v -file <certs.crt>
Примечание: Двойной клик в Windows не работает. Windows читает только первый сертификат в хранилище ключей и автоматически расширяет цепочку доверия из своего встроенного хранилища сертификатов.
Результаты:
- Все, кроме первого сертификата в файле
.crt
, не отображаются - Вы можете увидеть отображаемую цепочку доверия, отличную от той, что у вас в файле
.crt
. Это может привести к неправильным выводам.
openssl storeutl -noout -text -certs bundle.crt
Парафразируя из документации OpenSSL:
Программа openssl storeutl
была добавлена в OpenSSL 1.1.1.
Команда storeutl
может использоваться для отображения содержимого, извлеченного из заданных URI.
-noout
предотвращает вывод данных PEM-text
выводит объекты в текстовом виде, аналогично выводу-text
изopenssl x509
-certs
только выбирает сертификаты из данного URI
Однострочник, который отображает сводку каждого сертификата в файле.
openssl crl2pkcs7 -nocrl -certfile CHAINED.pem | openssl pkcs7 -print_certs -noout
Он объединяет все сертификаты в один промежуточный файл PKCS7 и затем разбирает информацию в каждой части этого файла.
(Это то же самое, что ответ Бени, но выводит короче, без опции -text
).
пример:
$ openssl crl2pkcs7 -nocrl -certfile bundled.crt | openssl pkcs7 -print_certs -noout
subject=/C=NL/postalCode=5705 CN/L=City/street=Example 20/O=Foobar B.V./OU=ICT/OU=Wildcard SSL/CN=*.example.com
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
subject=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
subject=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
issuer=/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Roo
Следование этой FAQ привело меня к этому perl-скрипту, который очень настоятельно заставляет меня думать, что openssl
не имеет нативной поддержки для обработки nth сертификата в пакетах, и что вместо этого мы должны использовать какой-то инструмент для разделения ввода перед тем, как передавать каждый сертификат в openssl
. Этот perl-скрипт, свободно адаптированный от скрипта Ника Берча, кажется, выполняет задачу:
#!/usr/bin/perl
# скрипт для разделения много сертификатного ввода на отдельные сертификаты
# Художественная лицензия
#
# v0.0.1 Ник Бёрч <[email protected]>
# v0.0.2 Том Ятс <[email protected]>
#
$filename = shift;
unless($filename) {
die("Вы должны указать файл сертификата.\n");
}
open INP, "<$filename" or die("Не удалось загрузить \"$filename\"\n");
$thisfile = "";
while(<INP>) {
$thisfile .= $_;
if($_ =~ /^\-+END(\s\w+)?\sCERTIFICATE\-+$/) {
print "Найден полный сертификат:\n";
print `echo \'$thisfile\' | openssl x509 -noout -text`;
$thisfile = "";
}
}
close INP;
Поскольку нет решения на основе awk:
$ awk '/BEGIN/ { i++; } /BEGIN /, /END / { print > i ".extracted.crt" }' ca-bundle
$ for cert in *.extracted.crt; do openssl x509 -in $cert -text -noout; done
Первая команда разбивает пакет на сертификаты, ищя строки “BEGIN ” и “END “. Вторая команда проходит по извлечённым сертификатам и отображает их.
Это может быть не красиво или элегантно, но это было быстро и сработало для меня, используя bash на linux и блоки в формате PEM в файле ca-cert bundle.
while read line
do
if [ "${line//END}" != "$line" ]; then
txt="$txt$line\n"
printf -- "$txt" | openssl x509 -subject -issuer -noout
txt=""
else
txt="$txt$line\n"
fi
done < /path/to/bundle/file
Вы можете это всё объединить в одну строку и подстроить опции openssl под себя. Я действительно希望, что для этого было бы более элегантное решение, но в данном случае я думаю, что поиск более элегантного решения занял бы больше времени, чем написание неэлегантного.
Попробуйте этот скрипт:
https://github.com/jkolezyn/cert_tree
Он отображает сертификаты в pem-пакете в виде дерева, построенного на основе полей Subject и Issuer.
Он выводит дерево вроде этого:
cert_tree.py -p ~/.certs/ca_list.pem
━ CorpRoot [1]
┣━ ServerCA [2]
┣━ example_cert [3]
┗━ example_2 [8]
━ RootCert [4]
┣━ example_cert3 [5] [ИСТЕК 03.06.2019 13:26:21]
┣━ other [6]
┣━ other1 [7] [ИСТЕК 16.06.2017 21:12:18]
┗━ AnotherOne [9]
cert_tree.py -pe ~/.certs/mycert.pem
━ RootCert [3] [действителен до: 2023-07-08 17:57:15]
┗━ IntermediateCert [2] [действителен до: 2023-07-08 18:55:58]
┗━ UserCert [1] [действителен до: 2023-09-17 13:33:00]
В bash обычно нужна только одна (длинная) строка кода 🙂
tfile=$( mktemp -u ) && \
csplit -z -q -f "$tfile" bundle.crt '/----BEGIN CERTIFICATE-----/' '{*}' && \
find "${tfile%/*}" -name "${tfile##*/}*" -exec openssl x509 -noout -subject -in "{}" \; -delete
awk -v cmd='openssl x509 -noout -text' \
'/BEGIN/{close(cmd)}; {print | cmd}' \
< bundle.pem | less
Или чтобы перечислить только субъекты и издателей:
awk -v cmd='openssl x509 -noout -subject -issuer' \
'/BEGIN/{close(cmd)}; {print | cmd}' \
< bundle.pem
Как это работает. print
запускает cmd
и передает ему строки одну за другой, пока не достигнется строка /BEGIN/
. В этот момент он закрывает трубку. Следующий print
запускает другую cmd
и начинает передавать строки в свежесозданную трубку.
Долг чести принадлежит Стефану Шазелу.
Вот решение на основе awk, которое не зависит от промежуточных файлов.
cat bundle.crt | awk '{
if ($0 == "-----BEGIN CERTIFICATE-----") cert=""
else if ($0 == "-----END CERTIFICATE-----") print cert
else cert=cert$0
}' | while read CERT; do
echo "$CERT" | base64 -d | openssl x509 -inform DER -text -noout
done
Это работает, извлекая блоки PEM из stdin и объединяя каждый блок в одну строку, закодированную в формате base64. Затем строки читаются, декодируются и передаются в openssl как сертификаты, закодированные в DER.
Я хотел бы вставить здесь идиоматическую perl-команду:
perl -ne "\$n++ if /BEGIN/; print if \$n == 1;" mysite.pem
Если есть текст, то немного более надежное изменение:
perl -ne "\$n++ if /^-----BEGIN CERTIFICATE-----\$/; print if \$n == 3 && /^-----BEGIN CERTIFICATE-----\$/.../^-----END CERTIFICATE-----\$/;" mysite.pem
Просто измените значение, что должно быть n во втором утверждении, чтобы получить n-ный сертификат.
@user1686 предложил другое решение в https://superuser.com/questions/1599666/view-all-certs-in-a-pem-cert-file-full-cert-chain-with-openssl-or-another-comm
это часть стека GnuTLS.
certtool -i < multiplecerts.pem
Небольшое изменение в публикации MadHatter, чтобы позволить вам копировать/вставлять прямо в CLI. Я также включил хеш MD5, который полезен при проверке корректности сертификатов. Строка stdin возвращает md5 хеш сертификата(ов).
perl -e 'my $thisfile = "";
foreach (<>) {
$thisfile .= $_;
if($_ =~ /^\-+END(\s\w+)?\sCERTIFICATE\-+$/) {
print "Найден полный сертификат:\n";
print `echo "$thisfile" | openssl x509 -noout -text`;
print `echo "$thisfile" | openssl x509 -noout -modulus | openssl md5`;
$thisfile = "";
}
}' < my_id_cert_and_ca_bundle.crt
Если вы хотите получить хороший краткий вывод, вы можете использовать эту версию. Полезно, если вы просто проверяете, что у вас есть все сертификаты, но не проверяете использование и т. д. сертификата(ов).
perl -e 'my $thisfile = "";
foreach (<>) {
$thisfile .= $_;
if($_ =~ /^\-+END(\s\w+)?\sCERTIFICATE\-+$/) {
print "Найден полный сертификат:\n";
print `echo "$thisfile" | openssl x509 -noout -serial -subject -dates -alias -issuer`;
print `echo "$thisfile" | openssl x509 -noout -modulus | openssl md5` . "\n";
$thisfile = "";
}
}' < my_id_cert_and_ca_bundle.crt
На всякий случай, если ваша версия openssl не поддерживает все эти флаги, вот некоторые egrep, которые вы можете использовать. То же самое, что и в первом, но просто передать в egrep.
perl -e '.....
' < my_id_cert_and_ca_bundle.crt | egrep "Serial|Subject:|Not |Public-Key|^Cert|stdin|ssuer"
Чтобы проверить MD5 хеш частного ключа, вы можете сделать следующее.
openssl rsa -noout -modulus -in privateKey.key | openssl md5
Ответ или решение
Чтобы просмотреть все SSL-сертификаты в файле-обертке (например, в файле с расширением .crt), вы можете использовать различные команды и скрипты, поскольку OpenSSL не поддерживает прямой вывод всех сертификатов из одного файла одновременно. Ниже приведены несколько методов для достижения этой цели.
1. Использование команды OpenSSL
Начиная с OpenSSL версии 1.1.1, вы можете использовать команду openssl storeutl
, чтобы извлечь и отобразить все сертификаты в файле. Команда выглядит следующим образом:
openssl storeutl -noout -text -certs bundle.crt
2. Использование комбинации OpenSSL команд
Если у вас есть несколько сертификатов в PEM-формате, вы можете сначала скомбинировать их в формат PKCS#7, а затем вывести. Этот метод позволит вам увидеть все сертификаты:
openssl crl2pkcs7 -nocrl -certfile bundle.crt | openssl pkcs7 -print_certs -noout
3. Использование инструментов Java Keytool
Если у вас установлен Java, вы можете использовать команду keytool
для вывода всех сертификатов в файле:
keytool -printcert -v -file bundle.crt
4. Скрипты и утилиты
Для более автоматизированного подхода можно использовать скрипты. Например, вы можете воспользоваться следующим Perl-скриптом, который разделит файл на отдельные сертификаты и выведет их:
#!/usr/bin/perl
use strict;
use warnings;
my $filename = shift;
unless ($filename) {
die("Укажите файл сертификатов.\n");
}
my $thisfile = "";
open my $inp, '<', $filename or die("Не удалось открыть \"$filename\"\n");
while (<$inp>) {
$thisfile .= $_;
if ($_ =~ /^-+END(\s\w+)?\sCERTIFICATE-+$/) {
print "Найден полный сертификат:\n";
print `echo "$thisfile" | openssl x509 -noout -text`;
$thisfile = "";
}
}
close $inp;
5. Использование команды awk
Если вы предпочитаете решение без использования промежуточных файлов, вы можете использовать awk
в вашей командной строке:
awk -v cmd='openssl x509 -noout -text' '/BEGIN/{close(cmd)}; {print | cmd}' < bundle.pem
6. Утилиты GnuTLS
Если у вас установлена GnuTLS, вы также можете использовать certtool
, чтобы просмотреть сертификаты в файле:
certtool -i < multiplecerts.pem
Заключение
Каждый из этих методов подходит для просмотра всех сертификатов в файле-обертке. Выбор метода зависит от ваших предпочтений и доступного программного обеспечения. Метод openssl storeutl
является наиболее простым и современным подходом, если вы используете OpenSSL 1.1.1 или выше.