1.2.1 Key Generation
The first step in preparing to run a TLS server is to generate a private key. Before you begin, you must make several decisions:
- Key algorithm
-
OpenSSL supports RSA, DSA, ECDSA, and EdDSA key algorithms, but not all of them are useful in practice. For example, DSA is obsolete and EdDSA is not yet widely supported. That leaves us with RSA and ECDSA algorithms to use in our certificates.
- Key size
-
The default key sizes might not be secure, which is why you should always explicitly configure key size. For example, the default for RSA keys used to be 512 bits, which is insecure. If you used a 512-bit key on your server today, an intruder could take your certificate and use brute force to recover your private key, after which she could impersonate your web site. Today, 2,048-bit RSA keys are considered secure, or 256 bits for ECDSA.
- Passphrase
-
Using a passphrase with a key is optional, but strongly recommended. Protected keys can be safely stored, transported, and backed up. On the other hand, such keys are inconvenient, because they can’t be used without their passphrases. For example, you might be asked to enter the passphrase every time you wish to restart your web server. For most, this is either too inconvenient or has unacceptable availability implications. In addition, using protected keys in production does not actually increase the security much, if at all. This is because, once activated, private keys are kept unprotected in program memory; an attacker who can get to the server can get the keys from there with just a little more effort. Thus, passphrases should be viewed only as a mechanism for protecting private keys when they are not installed on production systems. In other words, it’s all right to keep passphrases on production systems, next to the keys. If you need better security in production, you should invest in a hardware solution.1
To generate an RSA key, use the following genpkey
command:
$ openssl genpkey -out fd.key \
-algorithm RSA \
-pkeyopt rsa_keygen_bits:2048 \
-aes-128-cbc
..........................................+++++
...................................................................+++++
Enter PEM pass phrase: ************
Verifying - Enter PEM pass phrase: ************
Here, I specified that the key be protected with AES-128. You can also use AES-256 (with the -aes-256-cbc
switch), but it’s best to stay away from the other algorithms (e.g., DES, 3DES, and SEED).
By default, OpenSSL will set the public exponent of new RSA keys to 65,537. This is what’s known as a short public exponent, and it significantly improves the performance of RSA verification. You may come across advice to choose 3 as your public exponent and make verification even faster. Although that’s true, there are some unpleasant historical weaknesses associated with the use of 3 as a public exponent, which is why you should stick with 65,537. This choice provides a safety margin that’s been proven effective in the past.
When you use the genpkey
command, the generated private keys are stored in PKCS #8 format,2 which is just text and doesn’t look like much:
$ cat fd.key
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQInW7GrFjUhUcCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBn8AErtRKB9p7ii1+g2OhWBIIE
0MnC2dwGznZqpTMX0MYekzyxe4dKlJiIsVr1hgwmjFifzEBs/KvHBV3eIe9wDAzq
[21 lines removed...]
IfveVZzM6PLbDaysxX6jEgi4xVbqWugd9h3eAPeBv9Z5iZ/bZq5hMbt37ElA2Rnh
RfmWSzlASjQi4XAHVLCs6XmULCda6QGvyB7WXxuzbhOv3C6BPXR49z6S1MFvOyDA
2oaXkfS+Ip3x2svgFJj/VpYZHUHwRCzXcDl/CdVg9fxwxcYHuJDH16Qfue/LRtiJ
hqr4fHrnbbk+MZpDaU+h4shLRBg2dONdUEzhPkpdOOkF
-----END ENCRYPTED PRIVATE KEY-----
However, a private key isn’t just a blob of random data, even though that’s what it looks like at a glance. You can see a key’s structure using the following pkey
command:
$ openssl pkey -in fd.key -text -noout
Enter pass phrase for fd.key: ****************
RSA Private-Key: (2048 bit, 2 primes)
modulus:
00:be:79:08:22:1a:bc:78:3c:17:34:4a:d3:5f:2b:
[...]
publicExponent: 65537 (0x10001)
privateExponent:
10:20:95:54:b5:e8:d1:51:5d:31:9b:48:4c:5d:90:
[...]
prime1:
00:f5:3f:74:cf:ef:8f:93:e9:54:b3:79:a1:f2:91:
5a:7e:15:13:26:f7:f9:d7:a8:f3:f9:6b:2b:90:93:
57:54:cc:84:c9:ea:6f:9f:39:ad:ad:60:4c:f0:68:
16:db:1a:49:51:56:87:f1:70:ae:c9:42:89:2a:38:
55:3e:17:a0:78:a7:52:49:10:79:cf:99:ae:53:c8:
e0:60:5d:7e:91:26:86:3b:79:d2:70:c0:39:38:dd:
ed:ee:75:c0:15:c6:30:51:00:a8:93:f3:8b:25:01:
04:25:72:fc:9c:e9:73:d0:93:11:2d:82:e2:e3:d0:
66:c0:36:2f:b6:de:de:0d:47
prime2:
00:c6:d2:ce:66:b5:35:6b:35:d7:bb:b0:e3:f4:2d:
[...]
exponent1:
00:e9:2e:e9:b9:5f:f5:2b:54:fa:c5:1f:4c:7d:5f:
[...]
exponent2:
00:83:ea:bc:ad:a2:cf:a5:a9:9c:d0:d8:85:f6:ae:
[...]
coefficient:
68:18:a7:4f:aa:86:a7:e0:92:49:76:8d:24:65:fa:
[...]
If you need to have just the public part of a key separately, you can do that with the following rsa
command:
$ openssl pkey -in fd.key -pubout -out fd-public.key
Enter pass phrase for fd.key: ****************
If you look into the newly generated file, you’ll see that the markers clearly indicate that the contained information is indeed public:
$ cat fd-public.key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvnkIIhq8eDwXNErTXytD
U1JGrYUgFsN8IgFVMJmAuY15dBvSCO+6y9FA0H08utJVtHScyWeOlo1uo0TQ3RWr
Pe7W3O2SaW2gIby2cwzGf/FBExZ+BCNXkN5z8Kd38PXDLt8ar+7MJ3vrb/sW7zs2
v+rtfRar2RmhDPpVvI6sugCeHrvYDGdA/gIZAMMg3pVFivPpHnTH4AR7rTzWCWlb
nCB3z2FVYpvumrY8TvIo5OioD2I+TQyvlxDRo14QWxIdZxvPcCUxXMN9MC8fBtLu
IlllDmah8JzF2CF5IxVgVhi7hyTtSQfKsK91tAvN30F9qkZNEpjNX37M5duHUVPb
tQIDAQAB
-----END PUBLIC KEY-----
It’s good practice to verify that the output contains what you’re expecting. For example, if you forget to include the -pubout
switch on the command line, the output will contain your private key instead of the public key.
The process is similar for ECDSA keys, except that it isn’t possible to create keys of arbitrary sizes. Instead, for each key you select a named curve, which controls key size, but it controls other EC parameters as well. The following example creates a 256-bit ECDSA key using the P-256 (or secp256r1
) named curve:
$ openssl genpkey -out fd.key \
-algorithm EC \
-pkeyopt ec_paramgen_curve:P-256 \
-aes-128-cbc
Enter PEM pass phrase: ****************
Verifying - Enter PEM pass phrase: ****************
OpenSSL supports many named curves, but for web server keys, you’re generally (still) limited to only two curves that are widely supported: P-256 (also known as secp256r1
or prime256v1
) and P-384 (secp384r1
). Of these two, P-256 is sufficiently secure and provides better performance. If you’re curious to see a list of all named curves supported by OpenSSL, you can get it using the ecparam
command and the -list_curves
switch.
The recent additions x25519, x448, ed25519, and ed448 are also supported, but they are different types of curves and have to be specified using the -algorithm
switch—for example:
$ openssl genpkey -algorithm ed25519
-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIF6K3m4WM7/yMA9COn6HYyx7PjJCIzY7bnBoKupYgdTL
-----END PRIVATE KEY-----