Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to display the Subject Alternative Name of a certificate?

The closest answer that I found is using "grep".

> openssl x509 -text -noout -in cert.pem | grep DNS

Is there better way to do this? I only prefer command line.

Thanks.

like image 445
user180574 Avatar asked Jan 07 '14 22:01

user180574


4 Answers

Note that you can limit the output of -text to just the extensions by adding the following option:

-certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_issuer,no_pubkey,no_sigdump,no_aux

i.e.:

openssl x509 -text -noout -in cert.pem \
  -certopt no_subject,no_header,no_version,no_serial,no_signame,no_validity,no_issuer,no_pubkey,no_sigdump,no_aux

However, you'll still need to apply some text parsing logic to get just the Subject Alternative Name.

If that isn't sufficient, I think you'll need to write a small program that uses the openssl library to extract the specific field you are looking for. Here are some example programs that show how to parse a cert, including extracting extension fields such as Subject Alternative Name:

https://zakird.com/2013/10/13/certificate-parsing-with-openssl

Note that you don't have to use openssl and C if you go the programming route... you can pick your favorite language and ASN.1 parser library, and use that. For example, in Java, you could use http://jac-asn1.sourceforge.net/, and many others.

like image 64
Raman Avatar answered Nov 06 '22 11:11

Raman


Newer versions of openssl have an '-ext' option that allows you to print only the subjectAltName record. Am using 'OpenSSL 1.1.1b' on Debian 9.9

openssl x509 -noout -ext subjectAltName -in cert.pem

Though you'll still need to parse the output.

The change was made in https://github.com/openssl/openssl/issues/3932

like image 36
Ely Avatar answered Nov 06 '22 13:11

Ely


Fetch Certificate Data

With gnutls and certtool

$ gnutls-cli example.com -p 443 --print-cert < /dev/null | certtool -i | grep -C3 -i dns

With openssl

Taken from https://stackoverflow.com/a/13128918/1695680

$ openssl s_client -connect example.com:443 < /dev/null | openssl x509 -noout -text | grep -C3 -i dns

Extracting Certificate Data

| grep -C3 -i dns works for a simple-case, if your reviewing this data by hand sure works well enough. However certificate data is hierarchical, not line-oriented (so greping will be messy, particularly for ca chains).

I don't know of any x509 command line tools that can do key-value extraction, most systems I work with have python on-box or nearby so here is an approach using python, x509 interface provided by cryptography on pypi. Using cryptography is a little verbose, I didn't feel comfortable condensing this into a oneliner, but with this script you can extract dns names from certificates passed to stdin

#!/usr/bin/env python3

import sys

import cryptography.x509
import cryptography.hazmat.backends
import cryptography.hazmat.primitives

DEFAULT_FINGERPRINT_HASH = cryptography.hazmat.primitives.hashes.SHA256


def _x509_san_dns_names(certificate):
    """ Return a list of strings containing san dns names
    """
    crt_san_data = certificate.extensions.get_extension_for_oid(
        cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME
    )

    dns_names = crt_san_data.value.get_values_for_type(
        cryptography.x509.DNSName
    )

    return dns_names


def _find_certificate_pem(stream):
    """ Yield hunks of pem certificates
    """
    certificate_pem = []
    begin_certificate = False
    for line in stream:
        if line == b'-----END CERTIFICATE-----\n':
            begin_certificate = False
            certificate_pem.append(line)
            yield b''.join(certificate_pem)
            certificate_pem = []

        if line == b'-----BEGIN CERTIFICATE-----\n':
            begin_certificate = True

        if begin_certificate:
            certificate_pem.append(line)


def _dump_stdincert_san_dnsnames():
    """ Print line-oriented certificate fingerprint and san dns name
    """
    for certificate_pem in _find_certificate_pem(sys.stdin.buffer):
        certificate = cryptography.x509.load_pem_x509_certificate(
            certificate_pem,
            cryptography.hazmat.backends.default_backend()
        )
        certificate_fingerprint = certificate.fingerprint(
            DEFAULT_FINGERPRINT_HASH(),
        )
        certificate_fingerprint_str = ':'.join(
            '{:02x}'.format(i) for i in certificate_fingerprint
        )
        try:
            for dns_name in _x509_san_dns_names(certificate):
                sys.stdout.write('{} {}\n'.format(certificate_fingerprint_str, dns_name))

        except cryptography.x509.extensions.ExtensionNotFound:
            sys.stderr.write('{} Certificate has no extension SubjectAlternativeName\n'.format(certificate_fingerprint_str))


def main():
    _dump_stdincert_san_dnsnames()


if __name__ == '__main__':
    main()

#### Example
$ true | openssl s_client -connect localhost:8443 | openssl x509 -noout -text | grep DNS:
depth=2 C = US, ST = NC, L = SomeCity, O = SomeCompany Security, OU = SomeOU, CN = SomeCN
verify error:num=19:self signed certificate in certificate chain
DONE
                DNS:localhost, DNS:127.0.0.1, DNS:servername1.somedom.com, DNS:servername2.somedom.local
