[Utility Tool Series] (7) Create a certificate using OpenSSL

Article directory

  • Preface
  • 1. Introduction to OpenSSL
  • 2. Create a certificate script file
    • 1. Generate root certificate
    • 2. Generate end-user certificate
    • 3. Revoke end-user certificates
    • 4. Update revocation list (CRL)
    • 5. OpenSSL configuration file
  • Summarize

Foreword

Recently I have been working on PDF signature-related functions, which require the use of certificates for signature. So I referred to OpenSSL and other network articles and made some convenient script files to create CA certificates and terminal certificates.

1. Introduction to OpenSSL

For certificate-related concepts, you can search for relevant articles by yourself, so I won’t go into details here. The use of OpenSSL commands can be parameterized on its official website, which contains detailed instructions for each command and its parameters.

2. Create certificate script file

First install OpenSSL, you can download the installer from https://slproweb.com/download/Win64OpenSSL_Light-3_1_3.msi.
The following script assumes that the OpenSSL installation directory is: D:\opts\OpenSSL-Win64\bin, which can be adjusted according to your own situation.

1. Generate root certificate

The code is as follows (example):

@echo off

path D:\opts\OpenSSL-Win64\bin;%PATH%

setlocal enabledelayeexpansion

rem sets the root directory of the CA
set CATOP=%~dp0
set OPENSSL_CONF=?TOP%openssl.cnf

for /f "tokens=1 delims= " %%i in (ca-list) do (
    set ca_name=%%i
    set ca_dir=?TOP%!ca_name!
    set ca_crt=!ca_dir!\cacert.pem
    set ca_key=!ca_dir!\private\cakey.pem

    if not exist !ca_crt! if not exist !ca_key! (
       mkdir !ca_dir!
       mkdir !ca_dir!\private

       openssl req ^
           -new ^
           -keyout !ca_key! ^
           -out !ca_crt! ^
           -passout "pass:mycap@ss" ^
           -subj "/C=CN/ST=ShanngXi/O=Test/CN=!ca_name!" ^
           -x509 -days 1095 -extensions v3_ca
    )
)

The content of the file ca-list is as follows:

Test-Root-CA

2. Generate end-user certificate

The code is as follows (example):

@echo off

path D:\opts\OpenSSL-Win64\bin;%PATH%

setlocal enabledelayeexpansion

rem sets the root directory of the CA
set CATOP=%~dp0
set OPENSSL_CONF=?TOP%openssl.cnf


for /f "tokens=1-4 delims= " %%i in (target-list) do (
    set common_name=%%i
    set ca_name=%%j
    set ext_section=%%k
    set expire_days=%%l


    set ca_dir=?TOP%!ca_name!


    if not exist !ca_dir!\certs mkdir !ca_dir!\certs >nul
    if not exist !ca_dir!\\
ewcerts mkdir !ca_dir!\\
ewcerts >nul

    if not exist !ca_dir!\serial echo 1000 >!ca_dir!\serial
    if not exist !ca_dir!\index.txt type nul >!ca_dir!\index.txt

    set new_key=!ca_dir!\certs\!common_name!.key.pem
    set new_csr=!ca_dir!\certs\!common_name!.csr.pem
    set new_crt=!ca_dir!\certs\!common_name!.crt.pem
    set new_p12=!ca_dir!\certs\!common_name!.p12

    if not exist !new_key! (
        openssl genrsa -out !new_key! 2048
    )

    if not exist !new_csr! (
        openssl req -new -key !new_key! -out !new_csr! -subj "/title=Title1/street=Street1/initials=F.K./SN=LastName1/GN=FirstName1/name=!common_name!/emailAddress=test@necas .com/CN=!common_name!/O=Test/ST=ShanngXi/C=CN"
    )

    if not exist !new_crt! (
        openssl ca ^
            -batch ^
            -extensions !ext_section! ^
            -out !new_crt! ^
            -days !expire_days! ^
            -passin pass:mycap@ss ^
            -in !new_csr!
    )
    rem openssl x509 -in Test-Root-CA\certs\alice.crt.pem -noout -text
    rem openssl x509 -in Test-Root-CA\certs\alice.crt.pem -noout -nameopt RFC2253 -text

    openssl pkcs12 ^
        -export ^
        -in !new_crt! -inkey !new_key! -chain -CAfile !ca_dir!\cacert.pem ^
        -name !common_name! ^
        -out !new_p12! ^
        -password pass:password
)
endlocal

The content of the file target-list is as follows:

