Protecting OpenSSL private keys using PKCS#8

When generating private keys with OpenSSL, by default they are unprotected by a passphrase. For example:

$ openssl genrsa
-----BEGIN PRIVATE KEY----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCY+q1kOPM4RF5T
...
Rjet4T2TrJmFzIL1dsgJACU=
-----END PRIVATE KEY-----

generates a plaintext private key. By using the -aes256 parameters, the generated key is protected according to PKCS#8:

$ openssl genrsa -aes256
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQITQpYPEgrGN0CAggA
...
XIdi3hSPE8NoPWnROVGMnGbFOMm36g6064nZzQBD4r+4
-----END ENCRYPTED PRIVATE KEY-----

However, is it actually secure?

The default protection used by genrsa is PBKDF2 with 2048 (0x800) iterations of HMAC-SHA256. This can be verified by examining the PKCS#8 key, which is ASN.1 encoded.

$ openssl asn1parse -in key.priv 
    0:d=0  hl=4 l=1325 cons: SEQUENCE          
    4:d=1  hl=2 l=  87 cons: SEQUENCE          
    6:d=2  hl=2 l=   9 prim: OBJECT            :PBES2
   17:d=2  hl=2 l=  74 cons: SEQUENCE          
   19:d=3  hl=2 l=  41 cons: SEQUENCE          
   21:d=4  hl=2 l=   9 prim: OBJECT            :PBKDF2
   32:d=4  hl=2 l=  28 cons: SEQUENCE          
   34:d=5  hl=2 l=   8 prim: OCTET STRING      [HEX DUMP]:EAD8B97C8EAD1414
   44:d=5  hl=2 l=   2 prim: INTEGER           :0800
   48:d=5  hl=2 l=  12 cons: SEQUENCE          
   50:d=6  hl=2 l=   8 prim: OBJECT            :hmacWithSHA256
   60:d=6  hl=2 l=   0 prim: NULL              
   62:d=3  hl=2 l=  29 cons: SEQUENCE          
   64:d=4  hl=2 l=   9 prim: OBJECT            :aes-256-cbc
   75:d=4  hl=2 l=  16 prim: OCTET STRING      [HEX DUMP]:290DB8F1786A9C8667829A4A394A8FB7
   93:d=1  hl=4 l=1232 prim: OCTET STRING      [HEX DUMP]:D828C11F170D22B82801A567A9136AA293D630692B88C1F07987ED256C29DC45C4625709A0D36039B22E5A8BCA2B400C590C2560CED3629D1F8C5D77AD...CC5F8027A3A5ED533A00D626E7DDC4ABC85547

A meager 2048 SHA256 iterations provide very little added protection on top of the password intrinsic entropy.

Better protection can be achieved by using scrypt to protect the PKCS#8 encoded key. You can upgrade an existing key’s protection to scrypt:

openssl pkcs8 -in key.priv -topk8 -scrypt -out key-scrypt.priv

The command will prompt for the key’s passphrase and re-encrypt it using a key derived from the same passphrase using scrypt.

openssl pkcs8 -in key3.priv -topk8 -scrypt -out key5priv

The default openssl scrypt parameters, N=0x4000=2^14, r=8, p=1, are simply weak. They are what scrypt’s author recommended to achieve 100ms per iteration on his 2002-era laptop. While better than the PBKDF2 protection offered by OpenSSL, you might want stronger protection.

Choosing better scrypt parameters

Before we dive into the parameters, let’s understand the scrypt algorithm briefly. Scrypt takes three input parameters: password, salt, and cost parameters. The password and salt are user-provided inputs, whereas the cost parameters are fixed for a given implementation.

The cost parameters determine the computational cost of the algorithm. Higher cost parameters result in increased computational requirements, making it harder to brute-force the derived key. The cost parameters are as follows:

  • N: This is the CPU/memory cost parameter. It determines the number of iterations the algorithm performs. The value of N must be a power of two.
  • r: This is the block size parameter. It determines the size of the blocks of memory used during the algorithm’s execution.
  • p: This is the parallelization parameter. Doesn’t affect the memory consumption of the algorithm. However, despite it’s name, OpenSSL doesn’t perform any parallelization, instead opting to run the algorithm serially.

The original article recommends r=8 and p=1 as a good ratio between memory and CPU costs. This allows us to scale N almost arbitrary, increasing both CPU and memory costs. However, OpenSSL, by default, limits it’s memory usage.

$ openssl pkcs8 -in key.priv -topk8 -scrypt -scrypt_N 0x8000 -out key-scrypt2.priv
Enter pass phrase for key.priv:
Error setting PBE algorithm
40E778DD277F0000:error:030000AC:digital envelope routines:scrypt_alg:memory limit exceeded:../providers/implementations/kdfs/scrypt.c:482:
40E778DD277F0000:error:068000E3:asn1 encoding routines:PKCS5_pbe2_set_scrypt:invalid scrypt parameters:../crypto/asn1/p5_scrypt.c:59:

This limitation severely our parameters choice. The only option to increase security, is through the CPU cost parameter (p). We can set it (almost) arbitrary large, getting added security. For example:

$ openssl pkcs8 -in MOK.priv -topk8 -scrypt -scrypt_p 32 -passin pass:1234 -passout pass:1234 -out key-scrypt3.priv

Signing kernel modules for Secure Boot

Some time ago, I needed to use the v4l2loopback module. It can be installed via:

$ sudo apt install v4l2loopback-dkms

Normally, after installing a module, you can just modprobe it, and it will load. However, due to Secure Boot, it will fail.

$ sudo modprobe v4l2loopback 
modprobe: ERROR: could not insert 'v4l2loopback': Operation not permitted

The problem is that the v4l2loopback isn’t signed. For example, compare the output of:

$ /usr/sbin/modinfo -F signer v4l2loopback

which is empty, versus

$ /usr/sbin/modinfo -F signer xor
Debian Secure Boot CA

The solution would be to sign the v4l2loopback module ourselves.

Creating a key

The update-secureboot-policy script available in Ubuntu’s shim-signed package is able to generate Machine Owner Keys (MOK) by itself. However, the currently available in Debian Unstable doesn’t have the key generation functionality. We can either fetch the Ubuntu version or generate the keys ourselves.

$ wget https://git.launchpad.net/ubuntu/+source/shim-signed/plain/update-secureboot-policy
$ chmod +x ./update-secureboot-policy
$ sudo ./update-secureboot-policy --new-key

Or through generating the keys ourselves:

$ sudo mkdir -p /var/lib/shim-signed/mok
$ cd /var/lib/shim-signed/mok/
$ sudo openssl genrsa -aes256 -out MOK.priv
$ sudo openssl req \
        -subj "/CN=`hostname -s | cut -b1-31` Secure Boot Module Signature key" \
        -new -x509 -nodes -days 36500 -outform DER \
        -key MOK.priv \
        -out MOK.der

Write down the passphrase for your private key. You will need it whenever you want to sign drivers.

Now we enroll the newly created key:

$ sudo mokutil --import MOK.der