like image 37
ThorSummoner Avatar answered Nov 06 '22 13:11

ThorSummoner


There is my solution (using openssl and sed):

bash first

sed -ne '
    s/^\( *\)[Ss]ubject[:=] */  \1/p;
    /X509v3 Subject Alternative Name/{
        N;
        s/^.*\n//;
      :a;
        s/^\( *\)\(.*\), /\1\2\n\1/;
        ta;
        p;
        q;
    }' < <(openssl x509 -in cert.pem -noout -subject -ext subjectAltName)

could be written:

sed -ne 's/^\( *\)[Ss]ubject[=:] */  \1/p;/X509v3 Subj.*Alt.*Name/{
    N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; }' < <(
    openssl x509 -in cert.pem -noout -subject -ext subjectAltName)

and could render something like:

         CN=www.example.com
                DNS:il0001.sample.com
                DNS:example.com
                DNS:demodomain.com
                DNS:testsite.com
                DNS:www.il0001.sample.com
                DNS:www.il0001.sample.com.vsite.il0001.sample.com
                DNS:www.example.com
                DNS:www.example.com.vsite.il0001.sample.com
                DNS:www.demodomain.com
                DNS:www.demodomain.com.vsite.il0001.sample.com
                DNS:www.testsite.com
                DNS:www.testsite.com.vsite.il0001.sample.com

Same for live server

sed -ne 's/^\( *\)[Ss]ubject[=:] */  \1/p;/X509v3 Subject Alternative Name/{
    N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; }' < <(
    openssl x509 -noout -subject -ext subjectAltName -in <(
        openssl s_client -ign_eof 2>/dev/null <<<$'HEAD / HTTP/1.0\r\n\r' \
            -connect google.com:443 ) )

May output:

         C=US, ST=California, L=Mountain View, O=Google Inc, CN=*.google.com
                DNS:*.google.com
                DNS:*.android.com
                DNS:*.appengine.google.com
                DNS:*.cloud.google.com
                DNS:*.gcp.gvt2.com
                DNS:*.google-analytics.com
                DNS:*.google.ca
                DNS:*.google.cl
                DNS:*.google.co.in
                DNS:*.google.co.jp
                DNS:*.google.co.uk
                DNS:*.google.com.ar
                DNS:*.google.com.au
                DNS:*.google.com.br
                DNS:*.google.com.co
                DNS:*.google.com.mx
                DNS:*.google.com.tr
                DNS:*.google.com.vn
                DNS:*.google.de
                DNS:*.google.es
                DNS:*.google.fr
                DNS:*.google.hu
                DNS:*.google.it
                DNS:*.google.nl
                DNS:*.google.pl
                DNS:*.google.pt
                DNS:*.googleadapis.com
                DNS:*.googleapis.cn
                DNS:*.googlecommerce.com
                DNS:*.googlevideo.com
                DNS:*.gstatic.cn
                DNS:*.gstatic.com
                DNS:*.gvt1.com
                DNS:*.gvt2.com
                DNS:*.metric.gstatic.com
                DNS:*.urchin.com
                DNS:*.url.google.com
                DNS:*.youtube-nocookie.com
                DNS:*.youtube.com
                DNS:*.youtubeeducation.com
                DNS:*.ytimg.com
                DNS:android.clients.google.com
                DNS:android.com
                DNS:developer.android.google.cn
                DNS:g.co
                DNS:goo.gl
                DNS:google-analytics.com
                DNS:google.com
                DNS:googlecommerce.com
                DNS:urchin.com
                DNS:www.goo.gl
                DNS:youtu.be
                DNS:youtube.com
                DNS:youtubeeducation.com

POSIX shell now

As < <(...) is a bashism, same command have to be written:

openssl x509 -in cert.pem -noout -text | sed -ne '
  s/^\( *\)Subject:/\1/p;
  /X509v3 Subject Alternative Name/{
      N;
      s/^.*\n//;
    :a;
      s/^\( *\)\(.*\), /\1\2\n\1/;
      ta;
      p;
      q;
  }'

and

printf 'HEAD / HTTP/1.0\r\n\r\n' |
    openssl s_client -ign_eof 2>/dev/null -connect google.com:443 |
    openssl x509 -noout -subject -ext subjectAltName |
    sed -ne 's/^\( *\)[Ss]ubject[=:] */  \1/p;/X509v3 Subj.*Alt.*Name/{
        N;s/^.*\n//;:a;s/^\( *\)\(.*\), /\1\2\n\1/;ta;p;q; }'

Full bash script on another answer

Have a look at bottom of How to determine SSL cert expiration date from a PEM encoded certificate?!!

like image 14
F. Hauri Avatar answered Nov 06 '22 11:11

F. Hauri