Alice Test-Root-CA v3_sign_user 1095
Bob Test-Root-CA v3_sign_user 1095
Carol Test-Root-CA v3_sign_user 1095

3. Revoke end user certificate

The code is as follows (example):

@echo off

path D:\opts\OpenSSL-Win64\bin;%PATH%

setlocal enabledelayeexpansion

rem sets the root directory of the CA
set CATOP=%~dp0
set OPENSSL_CONF=?TOP%openssl.cnf

for /f "tokens=1-2 delims= " %%i in (revoke-list) do (
    rem The variables defined here need to be accessed using !
    set common_name=%%i
    set ca_name=%%j

    set ca_dir=?TOP%!ca_name!
    set crt_file=!ca_dir!\certs\!common_name!.crt.pem

    if exist !crt_file! (
        openssl ca ^ -revoke !crt_file! -passin pass:mycap@ss
    )
)

endlocal

The content of the file revoke-list is as follows:

Carol NECAS-Root-CA

4. Update revocation list (CRL)

The code is as follows (example):

@echo off

path D:\opts\OpenSSL-Win64\bin;%PATH%

setlocal enabledelayeexpansion

rem sets the root directory of the CA
set CATOP=%~dp0
set OPENSSL_CONF=?TOP%openssl.cnf

for /f "tokens=1 delims= " %%i in (ca-list) do (
    set ca_name=%%i

    set ca_dir=?TOP%!ca_name!
    set crl_file=!ca_dir!\crl\crl.pem

    echo !crl_file!

    if not exist !ca_dir!\crl mkdir !ca_dir!\crl
    if not exist !ca_dir!\crlnumber echo 00 >!ca_dir!\crlnumber

    openssl ca -gencrl -crldays 365 -out !crl_file! -passin pass:mycap@ss
)

endlocal

5. OpenSSL configuration file

Its content is as follows:

dir=.
CA_DIR=.\demoCA

[ ca ]
default_ca = CA_default

[CA_default]
prompt=no
dir = $ENV::CA_DIR
certs = $dir/certs
crl_dir = $dir/crl
database = $dir/index.txt

new_certs_dir=$dir/newcerts
certificate=$dir/cacert.pem
serial=$dir/serial

crlnumber=$dir/crlnumber
crl=$dir/crl.pem

private_key=$dir/private/cakey.pem


default_days=365
default_crl_days=30
default_md=default
#preserve=no

policy=policy_match

[policy_match]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName=supplied
emailAddress=optional
name=optional
givenName = optional
surname=optional
initials=optional
streetAddress=optional
title=optional

[req]
default_bits=2048
default_keyfile=privkey.pem
distinguished_name=req_distinguished_name
attributes=req_attributes
x509_extensions=v3_ca

# input_password=myp@ss
# output_password=myp@ss

[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = CN
countryName_min = 2
countryName_max = 2

stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = ShanngXi

localityName = Locality Name (eg, city)

organizationName = Organization Name (eg, company)
organizationName_default = Test

# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd

organizationalUnitName = Organizational Unit Name (eg, section)
#organizationalUnitName_default =

commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64

emailAddress = Email Address
emailAddress_default = [email protected]
emailAddress_max = 64

# SET-ex3 = SET extension number 3

name=Name
name_default=Name1
givenName=FirstName
givenName_default=FirstName1
surname=LastName
surname_default=LastName1
initials=Initials
initials_default=F.K.
streetAddress=Street
streetAddress_default=Street1
title=Title
title_default=Title1

[req_attributes]


[ v3_ca ]
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer
basicConstraints = CA:true
keyUsage = cRLSign, keyCertSign


[ v3_sign_user ]
basicConstraints = CA:false
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid,issuer

subjectAltName = @SubjectAlternativeName

crlDistributionPoints=@crl_section

authorityInfoAccess=@ocsp_section

[SubjectAlternativeName]
IP.1 = 127.0.0.1
DNS.1 = localhost

[crl_section]
URI.0 = http://localhost:8080/crl.pem

[ocsp_section]
caIssuers;URI.0 = http://localhost:8080/cacert.pem
OCSP;URI.0 = http://localhost:8080/ocsp

A special note about the configuration is that the DN field in the req_distinguished_name node also needs to set the policy for each field in the policy-match section, otherwise it will not be included in the signed certificate. Contains fields for which no policy is specified.

Summary

Because no further input is required during script execution, each certificate required for testing can be automatically and quickly created. Of course, the above script can also be modified to meet other purposes. I hope it can inspire others. It would be an honor if I could help you.

For reference only, I would be grateful if it helps. If you need to reprint, please indicate the source.
Please follow, like and collect.