258

Is it possible to provide a subjectAltName-Extension to the openssl req module directly on the command line?

I know it's possible via a openssl.cnf file, but that's not really elegant for batch-creation of CSRs.

Michael Seiwald
  • 2,713
  • 2
  • 11
  • 7
  • I just developed a web based tool that will generate this command automatically based on form input and display the output. http://kernelmanic.com/certificate-request-generator-with-multiple-common-names-and-subject-alternative-names/ – Lyas Spiehler Oct 27 '15 at 17:29
  • It isn't clear from what you have included how the output of your tool applies to the Question. Also, can you explain how your tool works (in case the link goes dead)? – schroeder Oct 27 '15 at 18:18
  • Peter(editor): 'OpenSSL' is the name of the _project_ and its output as a whole, but 'openssl' all-lower is the name of the command-line 'utility' program relevant to this Q. – dave_thompson_085 Dec 29 '18 at 12:28
  • As of 2019 this answer should be the accepted one: https://security.stackexchange.com/a/183973/143034 – wedi Jun 19 '20 at 17:14

17 Answers17

174

As of OpenSSL 1.1.1, providing subjectAltName directly on command line becomes much easier, with the introduction of the -addext flag to openssl req (via this commit).

The commit adds an example to the openssl req man page:

Example of giving the most common attributes (subject and extensions)
on the command line:

 openssl req -new -subj "/C=GB/CN=foo" \
                  -addext "subjectAltName = DNS:foo.co.uk" \
                  -addext "certificatePolicies = 1.2.3.4" \
                  -newkey rsa:2048 -keyout key.pem -out req.pem

This has been merged into the master branch of the openssl command on Github, and as of April 18 2018 can be installed via a git pull + compile (or via Homebrew if on OS X: brew install --devel openssl@1.1).

Note that if you have set the config attribute "req_extensions" at section "[req]" in openssl.cfg, it will ignore the command-line parameter

MrGuga
  • 3
  • 2
Peter W
  • 1,856
  • 1
  • 7
  • 4
  • 16
    The `-addext` is convenient for creating signing requests, but the SAN still has to be added when the csr is signed, correct? Openssl doesn't have the equivalent flag on the `x509` command, necessitating the use of a file. – end-user Feb 21 '19 at 18:52
  • 2
    @end-user: if you issue the cert (which is _not_ signing the CSR) with `openssl x509 -req -CA/CAkey` yes. If you isse with `openssl ca` it can be configured with `copy_extensions` to put the extensions from the CSR in the cert. – dave_thompson_085 Oct 11 '19 at 00:53
  • 1
    (necroed elsewhere) if you specify `-addext` and also have req_extensions in config, it **doesn't ignore** either; instead it creates _TWO_ attribute items with OID pkcs9.14, which clearly doesn't make sense, though AFAICS rfc2986 doesn't prohibit it (as rfc5280 _does_ for extensions in certs). `openssl req -text` only _displays_ the first, and since attributes is a SET and the tbs is DER-encoded, which set of extensions is first is determined in an arcane way that might as well be random. Other things using the CSR, like `openssl ca` with `copy_extensions`, are also likely to be confused. – dave_thompson_085 Sep 18 '20 at 01:25
151

Based on link from DarkLighting, here's the command I came up with using nested subshells.