You will be prompted for a password. This password will be required after reboot in order to complete the key enrollment, you will not need it afterwards.

After reboot, check that your key was indeed enrolled:

$ mokutil --list-enrolled

Signing the module

We need to put the passphrase for the private key in the KBUILD_SIGN_PIN env variable:

$ read -s KBUILD_SIGN_PIN
$ export KBUILD_SIGN_PIN

Now we can do the actual signing:

$ cd /usr/lib/modules/$(uname -r)/updates/dkms
$ sudo --preserve-env=KBUILD_SIGN_PIN /usr/lib/linux-kbuild-$(uname -r | cut -d. -f1-2)/scripts/sign-file sha256 /var/lib/shim-signed/mok/MOK{.priv,.der} v4l2loopback.ko

You will need to repeat this step for every new kernel that you install.

Quick crypto benchmarks

These are two easy ways to benchmark crypto operation speeds on a Linux machine. The first one is geared towards disk encryption operations:

$ /usr/sbin/cryptsetup benchmark 
# Tests are approximate using memory only (no storage IO).
PBKDF2-sha1      1691251 iterations per second for 256-bit key
PBKDF2-sha256    2144327 iterations per second for 256-bit key
PBKDF2-sha512    1615679 iterations per second for 256-bit key
PBKDF2-ripemd160  916587 iterations per second for 256-bit key
PBKDF2-whirlpool  701858 iterations per second for 256-bit key
argon2i       6 iterations, 1048576 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
argon2id      6 iterations, 1048576 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
#     Algorithm |       Key |      Encryption |      Decryption
        aes-cbc        128b      1209.7 MiB/s      3687.8 MiB/s
    serpent-cbc        128b        99.9 MiB/s       794.0 MiB/s
    twofish-cbc        128b       230.1 MiB/s       421.7 MiB/s
        aes-cbc        256b       937.9 MiB/s      2944.4 MiB/s
    serpent-cbc        256b       104.2 MiB/s       794.3 MiB/s
    twofish-cbc        256b       239.7 MiB/s       425.8 MiB/s
        aes-xts        256b      3634.5 MiB/s      3638.9 MiB/s
    serpent-xts        256b       762.0 MiB/s       747.9 MiB/s
    twofish-xts        256b       408.1 MiB/s       423.3 MiB/s
        aes-xts        512b      2956.6 MiB/s      2750.4 MiB/s
    serpent-xts        512b       746.9 MiB/s       752.9 MiB/s
    twofish-xts        512b       390.8 MiB/s       421.4 MiB/s

You can actually see how fast AES is compared to other block ciphers due to AES-NI.

The second is based on OpenSSL and provides by default a much more thorough benchmark:

$ openssl speed 
Doing md4 for 3s on 16 size blocks: 19471550 md4's in 3.00s
Doing md4 for 3s on 64 size blocks: 15103053 md4's in 3.00s
Doing md4 for 3s on 256 size blocks: 9059338 md4's in 3.00s
Doing md4 for 3s on 1024 size blocks: 3412145 md4's in 3.00s
Doing md4 for 3s on 8192 size blocks: 498545 md4's in 3.00s
Doing md4 for 3s on 16384 size blocks: 253216 md4's in 3.00s
Doing md5 for 3s on 16 size blocks: 28957162 md5's in 3.00s
Doing md5 for 3s on 64 size blocks: 16360429 md5's in 3.00s
Doing md5 for 3s on 256 size blocks: 7154365 md5's in 3.00s
Doing md5 for 3s on 1024 size blocks: 2218149 md5's in 3.00s
Doing md5 for 3s on 8192 size blocks: 297722 md5's in 3.00s
Doing md5 for 3s on 16384 size blocks: 147715 md5's in 3.00s
Doing hmac(md5) for 3s on 16 size blocks: 11929825 hmac(md5)'s in 3.00s
Doing hmac(md5) for 3s on 64 size blocks: 9120709 hmac(md5)'s in 3.00s
Doing hmac(md5) for 3s on 256 size blocks: 5318155 hmac(md5)'s in 3.00s
Doing hmac(md5) for 3s on 1024 size blocks: 2001226 hmac(md5)'s in 3.00s
Doing hmac(md5) for 3s on 8192 size blocks: 291987 hmac(md5)'s in 3.00s
Doing hmac(md5) for 3s on 16384 size blocks: 148269 hmac(md5)'s in 3.00s
Doing sha1 for 3s on 16 size blocks: 32235670 sha1's in 3.00s
Doing sha1 for 3s on 64 size blocks: 18479233 sha1's in 3.00s
Doing sha1 for 3s on 256 size blocks: 9153365 sha1's in 3.00s
Doing sha1 for 3s on 1024 size blocks: 3007449 sha1's in 3.00s
Doing sha1 for 3s on 8192 size blocks: 417697 sha1's in 3.00s
Doing sha1 for 3s on 16384 size blocks: 209985 sha1's in 3.00s
Doing sha256 for 3s on 16 size blocks: 17740446 sha256's in 3.00s
Doing sha256 for 3s on 64 size blocks: 9850795 sha256's in 3.00s
Doing sha256 for 3s on 256 size blocks: 4567892 sha256's in 3.00s
Doing sha256 for 3s on 1024 size blocks: 1408055 sha256's in 3.00s
Doing sha256 for 3s on 8192 size blocks: 190363 sha256's in 3.00s
Doing sha256 for 3s on 16384 size blocks: 95699 sha256's in 3.00s
Doing sha512 for 3s on 16 size blocks: 12097608 sha512's in 3.00s
Doing sha512 for 3s on 64 size blocks: 12203513 sha512's in 3.00s
Doing sha512 for 3s on 256 size blocks: 5178856 sha512's in 3.00s
Doing sha512 for 3s on 1024 size blocks: 1948042 sha512's in 3.00s
Doing sha512 for 3s on 8192 size blocks: 280301 sha512's in 3.00s
Doing sha512 for 3s on 16384 size blocks: 140813 sha512's in 3.00s
Doing whirlpool for 3s on 16 size blocks: 8456994 whirlpool's in 3.00s
Doing whirlpool for 3s on 64 size blocks: 4540393 whirlpool's in 3.00s
Doing whirlpool for 3s on 256 size blocks: 1868465 whirlpool's in 3.00s
Doing whirlpool for 3s on 1024 size blocks: 544008 whirlpool's in 2.99s
Doing whirlpool for 3s on 8192 size blocks: 74183 whirlpool's in 3.00s
Doing whirlpool for 3s on 16384 size blocks: 37414 whirlpool's in 3.00s
Doing rmd160 for 3s on 16 size blocks: 10071219 rmd160's in 3.00s
Doing rmd160 for 3s on 64 size blocks: 6032766 rmd160's in 3.00s
Doing rmd160 for 3s on 256 size blocks: 2779084 rmd160's in 3.00s
Doing rmd160 for 3s on 1024 size blocks: 880524 rmd160's in 3.00s
Doing rmd160 for 3s on 8192 size blocks: 118235 rmd160's in 3.00s
Doing rmd160 for 3s on 16384 size blocks: 59732 rmd160's in 3.00s
Doing rc4 for 3s on 16 size blocks: 148607832 rc4's in 3.00s
Doing rc4 for 3s on 64 size blocks: 39326183 rc4's in 3.00s
Doing rc4 for 3s on 256 size blocks: 8165860 rc4's in 3.00s
Doing rc4 for 3s on 1024 size blocks: 1913512 rc4's in 3.00s
Doing rc4 for 3s on 8192 size blocks: 236942 rc4's in 3.00s
Doing rc4 for 3s on 16384 size blocks: 117673 rc4's in 3.00s
Doing des cbc for 3s on 16 size blocks: 16847525 des cbc's in 3.00s
Doing des cbc for 3s on 64 size blocks: 4335887 des cbc's in 3.00s
Doing des cbc for 3s on 256 size blocks: 1087088 des cbc's in 3.00s
Doing des cbc for 3s on 1024 size blocks: 273049 des cbc's in 3.00s
Doing des cbc for 3s on 8192 size blocks: 34202 des cbc's in 3.00s
Doing des cbc for 3s on 16384 size blocks: 17021 des cbc's in 3.00s
Doing des ede3 for 3s on 16 size blocks: 6477330 des ede3's in 3.00s
Doing des ede3 for 3s on 64 size blocks: 1630515 des ede3's in 3.00s
Doing des ede3 for 3s on 256 size blocks: 408304 des ede3's in 3.00s
Doing des ede3 for 3s on 1024 size blocks: 101953 des ede3's in 3.00s
Doing des ede3 for 3s on 8192 size blocks: 12744 des ede3's in 2.99s
Doing des ede3 for 3s on 16384 size blocks: 6395 des ede3's in 3.00s
Doing aes-128 cbc for 3s on 16 size blocks: 50546274 aes-128 cbc's in 3.00s
Doing aes-128 cbc for 3s on 64 size blocks: 13358038 aes-128 cbc's in 3.00s
Doing aes-128 cbc for 3s on 256 size blocks: 3333758 aes-128 cbc's in 3.00s
Doing aes-128 cbc for 3s on 1024 size blocks: 842151 aes-128 cbc's in 3.00s
Doing aes-128 cbc for 3s on 8192 size blocks: 104943 aes-128 cbc's in 3.00s
Doing aes-128 cbc for 3s on 16384 size blocks: 52871 aes-128 cbc's in 3.00s
Doing aes-192 cbc for 3s on 16 size blocks: 44629776 aes-192 cbc's in 3.00s
Doing aes-192 cbc for 3s on 64 size blocks: 11527573 aes-192 cbc's in 3.00s
Doing aes-192 cbc for 3s on 256 size blocks: 2861904 aes-192 cbc's in 3.00s
Doing aes-192 cbc for 3s on 1024 size blocks: 719848 aes-192 cbc's in 3.00s
Doing aes-192 cbc for 3s on 8192 size blocks: 90802 aes-192 cbc's in 3.00s
Doing aes-192 cbc for 3s on 16384 size blocks: 45435 aes-192 cbc's in 3.00s
Doing aes-256 cbc for 3s on 16 size blocks: 39272215 aes-256 cbc's in 3.00s
Doing aes-256 cbc for 3s on 64 size blocks: 10093402 aes-256 cbc's in 3.00s
Doing aes-256 cbc for 3s on 256 size blocks: 2504776 aes-256 cbc's in 3.00s
Doing aes-256 cbc for 3s on 1024 size blocks: 637977 aes-256 cbc's in 3.00s
Doing aes-256 cbc for 3s on 8192 size blocks: 79787 aes-256 cbc's in 3.00s
Doing aes-256 cbc for 3s on 16384 size blocks: 39202 aes-256 cbc's in 3.00s
Doing aes-128 ige for 3s on 16 size blocks: 47968696 aes-128 ige's in 3.00s
Doing aes-128 ige for 3s on 64 size blocks: 12904621 aes-128 ige's in 3.00s
Doing aes-128 ige for 3s on 256 size blocks: 3298776 aes-128 ige's in 3.00s
Doing aes-128 ige for 3s on 1024 size blocks: 839361 aes-128 ige's in 3.00s
Doing aes-128 ige for 3s on 8192 size blocks: 105162 aes-128 ige's in 3.00s
Doing aes-128 ige for 3s on 16384 size blocks: 52661 aes-128 ige's in 3.00s
Doing aes-192 ige for 3s on 16 size blocks: 42104801 aes-192 ige's in 3.00s
Doing aes-192 ige for 3s on 64 size blocks: 11153161 aes-192 ige's in 3.00s
Doing aes-192 ige for 3s on 256 size blocks: 2861637 aes-192 ige's in 3.00s
Doing aes-192 ige for 3s on 1024 size blocks: 714282 aes-192 ige's in 2.99s
Doing aes-192 ige for 3s on 8192 size blocks: 89697 aes-192 ige's in 3.00s
Doing aes-192 ige for 3s on 16384 size blocks: 45105 aes-192 ige's in 3.00s
Doing aes-256 ige for 3s on 16 size blocks: 37372904 aes-256 ige's in 3.00s
Doing aes-256 ige for 3s on 64 size blocks: 9876719 aes-256 ige's in 3.00s
Doing aes-256 ige for 3s on 256 size blocks: 2494944 aes-256 ige's in 3.00s
Doing aes-256 ige for 3s on 1024 size blocks: 612649 aes-256 ige's in 3.00s
Doing aes-256 ige for 3s on 8192 size blocks: 78842 aes-256 ige's in 3.00s
Doing aes-256 ige for 3s on 16384 size blocks: 38146 aes-256 ige's in 3.00s
Doing ghash for 3s on 16 size blocks: 290449447 ghash's in 3.00s
Doing ghash for 3s on 64 size blocks: 279423358 ghash's in 3.00s
Doing ghash for 3s on 256 size blocks: 110269684 ghash's in 3.00s
Doing ghash for 3s on 1024 size blocks: 32100037 ghash's in 3.00s
Doing ghash for 3s on 8192 size blocks: 4100293 ghash's in 3.00s
Doing ghash for 3s on 16384 size blocks: 2050513 ghash's in 3.00s
Doing camellia-128 cbc for 3s on 16 size blocks: 25557277 camellia-128 cbc's in 3.00s
Doing camellia-128 cbc for 3s on 64 size blocks: 9739653 camellia-128 cbc's in 2.99s
Doing camellia-128 cbc for 3s on 256 size blocks: 2753092 camellia-128 cbc's in 3.00s
Doing camellia-128 cbc for 3s on 1024 size blocks: 705022 camellia-128 cbc's in 3.00s
Doing camellia-128 cbc for 3s on 8192 size blocks: 89548 camellia-128 cbc's in 3.00s
Doing camellia-128 cbc for 3s on 16384 size blocks: 44700 camellia-128 cbc's in 3.00s
Doing camellia-192 cbc for 3s on 16 size blocks: 22217978 camellia-192 cbc's in 3.00s
Doing camellia-192 cbc for 3s on 64 size blocks: 7553707 camellia-192 cbc's in 3.00s
Doing camellia-192 cbc for 3s on 256 size blocks: 2060504 camellia-192 cbc's in 3.00s
Doing camellia-192 cbc for 3s on 1024 size blocks: 531985 camellia-192 cbc's in 3.00s
Doing camellia-192 cbc for 3s on 8192 size blocks: 66556 camellia-192 cbc's in 3.00s
Doing camellia-192 cbc for 3s on 16384 size blocks: 32679 camellia-192 cbc's in 3.00s
Doing camellia-256 cbc for 3s on 16 size blocks: 22127633 camellia-256 cbc's in 3.00s
Doing camellia-256 cbc for 3s on 64 size blocks: 7448703 camellia-256 cbc's in 3.00s
Doing camellia-256 cbc for 3s on 256 size blocks: 1972494 camellia-256 cbc's in 3.00s
Doing camellia-256 cbc for 3s on 1024 size blocks: 529948 camellia-256 cbc's in 3.00s
Doing camellia-256 cbc for 3s on 8192 size blocks: 63902 camellia-256 cbc's in 3.00s
Doing camellia-256 cbc for 3s on 16384 size blocks: 33303 camellia-256 cbc's in 3.00s
Doing seed cbc for 3s on 16 size blocks: 19448527 seed cbc's in 2.96s
Doing seed cbc for 3s on 64 size blocks: 5086661 seed cbc's in 3.00s
Doing seed cbc for 3s on 256 size blocks: 1309579 seed cbc's in 3.00s
Doing seed cbc for 3s on 1024 size blocks: 326399 seed cbc's in 3.00s
Doing seed cbc for 3s on 8192 size blocks: 40847 seed cbc's in 2.97s
Doing seed cbc for 3s on 16384 size blocks: 20473 seed cbc's in 3.00s
Doing rc2 cbc for 3s on 16 size blocks: 11590538 rc2 cbc's in 3.00s
Doing rc2 cbc for 3s on 64 size blocks: 2993882 rc2 cbc's in 3.00s
Doing rc2 cbc for 3s on 256 size blocks: 756250 rc2 cbc's in 2.99s
Doing rc2 cbc for 3s on 1024 size blocks: 189369 rc2 cbc's in 3.00s
Doing rc2 cbc for 3s on 8192 size blocks: 23335 rc2 cbc's in 3.00s
Doing rc2 cbc for 3s on 16384 size blocks: 11620 rc2 cbc's in 3.00s
Doing blowfish cbc for 3s on 16 size blocks: 28823881 blowfish cbc's in 3.00s
Doing blowfish cbc for 3s on 64 size blocks: 7731814 blowfish cbc's in 3.00s
Doing blowfish cbc for 3s on 256 size blocks: 1819269 blowfish cbc's in 2.98s
Doing blowfish cbc for 3s on 1024 size blocks: 453845 blowfish cbc's in 2.96s
Doing blowfish cbc for 3s on 8192 size blocks: 59271 blowfish cbc's in 3.00s
Doing blowfish cbc for 3s on 16384 size blocks: 29739 blowfish cbc's in 3.00s
Doing cast cbc for 3s on 16 size blocks: 24843807 cast cbc's in 3.00s
Doing cast cbc for 3s on 64 size blocks: 7142122 cast cbc's in 3.00s
Doing cast cbc for 3s on 256 size blocks: 1720810 cast cbc's in 2.98s
Doing cast cbc for 3s on 1024 size blocks: 457591 cast cbc's in 2.99s
Doing cast cbc for 3s on 8192 size blocks: 56722 cast cbc's in 3.00s
Doing cast cbc for 3s on 16384 size blocks: 28285 cast cbc's in 3.00s
Doing rand for 3s on 16 size blocks: 3372230 rand's in 2.84s
Doing rand for 3s on 64 size blocks: 3356834 rand's in 2.75s
Doing rand for 3s on 256 size blocks: 3138476 rand's in 2.79s
Doing rand for 3s on 1024 size blocks: 2728751 rand's in 2.82s
Doing rand for 3s on 8192 size blocks: 1092880 rand's in 2.94s
Doing rand for 3s on 16384 size blocks: 651510 rand's in 2.96s
Doing 512 bits private rsa's for 10s: 290761 512 bits private RSA's in 10.00s
Doing 512 bits public rsa's for 10s: 4638554 512 bits public RSA's in 10.00s
Doing 1024 bits private rsa's for 10s: 134299 1024 bits private RSA's in 9.91s
Doing 1024 bits public rsa's for 10s: 1959617 1024 bits public RSA's in 9.98s
Doing 2048 bits private rsa's for 10s: 19348 2048 bits private RSA's in 9.99s
Doing 2048 bits public rsa's for 10s: 658878 2048 bits public RSA's in 10.00s
Doing 3072 bits private rsa's for 10s: 6641 3072 bits private RSA's in 10.01s
Doing 3072 bits public rsa's for 10s: 323266 3072 bits public RSA's in 10.00s
Doing 4096 bits private rsa's for 10s: 2972 4096 bits private RSA's in 10.00s
Doing 4096 bits public rsa's for 10s: 190751 4096 bits public RSA's in 10.00s
Doing 7680 bits private rsa's for 10s: 314 7680 bits private RSA's in 10.00s
Doing 7680 bits public rsa's for 10s: 54717 7680 bits public RSA's in 10.00s
Doing 15360 bits private rsa's for 10s: 60 15360 bits private RSA's in 10.08s
Doing 15360 bits public rsa's for 10s: 14720 15360 bits public RSA's in 10.00s
Doing 512 bits sign dsa's for 10s: 194123 512 bits DSA signs in 9.98s
Doing 512 bits verify dsa's for 10s: 278869 512 bits DSA verify in 10.00s
Doing 1024 bits sign dsa's for 10s: 113554 1024 bits DSA signs in 9.97s
Doing 1024 bits verify dsa's for 10s: 147432 1024 bits DSA verify in 10.00s
Doing 2048 bits sign dsa's for 10s: 45620 2048 bits DSA signs in 9.98s
Doing 2048 bits verify dsa's for 10s: 49758 2048 bits DSA verify in 10.00s
Doing 160 bits sign ecdsa's for 10s: 56221 160 bits ECDSA signs in 9.98s 
Doing 160 bits verify ecdsa's for 10s: 59458 160 bits ECDSA verify in 10.00s
Doing 192 bits sign ecdsa's for 10s: 47616 192 bits ECDSA signs in 9.98s 
Doing 192 bits verify ecdsa's for 10s: 47970 192 bits ECDSA verify in 10.00s
Doing 224 bits sign ecdsa's for 10s: 210941 224 bits ECDSA signs in 9.99s 
Doing 224 bits verify ecdsa's for 10s: 96849 224 bits ECDSA verify in 10.00s
Doing 256 bits sign ecdsa's for 10s: 520231 256 bits ECDSA signs in 9.91s 
Doing 256 bits verify ecdsa's for 10s: 173754 256 bits ECDSA verify in 9.99s
Doing 384 bits sign ecdsa's for 10s: 12826 384 bits ECDSA signs in 9.98s 
Doing 384 bits verify ecdsa's for 10s: 15207 384 bits ECDSA verify in 9.99s
Doing 521 bits sign ecdsa's for 10s: 39062 521 bits ECDSA signs in 9.99s 
Doing 521 bits verify ecdsa's for 10s: 19335 521 bits ECDSA verify in 10.00s
Doing 163 bits sign ecdsa's for 10s: 48363 163 bits ECDSA signs in 9.98s 
Doing 163 bits verify ecdsa's for 10s: 24392 163 bits ECDSA verify in 9.99s
Doing 233 bits sign ecdsa's for 10s: 34469 233 bits ECDSA signs in 9.97s 
Doing 233 bits verify ecdsa's for 10s: 17115 233 bits ECDSA verify in 9.97s
Doing 283 bits sign ecdsa's for 10s: 19982 283 bits ECDSA signs in 9.99s 
Doing 283 bits verify ecdsa's for 10s: 10045 283 bits ECDSA verify in 9.99s
Doing 409 bits sign ecdsa's for 10s: 11777 409 bits ECDSA signs in 10.00s 
Doing 409 bits verify ecdsa's for 10s: 6008 409 bits ECDSA verify in 10.00s
Doing 571 bits sign ecdsa's for 10s: 5447 571 bits ECDSA signs in 10.00s 
Doing 571 bits verify ecdsa's for 10s: 2799 571 bits ECDSA verify in 10.00s
Doing 163 bits sign ecdsa's for 10s: 46234 163 bits ECDSA signs in 9.97s 
Doing 163 bits verify ecdsa's for 10s: 23370 163 bits ECDSA verify in 9.99s
Doing 233 bits sign ecdsa's for 10s: 32597 233 bits ECDSA signs in 10.00s 
Doing 233 bits verify ecdsa's for 10s: 16665 233 bits ECDSA verify in 10.00s
Doing 283 bits sign ecdsa's for 10s: 19004 283 bits ECDSA signs in 9.99s 
Doing 283 bits verify ecdsa's for 10s: 9755 283 bits ECDSA verify in 9.98s
Doing 409 bits sign ecdsa's for 10s: 11207 409 bits ECDSA signs in 10.00s 
Doing 409 bits verify ecdsa's for 10s: 5814 409 bits ECDSA verify in 10.00s
Doing 571 bits sign ecdsa's for 10s: 5148 571 bits ECDSA signs in 9.99s 
Doing 571 bits verify ecdsa's for 10s: 2610 571 bits ECDSA verify in 10.00s
Doing 256 bits sign ecdsa's for 10s: 29886 256 bits ECDSA signs in 9.99s 
Doing 256 bits verify ecdsa's for 10s: 31093 256 bits ECDSA verify in 10.00s
Doing 256 bits sign ecdsa's for 10s: 29985 256 bits ECDSA signs in 9.99s 
Doing 256 bits verify ecdsa's for 10s: 32135 256 bits ECDSA verify in 9.99s
Doing 384 bits sign ecdsa's for 10s: 12609 384 bits ECDSA signs in 9.99s 
Doing 384 bits verify ecdsa's for 10s: 14306 384 bits ECDSA verify in 10.00s
Doing 384 bits sign ecdsa's for 10s: 12962 384 bits ECDSA signs in 9.99s 
Doing 384 bits verify ecdsa's for 10s: 15561 384 bits ECDSA verify in 10.00s
Doing 512 bits sign ecdsa's for 10s: 8564 512 bits ECDSA signs in 9.99s 
Doing 512 bits verify ecdsa's for 10s: 10422 512 bits ECDSA verify in 9.99s
Doing 512 bits sign ecdsa's for 10s: 8795 512 bits ECDSA signs in 10.00s 
Doing 512 bits verify ecdsa's for 10s: 10798 512 bits ECDSA verify in 9.99s
Doing 160 bits  ecdh's for 10s: 61839 160-bits ECDH ops in 10.00s
Doing 192 bits  ecdh's for 10s: 50575 192-bits ECDH ops in 9.99s
Doing 224 bits  ecdh's for 10s: 160868 224-bits ECDH ops in 10.00s
Doing 256 bits  ecdh's for 10s: 234630 256-bits ECDH ops in 10.00s
Doing 384 bits  ecdh's for 10s: 13245 384-bits ECDH ops in 10.00s
Doing 521 bits  ecdh's for 10s: 32844 521-bits ECDH ops in 10.00s
Doing 163 bits  ecdh's for 10s: 50343 163-bits ECDH ops in 9.97s
Doing 233 bits  ecdh's for 10s: 35536 233-bits ECDH ops in 9.99s
Doing 283 bits  ecdh's for 10s: 21057 283-bits ECDH ops in 9.99s
Doing 409 bits  ecdh's for 10s: 12564 409-bits ECDH ops in 10.00s
Doing 571 bits  ecdh's for 10s: 5707 571-bits ECDH ops in 10.00s
Doing 163 bits  ecdh's for 10s: 47944 163-bits ECDH ops in 9.99s
Doing 233 bits  ecdh's for 10s: 35651 233-bits ECDH ops in 9.98s
Doing 283 bits  ecdh's for 10s: 20495 283-bits ECDH ops in 9.97s
Doing 409 bits  ecdh's for 10s: 12072 409-bits ECDH ops in 10.00s
Doing 571 bits  ecdh's for 10s: 4908 571-bits ECDH ops in 9.97s
Doing 256 bits  ecdh's for 10s: 30134 256-bits ECDH ops in 9.99s
Doing 256 bits  ecdh's for 10s: 30999 256-bits ECDH ops in 9.99s
Doing 384 bits  ecdh's for 10s: 13382 384-bits ECDH ops in 10.00s
Doing 384 bits  ecdh's for 10s: 13593 384-bits ECDH ops in 10.00s
Doing 512 bits  ecdh's for 10s: 9110 512-bits ECDH ops in 10.00s
Doing 512 bits  ecdh's for 10s: 8964 512-bits ECDH ops in 9.99s
Doing 253 bits  ecdh's for 10s: 347784 253-bits ECDH ops in 10.00s
Doing 448 bits  ecdh's for 10s: 22735 448-bits ECDH ops in 9.99s
Doing 253 bits sign Ed25519's for 10s: 272233 253 bits Ed25519 signs in 10.00s 
Doing 253 bits verify Ed25519's for 10s: 101997 253 bits Ed25519 verify in 10.00s
Doing 456 bits sign Ed448's for 10s: 37378 456 bits Ed448 signs in 10.00s 
Doing 456 bits verify Ed448's for 10s: 20706 456 bits Ed448 verify in 10.00s
OpenSSL 1.1.1i  8 Dec 2020
built on: Tue Dec  8 19:32:32 2020 UTC
options:bn(64,64) rc4(16x,int) des(int) aes(partial) blowfish(ptr) 
compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-dgP4jq/openssl-1.1.1i=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2
The 'numbers' are in 1000s of bytes per second processed.
type             16 bytes     64 bytes    256 bytes   1024 bytes   8192 bytes  16384 bytes
md2                  0.00         0.00         0.00         0.00         0.00         0.00 
mdc2                 0.00         0.00         0.00         0.00         0.00         0.00 
md4             103848.27k   322198.46k   773063.51k  1164678.83k  1361360.21k  1382896.98k
md5             154438.20k   349022.49k   610505.81k   757128.19k   812979.54k   806720.85k
hmac(md5)        63625.73k   194575.13k   453815.89k   683085.14k   797319.17k   809746.43k
sha1            171923.57k   394223.64k   781087.15k  1026542.59k  1140591.27k  1146798.08k
rmd160           53713.17k   128699.01k   237148.50k   300552.19k   322860.37k   326216.36k
rc4             792575.10k   838958.57k   696820.05k   653145.43k   647009.62k   642651.48k
des cbc          89853.47k    92498.92k    92764.84k    93200.73k    93394.26k    92957.35k
des ede3         34545.76k    34784.32k    34841.94k    34799.96k    34916.00k    34925.23k
idea cbc             0.00         0.00         0.00         0.00         0.00         0.00 
seed cbc        105127.17k   108515.43k   111750.74k   111410.86k   112666.20k   111809.88k
rc2 cbc          61816.20k    63869.48k    64749.16k    64637.95k    63720.11k    63460.69k
rc5-32/12 cbc        0.00         0.00         0.00         0.00         0.00         0.00 
blowfish cbc    153727.37k   164945.37k   156286.20k   157005.84k   161849.34k   162414.59k
cast cbc        132500.30k   152365.27k   147827.97k   156713.44k   154888.87k   154473.81k
aes-128 cbc     269580.13k   284971.48k   284480.68k   287454.21k   286564.35k   288746.15k
aes-192 cbc     238025.47k   245921.56k   244215.81k   245708.12k   247949.99k   248135.68k
aes-256 cbc     209451.81k   215325.91k   213740.89k   217762.82k   217871.70k   214095.19k
camellia-128 cbc   136305.48k   208474.18k   234930.52k   240647.51k   244525.74k   244121.60k
camellia-192 cbc   118495.88k   161145.75k   175829.67k   181584.21k   181742.25k   178470.91k
camellia-256 cbc   118014.04k   158905.66k   168319.49k   180888.92k   174495.06k   181878.78k
sha256           94615.71k   210150.29k   389793.45k   480616.11k   519817.90k   522644.14k
sha512           64520.58k   260341.61k   441929.05k   664931.67k   765408.60k   769026.73k
whirlpool        45103.97k    96861.72k   159442.35k   186309.09k   202569.05k   204330.33k
aes-128 ige     255833.05k   275298.58k   281495.55k   286501.89k   287162.37k   287599.27k
aes-192 ige     224558.94k   237934.10k   244193.02k   244623.67k   244932.61k   246333.44k
aes-256 ige     199322.15k   210703.34k   212901.89k   209117.53k   215291.22k   208328.02k
ghash          1549063.72k  5961031.64k  9409679.70k 10956812.63k 11196533.42k 11198535.00k
rand             18998.48k    78122.68k   287974.86k   990865.61k  3045194.88k  3606195.89k
                  sign    verify    sign/s verify/s
