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
-
[1] X.509 – Wikipedia https://en.wikipedia.org/wiki/X.509
-
[2] What is the difference between “BEGIN RSA PRIVATE KEY” and “BEGIN PRIVATE KEY”? http://stackoverflow.com/questions/20065304/what-is-the-differences-between-begin-rsa-private-key-and-begin-private-key