openssl req -new -sha256 \
    -key domain.key \
    -subj "/C=US/ST=CA/O=Acme, Inc./CN=example.com" \
    -reqexts SAN \
    -config <(cat /etc/ssl/openssl.cnf \
        <(printf "\n[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) \
    -out domain.csr

All one line:

openssl req -new -sha256 -key domain.key -subj "/C=US/ST=CA/O=Acme, Inc./CN=example.com" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) -out domain.csr

Example use:

user@hostname:~$ openssl req -new -sha256 -key domain.key -subj "/C=US/ST=CA/O=Acme, Inc./CN=example.com" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com\n")) -out domain.csr
user@hostname:~$ openssl req -in domain.csr -text -noout
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=US, ST=CA, O=Acme, Inc., CN=example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:a8:05:50:86:49:98:c8:05:01:e9:50:18:7f:2f:
                    b4:89:09:29:d1:c1:58:d8:14:bb:58:1d:25:50:11:
                    bb:43:d8:28:03:a5:de:59:49:bb:d2:f7:d3:79:5c:
                    c6:99:2c:98:ff:99:23:8c:df:96:7c:ea:4b:62:2a:
                    a4:c2:84:f5:5d:62:7f:7d:c4:7c:e2:c3:db:e6:58:
                    03:c2:26:9d:02:da:bb:84:d9:11:82:fe:38:12:9b:
                    c7:b6:ff:b2:40:30:38:b1:44:d8:47:1d:43:4a:29:
                    58:6b:49:ec:33:d7:dc:a7:1b:90:05:3a:f5:e6:16:
                    98:08:5d:2d:7e:b4:ea:a2:a4:b1:84:89:f7:f1:c4:
                    67:a6:a1:06:70:dd:4e:6b:0c:f8:b5:9b:bc:3f:06:
                    ee:90:d6:86:29:52:d3:af:f6:d4:2f:c6:cf:4b:5a:
                    b8:cd:01:74:6d:5c:25:a8:02:1c:7c:e8:66:3d:46:
                    07:b1:9d:ef:cc:eb:90:b6:bf:7b:33:e0:5f:b2:9b:
                    e8:b4:12:67:2f:8d:0d:9b:54:9d:95:6e:09:83:cb:
                    f3:5b:1f:31:8e:3b:ca:4e:08:e0:40:c0:60:40:72:
                    dd:0d:3e:99:ec:7c:ac:c4:3c:ba:85:9d:d9:d9:6b:
                    02:2e:bf:a8:a3:02:1d:eb:c8:58:e3:04:b3:a5:f1:
                    67:37
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name: 
                DNS:example.com, DNS:www.example.com
    Signature Algorithm: sha256WithRSAEncryption
         a2:1d:1a:e8:56:43:e7:e5:c7:c1:04:c1:6a:eb:d5:70:92:78:
         06:c1:96:fa:60:e2:5f:3c:95:ee:75:ed:70:52:c1:f0:a7:54:
         d2:9f:4a:2f:52:0f:d4:27:d8:13:73:1f:21:be:34:3f:0a:9c:
         f1:2a:5c:98:d4:28:b8:9c:78:44:e8:ea:70:f3:11:6b:26:c3:
         d6:29:b3:25:a0:81:ea:a2:55:31:f2:63:c8:60:6d:68:e3:ab:
         24:c9:46:33:92:8f:f2:a7:72:43:c6:aa:bd:8d:e9:6f:64:64:
         9e:fe:30:48:3f:06:2e:58:7c:b5:ef:b1:4d:c3:84:cc:02:a5:
         58:c3:3f:d8:ed:98:c7:54:b9:5e:50:44:5e:be:99:c2:e4:03:
         81:4b:1f:47:9a:b0:4d:74:7b:10:29:2f:84:fd:d1:70:88:2e:
         ea:f3:42:b7:06:94:4a:06:f6:92:10:4c:ce:de:65:89:2d:0a:
         f1:0f:79:90:02:a4:b9:6d:b8:39:db:de:6e:34:61:4f:21:36:
         a0:b5:73:2b:2b:c6:7e:2f:f2:e5:1e:51:9f:85:c8:17:9c:1a:
         b6:59:b0:41:a7:06:c8:5b:f4:88:92:c9:34:71:9d:73:f0:2e:
         31:ae:ed:ab:35:0e:b4:8a:9a:72:7c:6f:7a:3e:5d:66:49:26:
         26:99:e1:69
user749618
  • 1,611
  • 1
  • 11
  • 2
  • 3
    If your config is missing a `[ SAN ]` section, the `-reqexts SAN` section will result in the error message 'Error Loading request extension section SAN'. Incase anybody else runs into that. – ThorSummoner Sep 04 '16 at 06:37
  • 15
    I also had to set `-extensions SAN` to get this to work. Full 1-liner: `openssl req -new -sha256 -key domain.key -subj "/C=US/ST=CA/O=Acme, Inc./CN=example.com" -reqexts SAN -extensions SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) -out domain.csr` – Jess Telford Apr 07 '17 at 05:35
  • 1
    For a self-signed cert I needed `x509_extensions = SAN` in config file, for CSR -reqexts seems to work... – Gert van den Berg Aug 01 '17 at 11:18
  • 2
    @GertvandenBerg For self-signed (meaning `openssl req -x509`) you can specify on the command line `-extensions SAN` and it works as well. – kubanczyk Aug 26 '17 at 20:23
  • 3
    This oneliner only works in BASH (bash) not Bourne shell (sh), probably because of the sub-shell syntax. – Devy Nov 13 '18 at 18:37
  • Well, that's half the answer, but there seems to be something special you have to do when you create the cert, too. My CSR is coming out with a SAN section, but the cert is being created without it! – Throw Away Account Jan 14 '21 at 17:28
  • Bare in mind that this also creates a signing request, and not a certificate. And if signed improperly the subjectAltNames will get lost in translation. – Torxed Mar 11 '21 at 09:46
59

This is my solution to finally generate a working self signed cert, based on the answers above(The accepted answer don't work for me):

openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 365 -key ca.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=Acme Root CA" -out ca.crt

openssl req -newkey rsa:2048 -nodes -keyout server.key -subj "/C=CN/ST=GD/L=SZ/O=Acme, Inc./CN=*.example.com" -out server.csr
openssl x509 -req -extfile <(printf "subjectAltName=DNS:example.com,DNS:www.example.com") -days 365 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt

openssl x509 -in server.crt -text -noout:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            ef:ca:cb:c7:3e:5c:25:85
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=CN, ST=GD, L=SZ, O=Acme, Inc., CN=Acme Root CA
        Validity
            Not Before: May 15 14:42:17 2017 GMT
            Not After : May 15 14:42:17 2018 GMT
        Subject: C=CN, ST=GD, L=SZ, O=Acme, Inc., CN=*.example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:f0:19:32:51:9c:13:ec:dc:d4:52:30:d9:39:4a:
                    f5:9b:53:60:48:10:2d:c1:c0:48:ac:75:a3:2a:d2:
                    6c:62:f1:ed:39:46:7e:e7:e7:03:34:7a:c2:53:b7:
                    42:5a:f2:47:ff:34:68:b1:c9:28:3c:1c:eb:57:af:
                    90:87:53:85:3c:0f:6c:85:62:a1:02:94:b6:5f:3e:
                    e2:d1:bc:48:20:81:46:fe:25:b4:06:cd:b8:04:c4:
                    f5:81:f6:29:55:66:98:95:2f:db:75:39:82:7f:32:
                    5b:18:d9:9d:69:d0:f4:6b:0b:a2:92:83:b2:02:1b:
                    6c:d9:1e:f9:c4:f4:72:a6:76:e7:03:14:d6:29:2b:
                    be:e7:96:3e:42:3a:12:16:8b:51:11:22:7d:c1:d9:
                    47:ab:cd:93:36:27:d3:ad:af:85:0b:c4:d1:75:6e:
                    c1:a8:ed:f8:0f:4a:c8:79:21:4c:02:7f:27:70:00:
                    60:ed:68:8f:97:e0:0e:63:86:9f:12:07:78:aa:bf:
                    b1:bb:d1:30:ff:e6:7e:5c:cd:48:3b:31:fd:ab:54:
                    b4:af:dd:95:49:a6:17:0b:23:98:5f:3d:98:f2:eb:
                    8c:e4:aa:6e:44:2e:2d:5e:d5:91:a3:3a:61:18:3b:
                    56:29:47:86:1f:1d:d7:7c:6b:29:e7:ae:28:ec:3c:
                    e3:b1
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name:
                DNS:example.com, DNS:www.example.com
    Signature Algorithm: sha1WithRSAEncryption
        56:d2:5b:d0:6a:d9:1d:0b:d4:2d:b3:99:cf:5f:92:e6:9f:4d:
        ea:b7:22:57:0b:85:e1:f7:4b:b1:13:c1:45:f7:7c:06:34:bd:
        0c:4b:e8:45:01:84:58:8a:7a:0d:7b:08:90:a0:91:7c:f1:f7:
        ef:de:3b:94:be:44:4b:71:c5:40:6f:3c:35:3e:61:79:b1:46:
        d9:81:31:bf:11:15:6a:b2:53:b9:a3:d7:81:cd:2d:f5:3e:20:
        dc:06:1c:a0:74:16:9f:d4:53:5d:f2:3a:23:1c:43:2d:ce:8b:
        68:d3:35:f3:36:8a:05:13:34:a7:42:75:6e:df:a2:b5:95:77:
        71:99:ae:be:4a:6c:ae:14:b4:d1:e4:f7:b4:39:b0:30:04:57:
        8a:d8:21:c5:1c:50:f3:86:38:ec:eb:0c:a6:f6:94:f3:f4:af:
        ec:1b:d1:79:ad:16:45:bc:c9:10:2a:a8:2d:b8:cf:7d:8a:aa:
        b4:b5:74:e0:d4:53:82:b5:71:b8:bb:2f:d2:12:51:87:ab:f1:
        b6:dd:1c:24:b1:8b:36:05:83:29:ca:58:ba:6b:f0:83:cc:27:
        86:43:00:da:73:a0:d5:36:31:bb:e7:e5:1b:2f:c0:42:55:7b:
        b4:2e:57:4f:88:b4:cd:0d:d0:bf:a8:87:76:a1:1b:bc:e4:fc:
        31:ba:ee:04

Repro step for "The accepted answer don't work for me" (On OSX 10.12.4, with system openssl):

bash-3.2$ openssl genrsa -out domain.key 2048
Generating RSA private key, 2048 bit long modulus
.........................................................................................+++
....................................+++
e is 65537 (0x10001)
bash-3.2$ openssl req -new -sha256 -key domain.key -subj "/C=US/ST=CA/O=Acme, Inc./CN=example.com" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:example.com,DNS:www.example.com")) -out domain.csr
bash-3.2$ openssl req -in domain.csr -text -noout
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: C=US, ST=CA, O=Acme, Inc., CN=example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:cd:a5:97:b2:1a:83:c6:1d:0e:78:1a:6f:ca:4c:
                    e6:e3:64:94:41:b8:fb:f3:4a:4c:56:8c:33:36:c1:
                    5d:10:25:f5:86:f5:14:c6:17:22:53:34:7b:16:52:
                    ea:f2:ac:bf:0d:09:7d:55:c8:16:ce:0e:f9:98:20:
                    aa:11:4e:bb:4d:75:b1:ed:1b:ca:37:82:f1:15:71:
                    56:ad:c0:be:40:b4:ef:f2:e6:a5:a2:3b:e3:a8:0c:
                    8b:38:3d:d5:41:1a:e8:92:f6:78:52:9f:35:c2:98:
                    a6:58:87:64:e6:d3:7e:a0:00:8c:d0:16:13:80:e9:
                    ee:81:aa:40:c7:1d:9d:fc:52:9a:50:7d:50:e6:ca:
                    20:38:89:12:7d:99:a0:68:ae:45:64:03:e0:00:3c:
                    30:b7:94:87:ab:de:51:90:73:6b:bc:48:c4:e8:47:
                    2d:0e:5a:d0:fb:b4:1b:cb:76:7b:05:70:1a:a8:03:
                    bc:35:38:70:b5:ca:07:43:d3:9d:66:8c:32:32:74:
                    7e:6f:61:e8:de:80:de:d9:fd:fc:27:d8:bb:fa:8c:
                    f9:94:42:c4:b8:e0:bb:24:8b:1f:71:5b:18:99:ca:
                    ac:42:3b:ed:d7:4d:5f:dc:79:8c:6c:fe:d1:df:44:
                    05:5f:1a:a7:bd:e8:1c:85:0c:70:fb:4e:29:62:a0:
                    e9:71
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:example.com, DNS:www.example.com
    Signature Algorithm: sha256WithRSAEncryption
        47:f3:82:ae:78:f2:19:76:05:e3:97:30:00:16:c5:9c:89:94:
        ef:b0:51:b0:cf:4a:93:81:7d:ee:94:25:9a:0a:9e:1f:7f:e0:
        d8:72:55:75:2d:ac:c3:f9:3a:74:b6:1f:1b:c3:f1:68:d4:66:
        72:89:ed:53:7b:09:da:35:eb:40:63:e6:6a:0f:9a:4f:6e:25:
        9f:63:df:bb:d6:00:77:c2:e7:d6:96:0c:50:58:01:c9:d1:ff:
        df:de:fb:19:fb:72:38:48:25:5d:b7:56:fb:eb:d7:41:f5:f6:
        d7:f7:4b:c7:07:4f:59:b4:b8:c3:d8:bf:c9:2c:07:5a:c3:0a:
        51:f8:02:4f:dc:de:2d:88:49:b7:6d:de:67:04:d0:78:6e:0f:
        96:d8:06:e4:73:4f:fb:ce:29:0f:1e:3a:1a:6e:3c:a5:f3:f1:
        68:3d:22:85:34:fa:f0:ad:f6:75:61:02:81:f1:c4:e3:69:2b:
        80:3d:05:39:c6:9d:72:66:2a:50:93:6c:79:5d:d0:33:42:cf:
        a6:68:6a:16:d7:dc:61:b4:c3:4e:01:ac:68:7c:77:29:d4:fe:
        0d:9d:34:0a:3e:73:02:27:12:a4:08:9c:b9:2e:3e:c8:3f:1d:
        91:33:3b:71:8f:24:6b:66:f5:c3:8a:d7:7b:fe:2d:7f:b4:6d:
        96:cf:52:74
bash-3.2$ openssl x509 -req -in domain.csr -signkey domain.key -out domain.crt
Signature ok
subject=/C=US/ST=CA/O=Acme, Inc./CN=example.com
Getting Private key
bash-3.2$ openssl x509 -in domain.crt -text -noout
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            de:c5:cf:28:1f:33:6c:53
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, ST=CA, O=Acme, Inc., CN=example.com
        Validity
            Not Before: May 15 15:30:07 2017 GMT
            Not After : Jun 14 15:30:07 2017 GMT
        Subject: C=US, ST=CA, O=Acme, Inc., CN=example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
            RSA Public Key: (2048 bit)
                Modulus (2048 bit):
                    00:cd:a5:97:b2:1a:83:c6:1d:0e:78:1a:6f:ca:4c:
                    e6:e3:64:94:41:b8:fb:f3:4a:4c:56:8c:33:36:c1:
                    5d:10:25:f5:86:f5:14:c6:17:22:53:34:7b:16:52:
                    ea:f2:ac:bf:0d:09:7d:55:c8:16:ce:0e:f9:98:20:
                    aa:11:4e:bb:4d:75:b1:ed:1b:ca:37:82:f1:15:71:
                    56:ad:c0:be:40:b4:ef:f2:e6:a5:a2:3b:e3:a8:0c:
                    8b:38:3d:d5:41:1a:e8:92:f6:78:52:9f:35:c2:98:
                    a6:58:87:64:e6:d3:7e:a0:00:8c:d0:16:13:80:e9:
                    ee:81:aa:40:c7:1d:9d:fc:52:9a:50:7d:50:e6:ca:
                    20:38:89:12:7d:99:a0:68:ae:45:64:03:e0:00:3c:
                    30:b7:94:87:ab:de:51:90:73:6b:bc:48:c4:e8:47:
                    2d:0e:5a:d0:fb:b4:1b:cb:76:7b:05:70:1a:a8:03:
                    bc:35:38:70:b5:ca:07:43:d3:9d:66:8c:32:32:74:
                    7e:6f:61:e8:de:80:de:d9:fd:fc:27:d8:bb:fa:8c:
                    f9:94:42:c4:b8:e0:bb:24:8b:1f:71:5b:18:99:ca:
                    ac:42:3b:ed:d7:4d:5f:dc:79:8c:6c:fe:d1:df:44:
                    05:5f:1a:a7:bd:e8:1c:85:0c:70:fb:4e:29:62:a0:
                    e9:71
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha1WithRSAEncryption
        02:71:7f:a5:8e:aa:7d:4b:0a:9d:54:8c:25:cb:b3:66:a3:22:
        c5:61:73:0c:c4:da:3b:ce:e8:4b:ec:ee:45:83:ca:db:e0:25:
        9b:a6:a3:c0:c9:7c:d9:76:a2:8c:38:38:b1:77:c7:84:33:03:
        b7:9a:cb:ff:bf:83:bc:7b:d8:4c:7e:c4:b3:8f:c5:23:22:75:
        67:d3:d6:5e:0e:bd:ef:0b:0f:6a:8d:f0:d3:20:8f:5a:cf:37:
        94:b7:8a:d9:b3:0e:99:31:4f:77:6f:89:33:c5:93:99:2e:8b:
        61:ad:84:17:af:b5:8e:1e:f0:4a:af:b1:90:c3:09:3a:d6:16:
        4b:1b:c4:6b:2e:22:7e:b1:7d:9b:3c:a9:3b:06:20:e2:37:14:
        8b:0d:da:c6:4b:e3:6e:83:9c:df:20:67:2e:d0:33:68:05:17:
        01:d5:5a:6f:51:b3:50:d7:73:10:73:c8:be:3b:de:e6:bd:28:
        60:6f:19:75:0c:05:16:37:4d:50:df:f4:bb:41:f0:65:ba:6f:
        7f:5c:56:27:ae:0e:18:0a:df:7e:d2:7b:93:db:40:d2:bb:e0:
        dc:b8:57:c7:08:07:37:e4:db:d4:09:b6:13:d7:22:e2:ef:6d:
        60:fa:3e:7c:f4:1f:0b:bf:26:f4:08:d0:39:cf:51:dd:bf:b1:
        0e:ee:46:d1
bash-3.2$ openssl version
OpenSSL 0.9.8zh 14 Jan 2016
tsl0922
  • 691
  • 5
  • 3
  • 3
    Why is it your solution? Can you talk us through it? – schroeder May 15 '17 at 15:09
  • 3
    How is this different from the accepted answer? – schroeder May 15 '17 at 15:10
  • Hi, I've edited my answer, I posted this because it works for me. The accepted answer doesn't work for me with `openssl x509 -req -in server.csr`, am I missing something? – tsl0922 May 15 '17 at 15:23
  • 1
    Ok, but *why* does it work for you? What part replaces the accepted answer and makes it work? Right now, it's just a wall of code. – schroeder May 15 '17 at 15:25
  • Don't know why, I've added the reproduce step to the answer. The change is: I moved the `subjectAltName ` param from `openssl req` to `openssl x509` command, then it works. – tsl0922 May 15 '17 at 15:39
  • 5
    The question was about creating a CSR (presumably for submission to a real CA) and the accepted answer does that. Your answer creates a self-signed cert with SAN but not a CSR with SAN, so it doesn't answer the question here although it does answer https://security.stackexchange.com/questions/150078/missing-x509-extensions-with-an-openssl-generated-certificate – dave_thompson_085 May 16 '17 at 05:27
  • 1
    dave_thompson_085 is right, but having this answer here clearly is useful for other people, me included. The question is about CSRs, but the title does not specify that. What I propose is that you copy or link to your answer from the question he mentioned, "Missing X509 extensions with an openssl-generated certificate" https://security.stackexchange.com/questions/150078/missing-x509-extensions-with-an-openssl-generated-certificate – Alberto González Palomo Nov 17 '17 at 15:10
  • 4
    The main difference in this answer compared to the accepted one (which only deals with how to generate a CSR with subjectAltName) is that in this answer two certificates are generated. A Root CA certificate and a domain certificate. The Root CA acts as the issuer of the domain certificate. – marekful Dec 03 '18 at 15:53
  • also in this answer it is shown how to sign a csr so the result certificate contains SAN – ciekawy Jan 23 '19 at 11:50
  • 1
    and this answer does not read the entire openssl config file \o/ – Thomas Dignan Aug 18 '19 at 23:32
  • worked for me, ca openssl < 1.1.1 – Zayne S Halsall Dec 14 '20 at 11:20
  • Works for me on openssl 1.1.1 (cygwin) for self-signed certificate. – Albert Zhong Feb 25 '21 at 07:37
49

My solution was to pass subjectAltName via an environment variable.

First have this added to openssl.conf:

[ san_env ]
subjectAltName=${ENV::SAN}

Then set the environment variable before invoking openssl:

export SAN=DNS:value1,DNS:value2
openssl req -extensions san_env -subj '/CN=value1' ...

Note: the -extensions san_env parameter needs to be present when signing the CSR as well as when generating it. Therefore, for CA-signed CSRs add -extensions san_env to the openssl ca command as well.

rustyx
  • 751
  • 6
  • 10
  • 1
    I get this error message `Error Loading extension section san_env` – Soichi Hayashi Dec 22 '16 at 14:05
  • 1
    That's probably because the `SAN` environment variable was not set or was empty. – rustyx Dec 22 '16 at 15:21
  • I used a combination of the accepted answer and this one to script it all from the commandline. – Todd Lyons Sep 14 '17 at 14:54
  • It helped me much....I used to BATCH on Windows OS – Abbas Jul 06 '19 at 09:48
  • 1
    Good .conf syntax to know! Slight enhancement: avoid polluting the current bash environment with `export` via `SAN=DNS:value1,DNS:value2 openssl req ...` – Anton Tykhyy Oct 23 '19 at 21:24
  • I don't think this works on macOS. I tried it with the export and with the variable inlined like Anton suggests. I end up with `4696151660:error:0EFFF068:configuration file routines:CRYPTO_internal:variable has no value:/BuildRoot/Library/Caches/com.apple.xbs/Sources/libressl/libressl-22.260.1/libressl-2.6/crypto/conf/conf_def.c:563:line 122` – bmauter May 15 '20 at 19:06
21

As of 2022, with OpenSSL ≥ 1.1.1, the following command demonstrates how to generate a self-signed certificate with SAN for example.com and example.net:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout example.key -out example.crt -subj '/CN=example.com' \
  -addext 'subjectAltName=DNS:example.com,DNS:example.net'

Here we are using the -addext option.

If you are stuck to OpenSSL ≤ 1.1.0, e.g. on Debian ≤ 9 or CentOS ≤ 7, you can apply instead a tiny hack via -extensions and -config. The following command is portable in the sense that we don't have to mess around with (or even know about) the location of the openssl.cnf file:

openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes \
  -keyout example.key -out example.crt -subj '/CN=example.com' \
  -extensions san \
  -config <(echo '[req]'; echo 'distinguished_name=req';
            echo '[san]'; echo 'subjectAltName=DNS:example.com,DNS:example.net')

The trick here is to include a minimal [req] section that is good enough for OpenSSL to get along without its main openssl.cnf file.

Either way, don't forget to verify the contents of the generated certificate:

openssl x509 -noout -text -in example.crt

See also: https://stackoverflow.com/a/41366949/19163 and https://unix.stackexchange.com/a/333325/20407

vog
  • 341
  • 2
  • 6
  • 2
    Well done, thanks for your multiple useful answers re: OpenSSL. This is really the only updated answer on this page in particular. – Jesse Nickles May 24 '21 at 08:46
  • What is `-addext`? - my openssl (v1.1) doesn't know this option and it's not listed on the [man page](https://www.openssl.org/docs/manmaster/man1/openssl.html)... – Marc Sep 22 '21 at 18:35
  • 1
    @Marc Please have a look at the OpenSSL manual of OpenSSL version 1.1.1, which clearly explains the `-addext` option: https://www.openssl.org/docs/man1.1.1/man1/openssl-req.html – vog Sep 23 '21 at 21:32
12

So I had a heck of a time getting this working right, and putting at all in Ansible. As Ansible's command module doesn't allow file-redirects (<(...)), I had to use a small .cnf file as a template, but it's all working now. Here's what I did to make it work:

The san.cnf template (generated for each CSR/CRT pair):

[req]
distinguished_name = req_distinguished_name
req_extensions     = v3_req
x509_extensions    = v3_req

[req_distinguished_name]
commonName       = {{ common_name }}
emailAddress     = {{ ssl_certs_email }}
organizationName = {{ ssl_certs_organization }}
localityName     = {{ ssl_certs_locality }}
countryName      = {{ ssl_certs_country }}

[v3_req]
# The extentions to add to a self-signed cert
subjectKeyIdentifier = hash
basicConstraints     = critical,CA:false
subjectAltName       = DNS:{{ common_name }}
keyUsage             = critical,digitalSignature,keyEncipherment

Some Variables

These Ansible variables used in the following commands, but you can substitute as needed in your scripts:

ssl_certs_fields: "/C={{ssl_certs_country}}/ST={{ssl_certs_state}}/L={{ssl_certs_locality}}/O={{ssl_certs_organization}}/CN={{common_name}}/emailAddress={{ssl_certs_email}}"
ssl_certs_local_privkey_path:       The path to the Private Key
ssl_certs_local_csr_path:           The path to the CSR
ssl_certs_local_path:               The local dir for this PKI file set
ssl_certs_local_decrypt_cakey_path: A temporarily decrypted copy of the CA 

key ssl_certs_local_caserial_path: The CA's serial numbering file ssl_certs_local_cert_path: The final generated certificate file.

The CSR Generation Command

openssl req -new -sha256 -subj "{{ ssl_certs_fields }}" 
-key "{{ ssl_certs_local_privkey_path }}"
-out "{{ ssl_certs_local_csr_path }}" 
-config "{{ssl_certs_local_path}}/san.cnf"

Self-Signing the CSR to create the Certificate

  openssl x509 -req -days {{ ssl_certs_days }}
  -sha256
  -extfile "{{ssl_certs_local_path}}/san.cnf"
  -extensions v3_req
  -in "{{ ssl_certs_local_csr_path }}"
  -CA "{{ ssl_certs_local_ca_path }}"
  -CAkey "{{ ssl_certs_local_decrypt_cakey_path }}"
  -CAcreateserial
  -CAserial "{{ ssl_certs_local_caserial_path }}"
  -out "{{ ssl_certs_local_cert_path }}"

To Verify the Result

openssl x509 -noout -text -in {{ ssl_certs_local_cert_path }}

That should include a section that appears as follows:

        X509v3 extensions:
        X509v3 Subject Key Identifier:
            3B:6E:E9:9F:B2:30:08:21:1C:C7:0D:4C:21:7A:B4:92:40:B6:71:98
        X509v3 Basic Constraints: critical
            CA:FALSE
        X509v3 Subject Alternative Name:
            DNS:foo.bar.com
Excalibur
  • 221
  • 2
  • 4
  • For complex stuff like this, the shell Ansible module should be used instead of command. It is not really elegant either way - but thank you for your template workaround, this certainly works too. – AdamKalisz May 22 '20 at 19:47
  • I have provided a solution using the Ansible shell module down bellow. If only it was easier to generate CSRs in a sane way (or had openssl at least have a better user interface)... – AdamKalisz Jul 06 '20 at 20:40
8

The 2nd post in this link says that it not possible to do that only from command line, but the 4th post in the same link provides a workaround using bash's ability of referencing data as if it was in a file.

Taking a further look into it, someone mentioned the reqexts parameter used to make additions to certificate request. This blog uses bash's env as an approach to this.

But i'm just trying to help. Haven't tested any of this myself.

DarkLighting
  • 1,523
  • 11
  • 16
  • Thanks. I went with the solution using bash's command substitution ability. This way everything is contained in a single script. – Michael Seiwald Dec 09 '14 at 10:18
3

Tested for RHEL7 (creating a self-signed certificate with a SAN)

openssl req -x509 -nodes -newkey rsa:2048 -days 3650 -sha256 -keyout test.key -out test.cert -reqexts SAN -extensions SAN -subj '/CN=test.example.com' -config <(cat /etc/pki/tls/openssl.cnf; printf "[SAN]\nsubjectAltName=DNS:test.example.com,DNS:test2.example.com")
Cameron Kerr
  • 131
  • 2
3

I wanted a one line command to create a CSR - worked perfectly with no conf files, but didn't generate a SubjAltName entry. This version is what I was using Using read -p to request FQDN I wanted this to work with a SAN entry as well - so here's a working solution.

There is a dependency on the version of openssl, needs to be at least 1.1.1. because you need -addext.

read -p "FQDN ?" CN; openssl req -new -key yourkeyfile.key -subj /C=GB/ST=county/L=city/O=company/OU=yourorg/CN=$CN -addext "subjectAltName = DNS:$CN" -out./$CN.csr

No messing with conf files this way.

Roger W
  • 31
  • 1
2

I needed to do this for creating self-signed certs for local testing, but also wanted to be able to pass multiple parameters for extensions, not just SAN. I discovered that doing multiple -extfile commands, just seemed to overwrite each other, and only the last -extfile value ended up in cert.

The solution was just to add more variables to the printf:

openssl x509 -req -sha256 \
    -extfile <(printf "extendedKeyUsage=serverAuth\nsubjectAltName=DNS:example.com") \
    -days 820 -in server.csr -signkey key.pem -out cert.pem

That works fine, but our workflow was already generated certs by storing the command in a package.json file, and then running npm run newcert. Attempting to add \n to the printf just broke the command. The solution for this was to switch to using a lot of echos, along with explicitly defining an extension name.

  • Note: For running these as an npm script, you'll have to escape the double quotes, and line continuations can't be used.
openssl req -newkey rsa:2048 -sha256 -nodes -keyout key.pem \
    -subj "/C=CN/ST=GD/L=SZ/O=Example/CN=example.com" -out server.csr

openssl x509 -req -sha256 -extensions v3_ca \
    -extfile <(echo "[v3_ca]"; echo "extendedKeyUsage=serverAuth"; echo "subjectAltName=DNS:example.com") \
    -days 820 -in server.csr -signkey key.pem -out cert.pem

Running openssl x509 -noout -text -in cert.pem shows it worked:

     X509v3 extensions:
            X509v3 Extended Key Usage:
                TLS Web Server Authentication
            X509v3 Subject Alternative Name:
                DNS:example.com
Mordred
  • 183
  • 8
2

My solution to this problem was to create and reference a temporary cnf file by appending my command-line-collected subjectAltName information.

Nick2253
  • 171
  • 4
1

The question has been answered, but I still struggled with getting this into an elegant and useful form to automate CSR generation. The one liner is nice so I incorporated it into a routine that allows the subject alternative names as command arguments rather than values in a file also the flexibility to SAN or not to SAN. Try it with one argument then with many.

#!/bin/bash

#san_cert.sh

# defaults =====================================================================
DOM=domain.com
O=My\ Company,\ LLC
L=Seattle
ST=Washington
C=US
OU=Operations
EMAIL=certalert

#basic checks and strings ======================================================
if [ -z "$1" ];then
    echo usage: $0 name1 optionalname optionalname ...
    echo example: san_cert.sh www web w3 exch mail
    exit
else
    CN=$1
    SUBJ="/C=$C/ST=$ST/L=$L/O=$O/OU=$OU/CN=$CN.$DOM/emailAddress=$EMAIL.$DOM"
fi

#clearing old files
rm $CN.$DOM.ssl_csr $CN.$DOM.ssl_key

#create private key ============================================================
openssl genrsa -out $CN.$DOM.ssl_key 2048


if [ $# -gt 1 ];then #test for arg count
    #build SAN string ==================
    A=($@)
    I=1
    while [ $I -lt ${#A[@]} ]
    do
        SAN="DNS:${A[I]}.$DOM$CMA${SAN}"
        CMA=","
        I=$[$I+1]
    done
    SAN="\n[SAN]\nsubjectAltName=${SAN}"
    #===================================

    #create SAN certificate signing request ====================================
    openssl req -new -sha256 \
    -subj "$SUBJ" \
    -key   $CN.$DOM.ssl_key \
    -out   $CN.$DOM.ssl_csr \
    -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "$SAN"))
else
    #create Single certificate signing request =================================
    openssl req -new -sha256 \
    -subj "$SUBJ" \
    -key   $CN.$DOM.ssl_key \
    -out   $CN.$DOM.ssl_csr
fi

#verification ==================================================================
openssl req -text -noout -verify -in $CN.$DOM.ssl_csr
1

This has been answered, but if anyone's still looking for a no-prompt, cli-only method to create a self-signed root cert (without CAs or CSRs) and don't mind using Java keytool, here's an alternative:

Generate a PKCS12 keystore with keytool

keytool -genkeypair \
 -keyalg RSA \
 -keysize 3072 \
 -alias titan \
 -dname "CN=titan,OU=Engineering,O=Titan Corp.,C=US" \
 -ext BC:c=ca:false \
 -ext EKU:c=serverAuth \
 -ext "SAN:c=DNS:titan,IP:192.168.1.7" \
 -validity 3650 \
 -keystore server.p12 \
 -storepass s3cr3t \
 -keypass s3cr3t \
 -storetype pkcs12

Export Certificate and Key with openssl

openssl pkcs12 -in server.p12 -nodes -out cert.pem -passin pass:s3cr3t
openssl pkcs12 -in server.p12 -nodes -nocerts -out key.pem -passin pass:s3cr3t
0

As an addition to the answer by @Excalibur (btw. thank you for your work!)

I find this form a bit more suited for Ansible. It sidesteps the problems of the official module openssl_csr that is somewhat difficult to work with due to library dependency and version problems.

The following is an adaptation of a part of the script generation by @Excalibur. You don't need to create a file. This particular playbook outputs the certificate to stdin which you can show with (ansible-playbook -vvvv <playbook.yml>) or dump to a variable and output using the debug module.

The domain.key needs to be in the same directory as the playbook.

---
- name: Test CSR generation
  hosts: localhost

  vars:
  - country: 'US'                     # C
  - state: 'NJ'                       # ST
  - locality: 'Trenton'               # L
  - organization: 'ACME'              # O
  - organization_unit: 'IT'           # OU
  - common_name: 'host.example.com'
  - email_address: 'info@example.com' # emailAddress
  - add_subj_alt_name: 'IP:192.0.2.0' # without common_name, e.g. IP:2001:db8::1

  tasks:
  - name: Generate CSR
    shell: |
      STR="/C={{ country }}/
        ST={{ state }}/
        L={{ locality }}/
        O={{ organization }}/
        OU={{ organization_unit }}/
        CN={{ common_name }}/
        emailAddress={{ email_address }}"
      openssl req -new -sha256 -key domain.key -subj "$STR" \
      -reqexts v3_req -extensions v3_req -config \
      <(cat <<<'
      [req]
      distinguished_name = req_distinguished_name
      req_extensions     = v3_req
      x509_extensions    = v3_req
      
      [req_distinguished_name]
      countryName                         = {{ country }}
      stateOrProvinceNamecountryName      = {{ state }}
      localityName                        = {{ locality }}
      organizationName                    = {{ organization }}
      organizationalUnitName              = {{ organization_unit }}
      commonName                          = {{ common_name }}
      emailAddress                        = {{ email_address }}
      
      [v3_req]
      # The extentions to add to a self-signed cert
      subjectKeyIdentifier = hash
      basicConstraints     = critical,CA:false
      subjectAltName       = DNS:{{ common_name }},{{ add_subj_alt_name }}
      keyUsage             = critical,digitalSignature,keyEncipherment') -noout -text
    args:
      executable: '/bin/bash'
AdamKalisz
  • 101
  • 2
0

extfile for IP SANs when signing CSR to CRT https://www.golinuxcloud.com/openssl-create-client-server-certificate/

openssl x509 -req -in server.csr -CA selfca.crt -CAkey selfca.key -CAcreateserial --extensions v3_req -extfile server.req -out server.crt
schroeder
  • 123,438
  • 55
  • 284
  • 319
sirkubax
  • 101
  • Why extfile? How is this answer different from all the other answers that mention extfile? If the answer is in the link, please include the relevant parts of the link in your answer here. – schroeder Jul 08 '20 at 08:54
0

Create a copy of the default openssl.cnf file and add the line below in the [req] section:

[ req ]
default_bits        = 2048
default_keyfile     = privkey.pem
distinguished_name  = req_distinguished_name
attributes      = req_attributes
**req_extensions     = req_ext**
x509_extensions = v3_ca # The extentions to add to the self signed cert

Then, add the content below under the req_extensions section:

#req_extensions = v3_req # The extensions to add to a certificate request
[ req_ext ]
subjectAltName = @alt_names
[alt_names]
DNS.1   = x.x.com
DNS.2   = x.x.com 
DNS.2   = x.x.com

Once the custom config file is created, explicitly specify this config file while creating a CSR:

req -newkey rsa:2048 -keyout test.key -out test.csr -config openssl_custom.cnf

Link to my working modified openssl config file:

https://drive.google.com/file/d/1LB26NdW13d4WggbaYwQXR7wnXd_pXed3/view?usp=sharing

schroeder
  • 123,438
  • 55
  • 284
  • 319
0

Simple answer is: you need two separate sections for requesting and signing certificates.

In signing section, REMOVE subjectAltName specification altogether.

Then it'll pass from the request.

[ x509_server ]

basicConstraints = critical, CA:FALSE
keyUsage = critical, digitalSignature, keyAgreement, dataEncipherment
extendedKeyUsage = serverAuth, emailProtection, clientAuth

subjectKeyIdentifier = ${x509_base::subjectKeyIdentifier}
#subjectAltName = ${x509_base::subjectAltName}

authorityKeyIdentifier = ${x509_base::authorityKeyIdentifier}
issuerAltName = ${x509_base::issuerAltName}

authorityInfoAccess = ${x509_base::authorityInfoAccess}
crlDistributionPoints = ${x509_base::crlDistributionPoints}


[ x509_server_req ]

basicConstraints = ${x509_server::basicConstraints}
keyUsage = ${x509_server::keyUsage}
extendedKeyUsage = ${x509_server::extendedKeyUsage}

subjectKeyIdentifier = ${x509_server::subjectKeyIdentifier}
subjectAltName = ${x509_base::subjectAltName}