How to create your own PKI with openssl
Posted by funkym0nk3y
Today certificates are widely used to verify, authenticate a client/user or server, to encrypt or sign emails or to sign other types of objects (e.g. source code). You are using a certificate at the moment, due to the secure http (https) connection.
In this post I will show you how to create your own Root Certificate Authority (CA).
1. Create a self signed root certificate
2. Create a sub ca certificate
3. Create a server certificate
4. Create a user certificate
5. Generate a certificate revocation list
6. Revoke a certificate
7. Export a certificate to PKCS#12 format
8. Bash script to manage your own CA
I recommend to configure your openssl.cnf (located at /etc/ssl/openssl.cnf). This is the most annoying part, but it simplifies the next steps. You can find an example of an openssl.cnf I’ll use at the end of this post. Be careful at the policy_match section, this can be a problem while signing a certificate signing request.
1. Create a self signed root certificate:
First of all we have to create a key for the root ca. The following commands will create a file with random noise (8192 Bytes) and a 2048 bit RSA key which is encrypted with AES 256 Bit:
$ openssl rand -out private/.randRootCA 8192 $ openssl genrsa -out rootca.key -aes256 2048 -rand private/.randRootCA
Explanation of the first command:
rand create random noise -out <file> output file of the random noise 8192 Number of bytes of random noise
Explanation of the second command:
genrsa generate private RSA key -out <file> output file of the RSA key -aes256 encrypt the key with AES 256 Bit 2048 Bitssize of the key -rand <file> use random noise
If you don’t want to encrypt the key, just remove the option -aes256, but I recommend it to encrypt the key. Feel free to change the bits size of the key to 4096 or whatever you want.
The next step is to create the self-signed root certificate:
$ openssl req -new -x509 -days 3650 -key private/rootca.key -out rootca.crt \ -config openssl.cnf
Explanation of the command:
req create a certificate signing request -new create a new signing request (you have to enter the entity data) -x509 output a self signed certificate instead of a certificate request -days <n> number of days to certify the certificate for -key <file> specifies the private key file -out <file> self signed root ca certificate -config <file> use the given openssl config file
You can take a look at the content of the certificate with the following command:
$ openssl x509 -text -in rootca.crt
Explanation of the command:
x509 X.509 data management -text print the certificate in text form -in <file> input certificate to print to stdout
2. Create a sub ca certificate (signed by the root ca):
The next step is to create a certificate signing request (CSR) for the Sub CA. To create the CSR we have to generate a key first. The generation of the key is equal to the key generation of the Root CA.
$ openssl rand -out .randSubCA 8192 $ openssl genrsa -out private/subca.key -aes256 -rand .randSubCA 2048
Now we can create the CSR:
$ openssl req -new -key private/subca.key -out subca.csr -config openssl.cnf
Explanation of the command:
req create a certificate signing request -new create a new signing request (you have to enter the entity data) -key <file> specifies the private key file -out <file> output file of Sub CA CSR -config <file> use the given openssl config file
The CSR can now be signed by the Root CA:
$ openssl ca -name CA_RootCA -in subca.csr -out certs/subca.crt \ -extensions subca_cert -config openssl.cnf
Explanation of the command:
ca certificate authority management -name <section> name of the CA (section within openssl.cnf) -in <file> input file (the CSR) -out <file> output file (signed certificate) -extensions <section> used extension, which will be added to the final certificate. The given section has to be present within the openssl.cnf -config <file> use the given openssl config file
It is also possible to change the message digest (MD) algorithm. The default MD algorithm is given in the openssl.cnf. To change the MD algorithm you can use the -md <algorithm> option. I recommend NOT to use MD5 as MD algorithm, you should use at least SHA-1. The openssl documentation says that md5, sha1 and mdc2 are possible algorithms. But it’s possible to use e.g. SHA-512 you just have to use the option: -md sha512
I got some error messages while signing the Sub CA CSR and here are the solutions I found to fix this errors:
- Error message: unable to open ‘./RootCA/index.txt’
Solution:$ touch ./RootCA/index.txt
- Error message: error while loading serial number
Solution:$ echo '01' > ./RootCA/serial
- Error message: Check that the request matches the signature \ Signature ok \ The commonName field needed to be supplied and was missing
Solution: change policy_match options in openssl.cnf or create a new CSR with a commonName
3. Create a server certificate (singed by the sub ca):
To create a server certificate, e.g. for a webserver http://www.example.com, you have to create a key and a CSR. So the first step to create the key is equal to the key creation of the Root or Sub CA:
$ openssl rand -out .randServer 8192 $ openssl genrsa -out private/server.key -aes256 -rand .randServer 2048
The next step is to create the CSR. But make sure that you enter the domain name of the server as common Name (in this example: commonName=www.example.com).
$ openssl req -new -key private/server.key -out server.csr -config openssl.cnf
Now we can sign the server CSR:
$ openssl ca -name CA_SubCA -in server.csr -out certs/server.crt -extensions server_cert -config openssl.cnf
Some applications use the subject alternative name as identifier, so it’s a good idea to set the subjectAltName with the domain name (in this example: subjectAltName=DNS:www.example.com). You can set the subjectAltName attribute in your openssl.cnf. For more details take a look at the opennssl doc: https://www.openssl.org/docs/apps/x509v3_config.html#Subject_Alternative_Name_
4. Create a user certificate (signed by the sub ca):
The creation of a user certificate is very similar to the creation of a server certificate, so I will keep this part short.
Key generation:
$ openssl rand -out .randUser 8192 $ openssl genrsa -out private/user.key -aes256 -rand .randUser 2048
Create CSR:
$ openssl req -new -key private/user.key -out user.csr -extensions user_cert \ -config openssl.cnf
Sign CSR:
$ openssl ca -name CA_SubCA -in user.csr -out certs/user.crt -extensions user_cert -config openssl.cnf
Some applications use the subject alternative name as identifier, so it’s a good idea to set the subjectAltName with the email address (in this example: subjectAltName=email:user@example.com). You can set this attribute in your openssl.cnf. I recommend to set this attribute to subjectAltName=email:copy, i.e. the email address will be set automatically.
5. Generate a certificate revocation list:
The creation of a certificate revocation list (CRL) is very easy. In this example I’ll create a CRL of the Sub CA:
$ openssl ca -name CA_SubCA -gencrl -out crl/crl.pem -config openssl.cnf
Explanation of the command:
ca certificate authority management -name <section> name of the CA (section within openssl.cnf) -gencrl create certificate revocation list -out <file> output file (the final CRL) -config <file> use the given openssl config file
You can change the MD algorithm with the -md option, e.g. -md sha512.
I got an error while generating the CRL and here is the solution I found to fix this error:
- Error message: error while loading CRL number
Solution:$ echo '01' > ./SubCA/crlnumber
6. Revoke a certificate
To revoke a certificate is very easy too. In this example I will revoke the certificate user.crt which was signed by the Sub CA:
$ openssl ca -name CA_SubCA -revoke certs/user.crt -config openssl.cnf
Explanation of the command:
ca certificate authority management -name <section> name of the CA (section within openssl.cnf) -revoke <file> input file (the certificate to revoke) -config <file> use the given openssl config file
I recommend to create a new CRL every time you revoke a certificate.
7. Export a certificate to PKCS#12 format
If you want to import a certificate to your browser or your email client you have to export the certificate to another format. Most of the applications need a certificate in PKCS#12 format. This format contains the public and private key. You have to enter a (backup) passphrase for exported certificate, to protect you private key.
$ openssl pkcs12 -export -in certs/user.crt -inkey private/user.key \ -out user.pfx -name "User Certificate"
Explanation of the command:
pkcs12 PKCS#12 management -export create a PKCS#12 file -in <file> input certificate -inkey <file> input key (corresponding to the certificate) -out <file> output file (final exported certificate) -name <string> name of the certificate (Some apps display this name in the list box)
The exported certificate user.pfx can be imported to e.g. your browser or your email client.
8. Bash script to manage your own CA
I’ve programmed a “little” Bash script to make all these steps a bit easier. The script is available at Github: https://github.com/linuxm0nk3ys/ca-script. Feel free to modify the script. If you find any bugs or you have any suggestions, please contact me (leave a comment or via Twitter: @linuxm0nk3ys).
Appendix
Full content of openssl.cnf:
# This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd # Extra OBJECT IDENTIFIER info: oid_section = new_oids [ new_oids ] # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] default_ca = CA_RootCA # The default ca section #################################################################### [ CA_RootCA ] dir = ./RootCA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/rootca.crt # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = $dir/private/rootca.key # The private key RANDFILE = $dir/private/.rootca.rand # private random number file x509_extensions = user_cert # The extentions to add to the cert name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha512 # use public key default MD preserve = no # keep passed DN ordering policy = policy_anything # CHANGE THIS #################################################################### [ CA_SubCA ] dir = ./SubCA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. new_certs_dir = $dir/newcerts # default place for new certs. certificate = $dir/subca.crt # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = $dir/private/subca.key # The private key RANDFILE = $dir/private/.subca.rand # private random number file x509_extensions = user_cert # The extentions to add to the cert name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options default_days = 3650 # how long to certify for default_crl_days= 30 # how long before next CRL default_md = sha512 # use public key default MD preserve = no # keep passed DN ordering policy = policy_match #################################################################### # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 2048 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = root_ca # The extentions to add to the self signed cert string_mask = utf8only #################################################################### [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = XX countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Some-State localityName = Locality Name (eg, city) 0.organizationName = Organization Name (eg, company) 0.organizationName_default = Example Organisation # CHANGE THIS 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_max = 64 #################################################################### [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name #################################################################### [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment #################################################################### [ root_ca ] subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = critical, CA:true, pathlen:1 keyUsage = cRLSign, keyCertSign subjectAltName=email:copy # URI of the CA certificate authorityInfoAccess = caIssuers;URI:http://my.ca/ca.html # CHANGE URI # URI of the CRL crlDistributionPoints=URI:http://crl1.example.com/my.crl # CHANGE THIS #################################################################### [ subca_cert ] subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer basicConstraints = critical, CA:true, pathlen:0 keyUsage = cRLSign, keyCertSign subjectAltName=email:copy # URI of the CA certificate authorityInfoAccess = caIssuers;URI:http://my.ca/ca.html # CHANGE URI # URI of the CRL crlDistributionPoints=URI:http://crl1.example.com/my.crl # CHANGE THIS #################################################################### [ user_cert ] basicConstraints=CA:FALSE # Feel free to add "dataEncipherment" or "keysAgreement" keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer subjectAltName=email:copy extendedKeyUsage = clientAuth, emailProtection, codeSigning # URI of the CA certificate authorityInfoAccess = caIssuers;URI:http://my.ca/ca.html # CHANGE URI # URI of the CRL crlDistributionPoints=URI:http://crl1.example.com/my.crl # CHANGE THIS #################################################################### [ server_cert ] basicConstraints=CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth # add subject Altname # IMPORTANT: You have to set the environment variable "ALTNAME", e.g. # export ALTNAME="DNS:www.example.com, DNS:www2.example.com" subjectAltName=$ENV::ALTNAME # URI of the CA certificate authorityInfoAccess = caIssuers;URI:http://my.ca/ca.html # CHANGE URI # URI of the CRL crlDistributionPoints=URI:http://crl1.example.com/my.crl # CHANGE THIS #################################################################### [ crl_ext ] authorityKeyIdentifier=keyid:always #################################################################### [ tsa ] default_tsa = tsa_config1 # the default TSA section #################################################################### [ tsa_config1 ] dir = ./demoCA # TSA root directory serial = $dir/tsaserial # The current serial number (mandatory) crypto_device = builtin # OpenSSL engine to use for signing signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) signer_key = $dir/private/tsakey.pem # The TSA private key (optional) default_policy = tsa_policy1 # Policy if request did not specify it # (optional) other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) digests = sha1 # Acceptable message digests (mandatory) accuracy = secs:1, millisecs:500, microsecs:100 # (optional) clock_precision_digits = 0 # number of digits after dot. (optional) ordering = yes # Is ordering defined for timestamps? # (optional, default: no) tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no)
Source:
www.openssl.org
RFC 2459
RFC 3280
Posted on June 19, 2013, in Bash-Scripts, Command-Line, Cryptography, Network, Security and tagged bash script, certificate, certificate authority, certificate revocation list, email, email certificate, linux command line, openssl, pki, private rsa key, random noise, revocation list, revoke, root ca, root certificate authority, sign, sub ca, vpn, vpn certificate. Bookmark the permalink. 3 Comments.
First, we want to thank you for the great bash script to manage the PKI with openssl.
Maybe, you know that great Fortigate-UTM which supports PKI to authenticate for example the Forticlient VPN.
To be compatible with it, there is need for some changes in your bash-Script:
a) In openssl.cnf, [ user_cert ], change:
Optional: keyUsage = nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, keyAgreement
Must have: extendedKeyUsage = serverAuth, clientAuth, emailProtection, codeSigning
If there is no serverAuth-value in the X509-env, the Fortigate will deny the login ☹
b) In conf.sh, change the Keysize to “2048”.
c) And the very last change to let it work: in the inc/ssl_config_parser.sh change the SHA512 to SHA256 in line 263 and 332.
Would be nice, if you maybe could add this to your docs because I think it will help the guys out there very much.
It was tested against FortiOS 5.6 with success.
Thank you!
Pingback: Elliptic Curve PKI with OpenSSL | Mostly Harmless
Pingback: Eigene Zertifizierungsstelle (CA) mit Sub-Zertifizierungsstellen (Sub-CA’s) | veloc1ty's Paradise