rsa  512 bits 0.000034s 0.000002s  29076.1 463855.4
rsa 1024 bits 0.000074s 0.000005s  13551.9 196354.4
rsa 2048 bits 0.000516s 0.000015s   1936.7  65887.8
rsa 3072 bits 0.001507s 0.000031s    663.4  32326.6
rsa 4096 bits 0.003365s 0.000052s    297.2  19075.1
rsa 7680 bits 0.031847s 0.000183s     31.4   5471.7
rsa 15360 bits 0.168000s 0.000679s      6.0   1472.0
                  sign    verify    sign/s verify/s
dsa  512 bits 0.000051s 0.000036s  19451.2  27886.9
dsa 1024 bits 0.000088s 0.000068s  11389.6  14743.2
dsa 2048 bits 0.000219s 0.000201s   4571.1   4975.8
                              sign    verify    sign/s verify/s
 160 bits ecdsa (secp160r1)   0.0002s   0.0002s   5633.4   5945.8
 192 bits ecdsa (nistp192)   0.0002s   0.0002s   4771.1   4797.0
 224 bits ecdsa (nistp224)   0.0000s   0.0001s  21115.2   9684.9
 256 bits ecdsa (nistp256)   0.0000s   0.0001s  52495.6  17392.8
 384 bits ecdsa (nistp384)   0.0008s   0.0007s   1285.2   1522.2
 521 bits ecdsa (nistp521)   0.0003s   0.0005s   3910.1   1933.5
 163 bits ecdsa (nistk163)   0.0002s   0.0004s   4846.0   2441.6
 233 bits ecdsa (nistk233)   0.0003s   0.0006s   3457.3   1716.6
 283 bits ecdsa (nistk283)   0.0005s   0.0010s   2000.2   1005.5
 409 bits ecdsa (nistk409)   0.0008s   0.0017s   1177.7    600.8
 571 bits ecdsa (nistk571)   0.0018s   0.0036s    544.7    279.9
 163 bits ecdsa (nistb163)   0.0002s   0.0004s   4637.3   2339.3
 233 bits ecdsa (nistb233)   0.0003s   0.0006s   3259.7   1666.5
 283 bits ecdsa (nistb283)   0.0005s   0.0010s   1902.3    977.5
 409 bits ecdsa (nistb409)   0.0009s   0.0017s   1120.7    581.4
 571 bits ecdsa (nistb571)   0.0019s   0.0038s    515.3    261.0
 256 bits ecdsa (brainpoolP256r1)   0.0003s   0.0003s   2991.6   3109.3
 256 bits ecdsa (brainpoolP256t1)   0.0003s   0.0003s   3001.5   3216.7
 384 bits ecdsa (brainpoolP384r1)   0.0008s   0.0007s   1262.2   1430.6
 384 bits ecdsa (brainpoolP384t1)   0.0008s   0.0006s   1297.5   1556.1
 512 bits ecdsa (brainpoolP512r1)   0.0012s   0.0010s    857.3   1043.2
 512 bits ecdsa (brainpoolP512t1)   0.0011s   0.0009s    879.5   1080.9
                              op      op/s
 160 bits ecdh (secp160r1)   0.0002s   6183.9
 192 bits ecdh (nistp192)   0.0002s   5062.6
 224 bits ecdh (nistp224)   0.0001s  16086.8
 256 bits ecdh (nistp256)   0.0000s  23463.0
 384 bits ecdh (nistp384)   0.0008s   1324.5
 521 bits ecdh (nistp521)   0.0003s   3284.4
 163 bits ecdh (nistk163)   0.0002s   5049.4
 233 bits ecdh (nistk233)   0.0003s   3557.2
 283 bits ecdh (nistk283)   0.0005s   2107.8
 409 bits ecdh (nistk409)   0.0008s   1256.4
 571 bits ecdh (nistk571)   0.0018s    570.7
 163 bits ecdh (nistb163)   0.0002s   4799.2
 233 bits ecdh (nistb233)   0.0003s   3572.2
 283 bits ecdh (nistb283)   0.0005s   2055.7
 409 bits ecdh (nistb409)   0.0008s   1207.2
 571 bits ecdh (nistb571)   0.0020s    492.3
 256 bits ecdh (brainpoolP256r1)   0.0003s   3016.4
 256 bits ecdh (brainpoolP256t1)   0.0003s   3103.0
 384 bits ecdh (brainpoolP384r1)   0.0007s   1338.2
 384 bits ecdh (brainpoolP384t1)   0.0007s   1359.3
 512 bits ecdh (brainpoolP512r1)   0.0011s    911.0
 512 bits ecdh (brainpoolP512t1)   0.0011s    897.3
 253 bits ecdh (X25519)   0.0000s  34778.4
 448 bits ecdh (X448)   0.0004s   2275.8
                              sign    verify    sign/s verify/s
 253 bits EdDSA (Ed25519)   0.0000s   0.0001s  27223.3  10199.7
 456 bits EdDSA (Ed448)   0.0003s   0.0005s   3737.8   2070.6

