Notes on Digital Certificates and Public/Private Keys

Posted on February 23, 2016 -

Terminology

  • X.509

    In cryptography, X.509 is an industry standard proposed by the ITU-T for Public Key Infrastructure (PKI) and Privilege Management Infrastructure (PMI). The X.509 standard specifies public key certificates, certificate revocation lists, attribute certificates, certificate path validation algorithms, and more. [1]

  • PKCS (Public Key Cryptography Standards)

    PKCS is a set of public-key cryptography standards defined by RSA. Those related to storage mainly include:

    • PKCS#1: Defines the mathematical foundations of RSA, formats of public/private keys, and encryption/decryption and signing/verifying workflows.
    • PKCS#8: Defines the representation of private key information.
    • PKCS#12: Defines a file format that contains a private key and public key certificates, where the private key is protected by a password.

Generating Public/Private Keys and Certificates

The following operations use both openssl and keytool. Unless otherwise stated, certificates and keys are stored in PEM format.

Generate a Private Key

openssl genrsa -out ca.key 2048

The private key generated by OpenSSL uses PKCS#1 encoding, which includes all key information (such as n, e, d, p, q, etc.) [2]. Therefore, genrsa generates only the private key.

If you need a PKCS#8-encoded private key, additional conversion is required:

openssl pkcs8 -topk8 -inform PEM -in ca.key -outform PEM -nocrypt -out ca.pkcs8.key

If you need a public key, use the following command:

Derive Public Key from Private Key

openssl rsa -in ca.key -pubout -outform PEM -out ca.pub

The generated public key is PKCS#8-encoded. This format is widely used for representing public keys.

Generate a Root Certificate

openssl req -x509 -new -nodes -key ca.key -days 3000 -out ca.crt

The resulting certificate is X.509-encoded and valid for 3000 days.

Issue a Certificate

Once you have a root certificate, you can sign other certificates. First generate a private key, for example host.key, and then create a certificate signing request (CSR):

openssl req -new -key host.key -out host.csr

OpenSSL will ask several questions—pay special attention to the CN field. It must match the hostname (domain name or IP address) where the certificate will be used. If certificate verification is enabled, the CN must match the host value used by the client when connecting.

With the CSR, you can issue a certificate using the root certificate and its private key:

openssl x509 -req -in host.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 1000 -out host.crt

The certificate is still X.509-encoded and valid for 1000 days.


Certificate Format Conversion

The keys and certificates generated above are in PEM format, but other formats may be required in different contexts.

p12 / pfx

p12 / pfx files are PKCS#12-encoded objects, usually containing an X.509 certificate and its corresponding private key. To generate a p12 file:

openssl pkcs12 -export -in ca.crt -inkey ca.key -out ca.p12

Because the file contains a private key, OpenSSL will prompt you for a password to protect it.

jks

In Java environments, JKS format is often required. JKS is Java’s standard keystore format, which stores multiple keys and certificates. To generate it, use the keytool utility.

Convert a p12 file to jks:

keytool -importkeystore -srckeystore cert.p12 -srcstoretype pkcs12 -destkeystore cert.jks

You will be prompted for both the target JKS password and the source P12 password.

Convert a crt file to jks:

keytool -import -file ca.crt -keystore ca.jks

Requirements of Different Environments / Tools

Java

In Java, if you want to use RSA algorithms in the java.security package, you need PKCS#8-encoded public and private keys. Load the Base64-decoded data using PKCS8EncodedKeySpec and X509EncodedKeySpec.

If you use KeyStore or TrustStore under javax.net.ssl, it is best to use the jks format to avoid unnecessary complications.

C#

In .NET, the X509Certificate2 class (note: X509Certificate is deprecated) under System.Security.Cryptography uses the p12 format.


Issues

Certificate Validation in .NET

By default, .NET performs very strict certificate validation, including but not limited to chain validation and revocation checks. In SSL/TLS, it also validates the hostname. However, for self-signed certificates, revocation checks will fail because no revocation information is available. Therefore, you must disable revocation checking:

var chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.Build(yourCert);

Certificate Validation in Java

Java maintains its own certificate chain store (at JAVA_HOME/lib/security/cacerts). Therefore, adding certificates to the system cacerts does not always work. For an SSLContext, you must manually add certificates to the TrustStore.

Furthermore, when using JSSE, Java does not perform hostname verification automatically—you must handle it yourself. There is a useful discussion here: http://stackoverflow.com/questions/18139448/how-should-i-do-hostname-validation-when-using-jsse


References