Подтвердить что ты не робот

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

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

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

openssl s_client -connect www.website.com:443 

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

4b9b3361

Ответ 1

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

Как получить SAN непосредственно из файла?

Для этого вам не нужна команда openssl s_client. Просто добавьте -in MyCertificate.crt в команду openssl x509 и еще раз проведите через grep, например:

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

Ответ 2

Есть ли способ программно проверить альтернативные имена сертификата 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);

}

Ответ 3

Если вы просто хотите увидеть 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 заставляет его читать весь ввод сразу, а не построчно)