Setting up a own CA for the enterprise

Inside an enterprise there are a lot of machines communicating with each other. It is necessary to keep these communications secure and private. This can be achieved through encryption.

In the enterprise SOA the most important protocol is HTTP. The encrypted version is HTTPS and needs at least one certificate. The certificate is the host certificate of the server and must be trusted on client side. For details have a look at this post.

The needed certificate can be bought with a yearly fee of some euros from an official certificate authority. An other possibility is to just use the certificates generated by the systems themselves (‘snakeoil’ certificate).

But if the enterprise needs many certificates a better solution is to set up a own certificate authority for the enterprise.

Don’t underestimate the effort for having your own CA. The effort is not in first place for setting up the CA or generating the certificates. Much more time is needed for the education of the employees handling the CA, the organizational processes and the documentation of issued certificates, keys and their lifetime. At official certificate authorities there are hundreds of folders describing how the employees have to act in different situations, who has the permissions to do what, who can substitute key persons and so on. Even if you don’t need such complex processes there should be some definitions about the confidentiality of the CA and the generated certificates and substations for the employee handling the CA.

I will describe how to do the serveral actions with openssl on a linux machine. Of course a current version of openssl should be used.

openssl.cnf

The first step for setting up the CA is to create or modify the file openssl.cnf. When I started to set up the CA I was surprised how difficult to understand and bad the documentation of this file was. In fact I was missing an example of the file with current settings and without everything else which is not really needed. The extension system of the openssl.cnf file makes it quite difficult to understand. If some reader has suggestions to improve it, I would be very appreciated.

