Есть ли способ программно проверить альтернативные имена объекта сертификата SAN SSL?
Используя, например, следующую команду, я могу получить много информации, но не все SAN:
openssl s_client -connect www.website.com:443
Большое спасибо!
Есть ли способ программно проверить альтернативные имена объекта сертификата SAN SSL?
Используя, например, следующую команду, я могу получить много информации, но не все SAN:
openssl s_client -connect www.website.com:443
Большое спасибо!
Чтобы получить альтернативные имена объекта (SAN) для сертификата, используйте следующую команду:
openssl s_client -connect website.com:443 | openssl x509 -noout -text | grep DNS:
Сначала эта команда подключается к сайту, который мы хотим (website.com, порт 443 для SSL):
openssl s_client -connect website.com:443
Затем pipe (|
) в эту команду:
openssl x509 -noout -text
Это берет файл сертификата и выводит все его сочные детали. Флаг -noout
не позволяет выводить сам файл сертификата (base64-encoded), который нам не нужен. Флаг -text
сообщает ему выводить данные сертификата в текстовой форме.
Обычно там очень много выходных данных (подпись, эмитент, расширения и т.д.), которые нам не нужны, поэтому мы передаем это в простой grep:
grep DNS:
Поскольку записи SAN начинаются с DNS:
, это просто возвращает только строки, которые содержат это, удаляя всю другую информацию и оставляя нам нужную информацию.
Вы можете заметить, что команда не выполняет чистое завершение; openssl s_client
фактически действует как клиент и оставляет соединение открытым, ожидая ввода. Если вы хотите, чтобы он немедленно вышел (например, для анализа вывода в оболочке script), просто вставьте в него echo
:
echo | openssl s_client -connect website.com:443 | openssl x509 -noout -text | grep DNS:
Для этого вам не нужна команда openssl s_client
. Просто добавьте -in MyCertificate.crt
в команду openssl x509
и еще раз проведите через grep, например:
openssl x509 -noout -text -in MyCertificate.crt | grep DNS:
Есть ли способ программно проверить альтернативные имена сертификата SAN SSL?
В сертификате X509 может быть несколько SAN. Ниже из вики OpenSSL в SSL/TLS Client. Он перебирает имена и печатает их.
Вы получаете X509*
от функции типа SSL_get_peer_certificate
из TLS-соединения, d2i_X509
из памяти или PEM_read_bio_X509
из файловой системы.
void print_san_name(const char* label, X509* const cert)
{
int success = 0;
GENERAL_NAMES* names = NULL;
unsigned char* utf8 = NULL;
do
{
if(!cert) break; /* failed */
names = X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0 );
if(!names) break;
int i = 0, count = sk_GENERAL_NAME_num(names);
if(!count) break; /* failed */
for( i = 0; i < count; ++i )
{
GENERAL_NAME* entry = sk_GENERAL_NAME_value(names, i);
if(!entry) continue;
if(GEN_DNS == entry->type)
{
int len1 = 0, len2 = -1;
len1 = ASN1_STRING_to_UTF8(&utf8, entry->d.dNSName);
if(utf8) {
len2 = (int)strlen((const char*)utf8);
}
if(len1 != len2) {
fprintf(stderr, " Strlen and ASN1_STRING size do not match (embedded null?): %d vs %d\n", len2, len1);
}
/* If there a problem with string lengths, then */
/* we skip the candidate and move on to the next. */
/* Another policy would be to fails since it probably */
/* indicates the client is under attack. */
if(utf8 && len1 && len2 && (len1 == len2)) {
fprintf(stdout, " %s: %s\n", label, utf8);
success = 1;
}
if(utf8) {
OPENSSL_free(utf8), utf8 = NULL;
}
}
else
{
fprintf(stderr, " Unknown GENERAL_NAME type: %d\n", entry->type);
}
}
} while (0);
if(names)
GENERAL_NAMES_free(names);
if(utf8)
OPENSSL_free(utf8);
if(!success)
fprintf(stdout, " %s: <not available>\n", label);
}
Если вы просто хотите увидеть SAN, то grep DNS:
- очевидное решение.
Если вы хотите иметь более чистый список для дальнейшей обработки, вы можете использовать это регулярное выражение Perl для извлечения только имен: @names=/\sDNS:([^\s,]+)/g
Например:
true | openssl s_client -connect example.com:443 2>/dev/null \
| openssl x509 -noout -text \
| perl -l -07 -ne '@names=/\bDNS:([^\s,]+)/g; print join("\n", sort @names);'
Что бы вывести это:
example.com
example.edu
example.net
example.org
www.example.com
www.example.edu
www.example.net
www.example.org
Так что вы можете передать это while read name; do echo "do stuff with $name"; done
и т.д.
Или для списка через одну строку, разделенного запятыми, замените join("\n",
на join(",",
(Переключатель -07
для perl заставляет его читать весь ввод сразу, а не построчно)