Как проверить альтернативные имена субъекта для сертификата SSL/TLS?

Есть ли способ программно проверить альтернативные имена субъекта сертификата SAN SSL?

Используя, например, следующую команду, я могу получить много информации, но не все SAN:

openssl s_client -connect www.website.com:443 

Большое спасибо!

5 ответов

Решение

Чтобы получить альтернативные имена субъекта (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

Тогда труба (|) что в эту команду:

openssl x509 -noout -text

Это берет файл сертификата и выводит все его сочные детали. -noout flag не позволяет выводить сам файл сертификата (в кодировке base64), который нам не нужен. -text флаг говорит ему выводить детали сертификата в текстовой форме.

Обычно есть много выходных данных (подпись, издатель, расширения и т. Д.), Которые нам не нужны, поэтому мы передаем их в простой grep:

grep DNS:

Поскольку записи в SAN начинаются с DNS: this simply returns only the lines that contain that, stripping out all the other info and leaving us with the desired information.

You may note that the command does not cleanly exit; openssl s_client actually acts as a client and leaves the connection open, waiting for input. If you want it to immediately exit (eg to parse the output in a shell script) simply pipe echo into it:

echo | openssl s_client -connect website.com:443 | openssl x509 -noout -text | grep DNS:

How do I get the SAN directly from a file?

For this, you don't need the openssl s_client команда. Просто добавь -in MyCertificate.crt на openssl x509 command and once again pipe through grep, eg:

openssl x509 -noout -text -in MyCertificate.crt | grep DNS:

Также можно использовать встроенные функции openssl:

openssl s_client -connect website.com:443 </dev/null | openssl x509 -noout -ext subjectAltName

Если вы просто хотите увидеть сети 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 -0777 -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(",",

(The -0777 переключатель для perl позволяет читать весь ввод сразу, а не построчно)

Есть ли способ программно проверить альтернативные имена сертификата SAN SSL?

В сертификате X509 может быть несколько SAN. Ниже приводится вики OpenSSL на клиенте SSL / TLS. Он перебирает имена и печатает их.

Вы получаете 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's 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 — по одному в строке:

      openssl x509 -noout -text -in "${CERT_FILE}" | grep -Po 'DNS:\K[^,]+'

Лучшее,

Другие вопросы по тегам