1 # 2 # OpenSSL example configuration file. 3 # This is mostly being used for generation of certificate requests. 4 # 5 6 # This definition stops the following lines choking if HOME isn't 7 # defined. 8 HOME = . 9 RANDFILE = $ENV::HOME/.rnd 10 11 #################################################################### 12 [ ca ] 13 default_ca = CA_default # The default ca section 14 15 #################################################################### 16 [ CA_default ] 17 18 dir = ./CA # Where everything is kept 19 certs = $dir/certs # Where the issued certs are kept 20 crl_dir = $dir/crl # Where the issued crl are kept 21 database = $dir/index.txt # database index file. 22 #unique_subject = no # Set to 'no' to allow creation of 23 # several ctificates with same subject. 24 new_certs_dir = $dir/newcerts # default place for new certs. 25 26 certificate = $dir/cacert.pem # The CA certificate 27 serial = $dir/serial # The current serial number 28 crlnumber = $dir/crlnumber # the current crl number 29 # must be commented out to leave a V1 CRL 30 crl = $dir/crl.pem # The current CRL 31 private_key = $dir/private/cakey.pem # The private key 32 RANDFILE = $dir/private/.rand # private random number file 33 34 default_days = 365 # how long to certify for 35 default_crl_days = 30 # how long before next CRL 36 default_md = default # use public key default MD 37 preserve = no # keep passed DN ordering 38 39 # A few difference way of specifying how similar the request should look 40 # For type CA, the listed attributes must be the same, and the optional 41 # and supplied fields are just that :-) 42 policy = policy_match 43 44 # For the CA policy 45 [ policy_match ] 46 countryName = match 47 stateOrProvinceName = optional 48 organizationName = match 49 organizationalUnitName = optional 50 commonName = supplied 51 emailAddress = optional 52 53 #################################################################### 54 [ req ] 55 default_bits = 2048 56 default_md = sha256 57 default_keyfile = privkey.pem 58 distinguished_name = req_distinguished_name 59 attributes = req_attributes 60 61 # This sets a mask for permitted string types. There are several options. 62 # default: PrintableString, T61String, BMPString. 63 # pkix : PrintableString, BMPString (PKIX recommendation before 2004) 64 # utf8only: only UTF8Strings (PKIX recommendation after 2004). 65 # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). 66 # MASK:XXXX a literal mask value. 67 # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. 68 string_mask = pkix 69 70 [ req_distinguished_name ] 71 countryName = Country Name (2 letter code) 72 countryName_default = DE 73 countryName_min = 2 74 countryName_max = 2 75 76 stateOrProvinceName = State or Province Name (full name) 77 78 localityName = Locality Name (eg, city) 79 localityName_default = Muenchen 80 81 0.organizationName = Organization Name (eg, company) 82 0.organizationName_default = opitz-consulting 83 84 organizationalUnitName = Organizational Unit Name (eg, section) 85 organizationalUnitName_default = IT Department 86 87 commonName = Common Name (eg, your name or your server\'s hostname) 88 commonName_max = 64 89 90 emailAddress = Email Address 91 emailAddress_max = 64 92 93 [ req_attributes ] 94 challengePassword = A challenge password 95 challengePassword_min = 4 96 challengePassword_max = 20 97 98 unstructuredName = An optional company name 99 100 #################################################################### 101 [ root_ca_extensions ] 102 # This (basicConstraints = critical,CA:true) is what PKIX recommends but some broken software chokes on critical extensions. So we do this instead: 103 basicConstraints = CA:true 104 105 # Key usage: this is typical for a CA certificate. However since it will 106 # prevent it being used as an test self-signed certificate it is best 107 # left out by default. 108 keyUsage = keyCertSign, cRLSign 109 110 # PKIX recommendations harmless if included in all certificates. 111 subjectKeyIdentifier = hash 112 authorityKeyIdentifier = keyid:always,issuer 113 114 115 #################################################################### 116 [ client_ca_extensions ] 117 # This goes against PKIX guidelines but some CAs do it and some software 118 # requires this to avoid interpreting an end user certificate as a CA. 119 basicConstraints = CA:false 120 121 # This is typical in keyUsage for a client certificate. 122 keyUsage = keyEncipherment,nonRepudiation,digitalSignature,keyAgreement 123 124 # This is required for TSA certificates. 125 # extendedKeyUsage = critical,timeStamping 126 extendedKeyUsage = 1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2 127 128 # PKIX recommendations harmless if included in all certificates. 129 subjectKeyIdentifier =hash 130 authorityKeyIdentifier =keyid,issuer 131 132 133 #################################################################### 134 [ server_ca_extensions ] 135 # This goes against PKIX guidelines but some CAs do it and some software 136 # requires this to avoid interpreting an end user certificate as a CA. 137 basicConstraints = CA:false 138 139 # This is typical in keyUsage for a host certificate. 140 keyUsage = keyEncipherment,nonRepudiation,digitalSignature,keyAgreement 141 142 # This is required for TSA certificates. 143 # extendedKeyUsage = critical,timeStamping 144 extendedKeyUsage = 1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2 145 146 # PKIX recommendations harmless if included in all certificates. 147 subjectKeyIdentifier = hash 148 authorityKeyIdentifier = keyid,issuer 149 150

In the first section CA_default the location of different files is described. The line ‘default_ca = CA_default’ is an inclusion of the CA_default extension.

If unique_subject  = no is commented out, it is necessary to revoke every old certificate until a new one with the same subject can be generated.

The value default_days = 365 issues by default certificates with the validity of a year.

The extension policy_match defines how certificate requests to be signed by this CA must be. It is activated by the line ‘policy = policy_match’.

Next one is the extension req. It will be activated if we are creating a certificate signing request. We set the default rsa key length to 2048 bit and the default algorithm to SHA-2.

With req_distinguished_name we set up the defaults for the CN and the other attributes of the certificate.

After that I decided to create three different extensions for the three different use cases: root_ca_extensions, client_ca_extensions and server_ca_extensions. The used extension is selected during the signing of the csr.

In my first tries the client_ca_extensions and server_ca_extensions had different extendedKeyUsage settings: 1.3.6.1.5.5.7.3.2 for the client and 1.3.6.1.5.5.7.3.1 for the server. In my opinion this should be enough, but I later found some unexpected behaviors on one system perhaps triggered by this so I didn’t do further research and added both key usages to both certificates.

For the field keyUsage I found a good describing comment on stackexchange. To sum up: Needed usages depends on the cipher suite, that’s why it is recommend to add all 4.

Creating the CA key and self signing the CA certificate

First we generate the private key of the root CA and store it encrypted in a file:

