DEV Community

Miro
Miro

Posted on

Quickly create a self signed certificate for a custom domain

I often find myself needing a simple self signed certificate that is either for a wildcard domain or that cover multiple domain names. And then, even when I have openssl somewhere the questions are "do I have the config file?", "do I really need to fill in all those distinguished name values?", "where do I put additional domains", etc.

So to make my life simpler I made a small batch script that takes domain names as arguments, creates configuration file and instructs openssl to create a self signed certificate for those domains.

Example usage

Requesting certificates like:

E:\Temp> self-signed-cert mydomain.local *.mydomain.local

will generate one certificate with 2 domain names (mydomain.local and *.mydomain.local) and will produce the following files:

  1. mydomain.local.conf - configuration file for openssl
  2. mydomain.local.key - private key
  3. mydomain.local.crt - certificate
  4. mydomain.local.pfx - a pkcs#12 file with both certificate and private key

Content of the generated mydomain.local.conf file:

[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = req_distinguished_name
x509_extensions = v3_req

[req_distinguished_name]
CN = mydomain.local

[v3_req]
subjectAltName = @san

[san]
DNS.1 = mydomain.local
DNS.2 = *.mydomain.local

And the generated certificate looks like this:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            fc:55:a1:88:a5:68:67:0c
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = mydomain.local
        Validity
            Not Before: Apr 13 12:12:57 2020 GMT
            Not After : Apr 11 12:12:57 2030 GMT
        Subject: CN = mydomain.local
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (4096 bit)
                Modulus:
                    00:cb:d9:5c:bb:df:23:1e:69:5d:b2:45:06:d8:1f:
                    d9:d1:4a:37:07:ab:c7:86:77:e0:2f:88:36:dc:f4:
                                      <...>
                    7d:70:b6:23:aa:9a:bc:57:83:c9:f8:13:3f:9f:b4:
                    01:54:71
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:mydomain.local, DNS:*.mydomain.local
    Signature Algorithm: sha256WithRSAEncryption
         b0:7b:4c:2c:f6:5e:40:2b:9d:9d:71:b1:d3:f8:0b:44:84:50:
         d1:ea:b0:91:fb:61:7d:d8:f8:d9:ca:5d:c8:b2:bf:d4:5d:75:
                               <...>
         5c:73:66:d1:52:10:11:2b:68:85:da:8a:16:3d:82:4f:0c:c5:
         50:a5:85:81:4f:06:27:33

Batch file explained

if [%1]==[] (
  echo Usage: %0 ^<domain_name^> [additional_domain] [additional_domain] ...
  exit /b 1
)

Just making sure that there is at least one argument

if [%2]==[] (set addsan=) else (set addsan=1)

Checking if it is a single domain name or multiple domain name

set fn=%~1
if [%fn:~0,2%] == [*.] set fn=_wildcard.%fn:~2%

Setting a file name for configuration and certificates. If the (first) domain name in the list is a wildcard domain, replace the asterisk character with "wildcard" word. Otherwise just take the domain name. So requesting a *.mydomain.local certificates will generate wildcard.mydomain.local files, while requesting web1.mydomain.local certificate will generate web1.mydomain.local files.

%~1 - removing surrounding quotes around first argument. E.g. if the first argument is "something with spaces", the %~1 will remove the quotes.
%fn:~0,2% - substring, first two characters of the fn variable
%fn:~2% - substring, everything after 2nd character

echo [req]>%fn%.conf
echo default_bits = 2048>>%fn%.conf
echo prompt = no>>%fn%.conf
echo default_md = sha256>>%fn%.conf
echo distinguished_name = req_distinguished_name>>%fn%.conf
if [%addsan%] == [1] echo x509_extensions = v3_req>>%fn%.conf
echo.>>%fn%.conf
echo [req_distinguished_name]>>%fn%.conf
echo CN = %~1>>%fn%.conf
echo.>>%fn%.conf

Making the openssl configuration, named %fn%.conf.

if not [%addsan%] == [1] goto :makecert

Skip additional configuration if there is only one domain

echo [v3_req]>>%fn%.conf
echo subjectAltName = @san>>%fn%.conf
echo.>>%fn%.conf
echo [san]>>%fn%.conf
set /a sanid = 0

Additional configuration sections for multiple domain names.

:sanloop
set /a sanid+=1
echo DNS.%sanid% = %~1>>%fn%.conf
shift
if [%~1]==[] goto :makecert
goto :sanloop

Looping through the arguments.
set /a sanid+=1 - increment counter so that each DNS entry has it's own number
shift - shift arguments so that %2 becomes %1, %3 becomes %2, etc
if [%~1]==[] goto :makecert - once there are no more arguments, break the loop

:makecert
openssl req -new -x509 -nodes -days 3650 -sha256 -newkey rsa:4096 -keyout %fn%.key -out %fn%.crt -config %fn%.conf

openssl pkcs12 -export -out %fn%.pfx -inkey %fn%.key -in %fn%.crt -name %friendly% -passout pass:

Finally, the openssl call. Frist create a new self signed certificate which is valid for 10 years, and then export it to pkcs#12 pfx file with an empty password.

Make sure that openssl is somewhere in PATH.

self-signed-cert.cmd

Discussion (0)