Quickly generate lots of random data

What is the quickest way to generate lots of random data on the command line? Usually when I had to wipe hard-drives I would simply use dd to copy from /dev/urandom over the device. However, `/dev/urandom is quite slow and wiping hard-disks can take a long time that way. So, I decided to benchmark a few methods to generate long random streams that are usable in such scenarios.

The benchmark is based on the dd command. For example:

$ dd if=/dev/urandom of=/dev/null bs=4k count=1M

This command will copy a 4GB of random bytes from /dev/urandom over /dev/null. This is probably the simplest method to create a large stream of random bytes, and as it turns out, also the slowest.

The second construct I tried is to use OpenSSL to create a stream of random data which I can read with dd and then write to the target. For example the following would use AES-128 with a random key:

$ openssl rand -hex 32 | openssl enc -aes-128-ctr -in /dev/zero -pass stdin -nosalt | dd if=/dev/stdin of=/dev/null bs=4k count=1M

Let’s breakup this command: openssl rand -hex 32 will generate a random encryption key to be used by the AES encryption. openssl enc -aes-128-ctr -in /dev/zero -pass stdin -nosalt does the actual encryption. It reads the (random) key from stdin and then uses it to encrypt /dev/zero using AES-128 in counter mode. As /dev/zero in an endless stream of zeros, it will simply output an endless stream of (pseudo-)random data. We can also repeat the same command only swapping aes-128-ctr with aes-256-ctr. For most (all?) usage scenarios it doesn’t provided any added security benefits but does have a (small) performance penalty.

Apart from AES, which is a block cipher, we can also try to use actual stream ciphers like the old rc4 and the modern chacha20.

Additionally, many new CPUs come with AES-NI extension which speeds up AES operations considerably. We can repeat the benchmark while disabling AES-NI to see how the different methods will perform if used a CPU that doesn’t support AES-NI.

Finally, I’ve repeated the test with /dev/zero as input, just to have an upper-limit in terms of performance to compare against.

Benchmark results
AES-NINo AES-NI
/dev/zero0.42609
/dev/urandom18.8967
chacha202.693063.79217
aes-128-ctr2.0410614.9022
aes-256-ctr2.2475618.9014
rc47.77392
Benchmark results, time (in seconds) to create 4GB of random data

Conclusions

The results clearly show that you should avoid /dev/urandom. It’s simply not suitable for this task and doesn’t perform well. The various methods of using OpenSSL perform much better. The best performance is achieved by the two AES variants, with aes-128-ctr being the fastest. However, if AES-NI is not supported by the CPU, AES takes a huge performance hit, and is even slower than the (not-so-)good and old RC4. However, ChaCha20 (a modern stream cipher) performs within 30% of AES if AES-NI is available, but if AES-NI is not supported ChaCha20 outperforms the AES variants. So, unless you know AES-NI is supported ChaCha20 is the safe choice.

Getting Started with Let’s Encrypt – Tutorial

A few days ago I got my invitation to Let’s Encrypt Beta Program. For those of you who are not familiar with Let’s encrypt:

Let’s Encrypt is a new free certificate authority, built on a foundation of cooperation and openness, that lets everyone be up and running with basic server certificates for their domains through a simple one-click process.

This short tutorial is intended to get you up and running with your own Let’s Encrypt signed certificates.

The first thing is to get the Let’s Encrypt client:

git clone https://github.com/letsencrypt/letsencrypt
cd letsencrypt

The main command we will be working with is ./letsencrypt-auto. The first time you will run it, it will also ask for sudo, install various dependencies using your package manager and setup a virtualenv environment.

The next step is to issue the certificate and prove to Let’s Encrypt that you have some control over the domain. The client supports two methods to perform the validation. The first one is the standalone server. It works by setting up a webserver on port 443, and responding to a challenge from the Let’s Encrypt servers. However, if you already have your own web-server running on port 443 (the default for TLS/SSL), you would have to temporarily shut it down. To use the standalone method run:

./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory certonly

The second method is called Webroot authentication. It works by placing a folder (.well-known/acme-challenge) in the document root of your server with files corresponding to responses for challenges.

./letsencrypt-auto --agree-dev-preview --server https://acme-v01.api.letsencrypt.org/directory -a webroot --webroot-path /var/www/html/ certonly

Whatever method you chose, it will ask for a list of domains you want to validate and your email address. You can write multiple domains. The first one will be the Common Name (CN) and the rest will appear in the Subject Alt Name field.

This slideshow requires JavaScript.

The newly generated certificates will be placed in

/etc/letsencrypt/live/yourdomain.com/

The important files in this directory are fullchain.pem which contain the full certificate chain to be served to the browser and privkey.pem which is the private key.

An example Nginx configuration will now look like:

        listen 443 ssl;
        ssl_certificate /etc/letsencrypt/live/guyrutenberg.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/guyrutenberg.com/privkey.pem;

Just don’t forget to reload the web-server so configuration changes take effect. No more government snooping on my blog 😉 .

certificate

Securing Access using TLS/SSL Client Certificates

This tutorial will guide you in setting up authentication using TLS/SSL Client Certificates. It is a simple one as it would not delve into details about integration with server-side apps. Instead, it simply gives you instructions on how to set up Client Certificate as means to prevent unwanted parties from accessing your website.

For example, one such scenario where it may come useful, is limiting access to sensitive things on the server, like phpMyAdmin. phpMyAdmin for example handles sensitive data (such as database authentication) and does in plain HTTP, which may pose several security risks. Even if the data would be encrypted, someone with access to the application, might find vulnerabilities in it and exploit the relatively high-privileges it got to compromise the server. The solution to this, is also limit who has access to the application at all. A possible solution, which I’ve used, is to limit access to only to the local machine and using SSH or a VPN Tunnel to access phpMyAdmin. A better solution would be to use TLS/SSL Client Certificates. They operate on the connection level and provide both encryption and authentication. They are easier to setup than VPN tunnels and easier to use.

Note that limiting access based on TLS/SSL Client Certificate can only be done on the sub-domain level, because it happens as part of the connection, before any specific HTTP request can be made.

Most of the tutorial is not HTTP server-specific, however the server configuration part relates to Nginx. As other servers (such as Lighttpd) use a very similar configuration for Client Certificates, adapting the instruction should be straightforward.

Creating a CA

The two commands below will create CA private key and a corresponding self-signed certificate for you to sign the TLS client certificates with.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes-128-cbc -out ca.key
openssl req -new -x509 -days 365 -sha256 -key ca.key -out ca.pem

The first command will ask you for a pass phrase for the key. It is used to protect access to the private key. You can decide to not use one by dropping the -aes-128-cbc option from the command.

The second command will ask you to provide some details to be included in the certificate. Those details will be sent to the browser by the web-server to let it know which client certificate to send back when authenticating.

Server Configuration

Upload the ca.pem that was just generated to your server. You should not upload the private key (ca.key).

The following instructions are for Nginx

ssl_client_certificate /path/to/ca.pem;
ssl_verify_client on; # we require client certificates to access

Assuming you already enabled TLS/SSL for the specific sub-domain, your configuration should look something like this:

server {
        server_name subdomain.example.com;

        # SSL configuration
        #
        listen 443 ssl;
        listen [::]:443 ssl;

        ssl_certificate /etc/nginx/example.pem;
        ssl_certificate_key /etc/nginx/example.key;

        ssl_client_certificate /etc/ngingx/ca.pem;
        ssl_verify_client on;

After reloading the server, check that everything is configured correctly by trying to access your site via HTTPS. It should report “400 Bad Request” and say that “No required SSL certificate was sent”.

Creating a Client Certificate

The following commands will create the private key used for the client certificate (client.key) and a corresponding Certificate Signing Request (client.csr) which the owner of the CA certificate can sign (which in the case of this tutorial will be you.

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out client.key
openssl req -new -key client.key -sha256 -out client.csr

You will be asked again to provide some details, this time about you. Those details will be available to server once your browser sends it the client certificate. You can safely leave the “challenge password” empty1.

You can add the flag -aes-128-cbc to the first command if you want the private key for the client certificate to be encrypted. If you opt for it, you will be prompted for a pass phrase just like before.

Signing a Client Certificate

The next step is to sign the certificate signing request from the last step. It is a good practice to overview it and make sure all the details are as expected, so you do not sign anything you would not intend to.

openssl req -in client.csr -text -verify -noout | less

If everything looks just fine, you can sign it with the following command.

openssl x509 -req -days 365 -in client.csr -CA ca.pem -CAkey ca.key \
    -set_serial 0x`openssl rand 16 -hex` -sha256 -out client.pem

You will be prompted for your pass phrase for ca.key if you chose one in the first step.

Installing Client Key

Now comes the final part, where we take the signed client certificate, client.pem and combine it with the private key so in can be installed in our browser.

openssl pkcs12 -export -in client.pem -inkey client.key -name "Sub-domain certificate for some name" -out client.p12

Adjust the -name parameter to your liking. It will be used to identify the certificate in various places such as the browser. If your private key was encrypted, you will be prompted to enter a pass phrase for it. Encrytion for certificates in p12 format is mandatory, so you will be prompted to enter a password for the generated file as well. It is OK to reuse the same password here, as those files are practically equivalent. Once imported to you browser, you would not need the password for normal usage, until you would like to import it to another browser.

GlobalSign provides instruction on how to actually install the p12 client certificate for browsers in Linux and Windows.

References


  1. It is used to “enhance” the security of certificate revocation request, by requiring not only knowledge of the private key, but also the challenge password. Thus, someone who got hold of your private key cannot revoke the certificate by himself. However, this is also the reason why this option is not used more often: When someone steals your private key, usually they will prefer the certificate not to be revoked. 

Default PBKDF2 Iteration Count for Encrypted Keys Generated by OpenSSL

When generating keys with openssl you have the option to encrypt them. It is done by specifying a cipher alogrithm, for example

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -aes-128-cbc -out key.pem 

generates a 2048 bit RSA key and encrypts it with AES in CBC mode. OpenSSL will prompt you to provide a pass-phrase for the encryption. It is important to understand how that pass-phrase/password will be used to derive a key for the AES encryption. The whole encryption scheme is defined by something called PBES2 1, which in turn uses PBKDF2. The important factor on the computation complexity of PBKDF2, is the number of hash-iterations used.

OpenSSL doesn’t have an option in its command-line utilities to control that number of iterations. However, that number is allowed to change pretty much arbitrarly by the standard, so it is part of the ASN1 representation of the generated encrypted key.

$ openssl asn1parse -i -in key.pem  | head
    0:d=0  hl=4 l=1311 cons: SEQUENCE          
    4:d=1  hl=2 l=  73 cons:  SEQUENCE          
    6:d=2  hl=2 l=   9 prim:   OBJECT            :PBES2
   17:d=2  hl=2 l=  60 cons:   SEQUENCE          
   19:d=3  hl=2 l=  27 cons:    SEQUENCE          
   21:d=4  hl=2 l=   9 prim:     OBJECT            :PBKDF2
   32:d=4  hl=2 l=  14 cons:     SEQUENCE          
   34:d=5  hl=2 l=   8 prim:      OCTET STRING      [HEX DUMP]:F3098873E5AB1A81
   44:d=5  hl=2 l=   2 prim:      INTEGER           :0800
   48:d=3  hl=2 l=  29 cons:    SEQUENCE       

The line saying INTEGER :0800 states the number of iteration used (in hex notation) for the generated key.pem. It means that at least for OpenSSL 1.0.1, the default number of iterations is 0x800=2048. This number is relatively low in modern standards2.


  1. As the name suggest there is also PBES1, which is now obsolete. The main difference is that PBES1 only allowed DES and RC2 to be used as cipers. See RFC 2898 for more details. 
  2. Apple uses 10,000 iterations for iTunes passwords, and LastPass defaults to 5,000 

Extract Public Key from X.509 Certificate as Hex

X.509 certificates are common way to exchange and distribute public key information. For example, most Open Social containers use the OAuth RSA-SHA1 signature method, and distribute their public keys in the X.509 format.

While working on an AppEngine application, I needed to verify requests from such containers. However, there is (currently) no pure python library able of parsing the certificates. This meant that I needed extract the public key out of the certificate manually, and store it in some parsed way inside the Python code.

Fortunately, parsing public keys form a X.509 certificate and representing them as a Hex number turned out simple and easy.
Continue reading Extract Public Key from X.509 Certificate as Hex