1 openssl genrsa -out ${ROOT_CA_KEY} 4096 -des3

ROOT_CA_KEY is the encrypted private key file of the root certificate of the CA.

The second step is to create the certificate of the root CA:

1 openssl req -x509 -new -days 3650 -md sha256 -config /etc/pki/tls/openssl.cnf -extensions root_ca_extensions -key ${ROOT_CA_KEY} -out ${ROOT_CA_CRT} -passin pass:${ROOT_CA_PWD}

ROOT_CA_PWD is the password for the private key of the root certificate.

ROOT_CA_CRT is the root certificate of the CA.

Now our CA is ready to create certificates with associated private keys and signing certificate signing requests.

Creating a client certificate with the associated private key

I have implemented for all three use cases the same two steps, even if they could be combined or are not necessary in some cases:

  • Create a certificate signing request and a private key (file extensions csr and key)
  • Signing the certificate signing request and generating the certificate (file extension crt)

In many examples the private key file and the certificate file use the extension pem. I prefer the extensions key and crt to make it more clear, what is inside the file.

In this use case we create the csr and the key with the command:

1 openssl req -sha256 -days 365 -newkey rsa:2048 -nodes -keyout ${KEY} -out ${CSR} -subj "/C=DE/L=Muenchen/OU=IT Department/O=opitz-consulting/CN=${CERT_CN}/emailAddress=${CERT_EMAIL}"

KEY is the filename with path of the generated private key file

CSR is the filename with path of the generated certificate signing request file

CERT_CN is the identifier of the user/system using this client certificate. This could be e.g. Mister Someone or CRM.

CERT_EMAIL is the mail address of the user or using system.

The second step is to sign the csr and create the certificate:

1 openssl ca -keyfile ${ROOT_CA_KEY} -cert ${ROOT_CA_CRT} -config /etc/pki/tls/openssl.cnf -extensions client_ca_extensions -notext -batch -days 365 -md sha256 -in ${CSR} -out ${CRT} -passin pass:${ROOT_CA_PWD}

CRT is the filename with path of the generated certificate file

We use the client_ca_extensions for creating this certificate.

It is highly recommend not just to execute the two command on the shell. Instead the commands should be wrapped into shell script having only one or two parameters and defining the folders, filenames, pattern for naming and so on. Otherwise, especially if different employees are issuing certificates, the overview about the certificates will be lost very soon.

Creating a host certificate with the associated private key

In this use case we create the csr and the key with the command:

1 openssl req -sha256 -days 365 -newkey rsa:2048 -nodes -keyout ${KEY} -out ${CSR} -subj "/C=DE/L=Muenchen/OU=IT Department/O=opitz-consulting/CN=${CERT_CN}"

KEY is the filename with path of the generated private key file

CSR is the filename with path of the generated certificate signing request file

CERT_CN is the full qualified hostname of the machine.

The second step is to sign the csr and create the certificate:

1 openssl ca -keyfile ${ROOT_CA_KEY} -cert ${ROOT_CA_CRT} -config /etc/pki/tls/openssl.cnf -extensions server_ca_extensions -notext -batch -days 365 -md sha256 -in ${CSR} -out ${CRT} -passin pass:${ROOT_CA_PWD}

CRT is the filename with path of the generated certificate file

We use the server_ca_extensions for creating this certificate.

Signing a host certificate request

In this use case the csr is created on an other system and provided for signing. Also the private key file associated to the certificate signing request stays on the other system and is not needed for the signing process. Only the second step is to sign the csr and create the certificate is excuted:

1 openssl ca -keyfile ${ROOT_CA_KEY} -cert ${ROOT_CA_CRT} -config /etc/pki/tls/openssl.cnf -extensions server_ca_extensions -notext -batch -days 365 -md sha256 -in ${CSR} -out ${CRT} -passin pass:${ROOT_CA_PWD}

CSR is the filename with path of the certificate signing request file provided

CRT is the filename with path of the generated certificate file

We use the server_ca_extensions for creating this certificate.

Bernhard Mähr @ OPITZ-CONSULTING published at http://thecattlecrew.wordpress.com/

Leave a Reply