Openssl Cookbook 3ed
Openssl Cookbook 3ed
EDITION
OPENSSL
COOKBOOK
The Definitive Guide to the Most
Useful Command Line Features
Ivan Ristić
Last update: Sun May 29 04:26:18 BST 2022 (build 781)
OpenSSL Cookbook
Ivan Ristić
OpenSSL Cookbook
by Ivan Ristić
Third edition (build 781). Published in May 2022.
Copyright © 2022 Feisty Duck Limited. All rights reserved.
All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or
by any means, without the prior permission in writing of the publisher.
The author and publisher have taken care in preparation of this book, but make no expressed or implied warranty of any kind and
assume no responsibility for errors or omissions. No liability is assumed for incidental or consequential damages in connection
with or arising out of the use of the information or programs contained herein.
Table of Contents
Preface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . v
Feedback v
Acknowledgments vi
About Bulletproof TLS and PKI vi
About the Author vii
1. OpenSSL Command Line . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Getting Started 1
Determine OpenSSL Version and Configuration 2
Building OpenSSL 3
Examine Available Commands 5
Building a Trust Store 7
Key and Certificate Management 8
Key Generation 8
Creating Certificate Signing Requests 12
Creating CSRs from Existing Certificates 14
Unattended CSR Generation 14
Signing Your Own Certificates 15
Creating Certificates Valid for Multiple Hostnames 15
Examining Certificates 16
Examining Public Certificates 17
Key and Certificate Conversion 20
Configuration 23
Obtaining Supported Suites 24
Understanding Security Levels 25
Configuring TLS 1.3 26
Configuring OpenSSL Defaults 28
Recommended Suite Configuration 29
Generating DH Parameters 31
Legacy Suite Configuration 31
iii
Performance 36
Creating a Private Certification Authority 39
Features and Limitations 40
Creating a Root CA 40
Creating a Subordinate CA 47
2. Testing TLS with OpenSSL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
Custom-Compile OpenSSL for Testing 51
Connecting to TLS Services 52
Certificate Verification 56
Testing Protocols That Upgrade to TLS 57
Extracting Remote Certificates 57
Testing Protocol Support 58
Testing Cipher Suite Configuration 59
Testing Cipher Suite Preference 61
Testing Named Groups 62
Testing DANE 64
Testing Session Resumption 65
Keeping Session State across Connections 66
Checking OCSP Revocation 67
Testing OCSP Stapling 69
Checking CRL Revocation 70
Testing Renegotiation 72
Testing for Heartbleed 74
Determining the Strength of Diffie-Hellman Parameters 77
iv
Preface
For all its warts, OpenSSL is one of the most successful and most important open source
projects. It’s successful because it’s so widely used; it’s important because the security of large
parts of the Internet infrastructure relies on it. The project consists of a high-performance
implementation of key cryptographic algorithms, a complete TLS and PKI stack, and a com-
mand-line toolkit. I think it’s safe to say that if your job has something to do with security,
web development, or system administration, you can’t avoid having to deal with OpenSSL
on at least some level. The majority of the Internet is powered by open source products, and
most of them rely on OpenSSL.
This book covers two ways in which OpenSSL can be used. Chapter 1, OpenSSL Command
Line, will help users who need to perform routine tasks of key and certificate generation, and
configure programs that rely on OpenSSL for TLS functionality. This chapter also discusses
how to create a complete private CA, which is useful for development and similar internal
environments. Chapter 2, Testing TLS with OpenSSL, focuses on server security testing using
OpenSSL. Although sometimes time consuming, this type of low-level testing can’t be avoided
when you wish to know exactly what’s going on.
Both chapters are borrowed from my larger work, called Bulletproof TLS and PKI. I decided
to publish the OpenSSL chapters as a separate free book because there is a severe lack of good
and easily available documentation. As is often true for complex and long-lived projects, the
OpenSSL documentation you can find on the Internet is often wrong and outdated.
Besides, publishers often give away one or more chapters in order to show what the book is
like, and I thought I should make the most of this practice by not only making the OpenSSL
chapters free, but also by committing to continue to maintain and improve them over time.
So here they are.
Feedback
Reader feedback is always very important, but especially so in this case, because this is a living
book. In traditional publishing, often years pass before reader feedback goes back into the
book, and then only if another edition actually sees the light of day (which often does not
v
happen for technical books, because of the small market size). With this book, you’ll see new
content appear in a matter of days. Ultimately, what you send to me will affect how the book
will evolve.
The best way to contact me is to use my email address, [email protected]. Sometimes I
may also be able to respond via Twitter, where you will find me under the handle @ivanristic.
Acknowledgments
This is a short book, but it’s packed with technical information. As a result, there are ample
opportunities for mistakes. I am very grateful to Matt Caswell for his help in keeping the mis-
takes away. Matt, who is a member of the OpenSSL development team, joined me as technical
reviewer for the third edition.
Various people have written to me with their thoughts and corrections. They, too, made this
book better. I extend my thanks to Brian Howson, Christian Folini, Jeff Kayser, Martin Car-
penter, Michael Reschly, Karsten Weiss, Olivier Levillain, and Stephen N. Henson.
My special thanks goes to my copyeditor, Melinda Rankin. She has been a pleasure to work
with, as always.
vi Preface
About the Author
Ivan Ristić writes computer security books and builds security products. His book Bulletproof
TLS and PKI, the result of more than a decade of research and study, is widely recognized
as the de-facto SSL/TLS and PKI reference manual. His work on SSL Labs made hundreds
of thousands of web sites more secure. Before that, he created ModSecurity, a leading open
source web application firewall.
More recently, Ivan founded Hardenize, a platform for continuous security monitoring that
provides free assessments to everyone. He’s a member of Let’s Encrypt’s technical advisory
board.
Getting Started
If you’re using one of the Unix platforms, getting started with OpenSSL should be easy; you’re
virtually guaranteed to have it already installed on your system. Still, things could go wrong.
For example, you could have a version that’s just not right, or there could be other tools (e.g.,
LibreSSL) configured to respond when OpenSSL is invoked. For this reason, it’s best to first
check what you have installed and resort to using a custom installation only if absolutely
necessary. Another option is to look for a packaging platform. For example, for OS X you
1
The letters “eay” in the name SSLeay are Eric A. Young’s initials.
1
could use Brew or MacPorts. As always, compiling something from scratch once is rarely a
problem; maintaining that piece of software indefinitely is.
In this chapter, I assume that you’re using a Unix platform because that’s the natural envi-
ronment for OpenSSL. On Windows, it’s less common to compile software from scratch be-
cause the tooling is not readily available. You can still compile OpenSSL yourself, but it might
take more work. Alternatively, you can consider downloading the binaries from the Shining
Light Productions web site.2 If you’re downloading binaries from multiple web sites, you need
to ensure that they’re not compiled under different versions of OpenSSL. If they are, you
might experience crashes that are difficult to troubleshoot. The best approach is to use a single
bundle of programs that includes everything that you need. For example, if you want to run
Apache on Windows, you can get your binaries from the Apache Lounge web site.3
At the time of writing, OpenSSL 1.1.1 is the dominant branch used in production and has
all the nice features. On older systems, you may find a release from the 1.1.0 branch, which
is fine because it can be used securely with TLS 1.2, but it won’t support modern features,
such as TLS 1.3. In the other direction is OpenSSL 3.0, which introduces a major update of
the libraries, with substantial architectural changes and a switch to the Apache License 2.0 for
better interoperability with other programs and libraries. The command-line tooling, which
is what I am covering in this chapter and the next, should be pretty much the same. That
said, every release—and especially the major ones—is very likely to change the tools’ behavior,
often in subtle ways. When you’re changing from one branch to another, it’s worth going
through the change documentation to understand what the differences might be.
Note
Although you wouldn’t know it from looking at the version number, various oper-
ating systems often don’t actually ship the exact official OpenSSL releases. More of-
ten than not, they contain forks that are either customized for a specific platform
or patched to address various known issues. However, the version number generally
2
Win32/Win64 OpenSSL (Shining Light Productions, retrieved 19 July 2020)
3
Apache 2.4 VS16 Windows Binaries and Modules (Apache Lounge, retrieved 18 September 2021)
I don’t suppose that you would find this output very interesting initially, but it’s useful to know
where you can find out how your OpenSSL was compiled. Of special interest is the OPENSSLDIR
setting, which in my example points to /usr/lib/ssl; it will tell you where OpenSSL looks
for its default configuration and root certificates. On my system, that location is essentially an
alias for /etc/ssl, Ubuntu’s main location for PKI-related files:
lrwxrwxrwx 1 root root 14 Apr 20 11:53 certs -> /etc/ssl/certs
drwxr-xr-x 2 root root 4096 May 14 21:38 misc
lrwxrwxrwx 1 root root 20 Apr 20 11:53 openssl.cnf -> /etc/ssl/openssl.cnf
lrwxrwxrwx 1 root root 16 Apr 20 11:53 private -> /etc/ssl/private
The misc/ folder contains a few supplementary scripts, the most interesting of which are the
scripts that allow you to implement a private certification authority (CA). You may or may
not end up using it, but later in this chapter I will show you how to do the equivalent work
from scratch.
Building OpenSSL
In most cases, you will be using the system-supplied version of OpenSSL, but sometimes there
are good reasons to use a newer or indeed an older version. For example, if you have an older
system, it may be stuck with a version of OpenSSL that does not support TLS 1.3. On the
other side, newer OpenSSL versions might not support SSL 2 or SSL 3. Although this is the
Building OpenSSL 3
right thing to do in a general case, you’ll need support for these older features if your job is
to test systems for security.
You can start by downloading the most recent version of OpenSSL (in my case, 1.1.1g):
$ wget https://www.openssl.org/source/openssl-1.1.1g.tar.gz
The next step is to configure OpenSSL before compilation. For this, you will usually use
the config script, which first attempts to guess your architecture and then runs through the
configuration process:
$ ./config \
--prefix=/opt/openssl \
--openssldir=/opt/openssl \
no-shared \
-DOPENSSL_TLS_SECURITY_LEVEL=2 \
enable-ec_nistp_64_gcc_128
The automated architecture detection can sometimes fail (e.g., with older versions of
OpenSSL on OS X), in which case you should instead invoke the Configure script with the
explicit architecture string. The configuration syntax is otherwise the same.
Unless you’re sure you want to do otherwise, it is essential to use the --prefix option to install
OpenSSL to a private location that doesn’t clash with the system-provided version. Getting
this wrong may break your server. The other important option is no-shared, which forces
static linking and makes self-contained command-line tools. If you don’t use this option,
you’ll need to play with your LD_LIBRARY_PATH configuration to get your tools to work.
When compiling OpenSSL 1.1.0 or later, the OPENSSL_TLS_SECURITY_LEVEL option configures
the default security level, which establishes default minimum security requirements for all
library users. It’s very useful to set this value at compile time as it can be used to prevent
configuration mistakes. I discuss security levels in more detail later in this chapter.
The enable-ec_nistp_64_gcc_128 parameter activates optimized versions of certain frequent-
ly used elliptic curves. This optimization depends on a compiler feature that can’t be auto-
matically detected, which is why it’s disabled by default. The complete set of configuration
options is available on the OpenSSL wiki.4
Note
When compiling software, it’s important to be familiar with the default configuration
of your compiler. System-provided packages are usually compiled using various
hardening options, but if you compile some software yourself there is no guarantee
that the same options will be used.5
4
Compilation and Installation (OpenSSL, retrieved 12 August 2020)
5
Hardening (Debian, 3 August 2020)
OpenSSL 1.1.0 and above will do this automatically, so you can proceed to build the main
package with the following:
$ make
$ make test
$ sudo make install
The private/ folder is empty, but that’s normal; you do not yet have any private keys. On the
other hand, you’ll probably be surprised to learn that the certs/ folder is empty too. OpenSSL
does not include any root certificates; maintaining a trust store is considered outside the scope
of the project. Luckily, your operating system probably already comes with a trust store that
you can use immediately. The following worked on my server:
$ cd /opt/openssl
$ sudo rmdir certs
$ sudo ln -s /etc/ssl/certs
The first part of the help output lists all available utilities. To get more information about a
particular utility, use the man command followed by the name of the utility. For example, man
Standard commands
asn1parse ca ciphers cms
crl crl2pkcs7 dgst dhparam
dsa dsaparam ec ecparam
enc engine errstr gendsa
genpkey genrsa help list
nseq ocsp passwd pkcs12
pkcs7 pkcs8 pkey pkeyparam
pkeyutl prime rand rehash
req rsa rsautl s_client
s_server s_time sess_id smime
speed spkac srp storeutl
ts verify version x509
The help output doesn’t actually end there, but the rest is somewhat less interesting. In the
second part, you get the list of message digest commands:
Message Digest commands (see the `dgst' command for more details)
blake2b512 blake2s256 gost md4
md5 rmd160 sha1 sha224
sha256 sha3-224 sha3-256 sha3-384
sha3-512 sha384 sha512 sha512-224
sha512-256 shake128 shake256 sm3
And then in the third part, you’ll see the list of all cipher commands:
Cipher commands (see the `enc' command for more details)
aes-128-cbc aes-128-ecb aes-192-cbc aes-192-ecb
aes-256-cbc aes-256-ecb aria-128-cbc aria-128-cfb
aria-128-cfb1 aria-128-cfb8 aria-128-ctr aria-128-ecb
aria-128-ofb aria-192-cbc aria-192-cfb aria-192-cfb1
aria-192-cfb8 aria-192-ctr aria-192-ecb aria-192-ofb
aria-256-cbc aria-256-cfb aria-256-cfb1 aria-256-cfb8
aria-256-ctr aria-256-ecb aria-256-ofb base64
bf bf-cbc bf-cfb bf-ecb
bf-ofb camellia-128-cbc camellia-128-ecb camellia-192-cbc
camellia-192-ecb camellia-256-cbc camellia-256-ecb cast
cast-cbc cast5-cbc cast5-cfb cast5-ecb
cast5-ofb des des-cbc des-cfb
des-ecb des-ede des-ede-cbc des-ede-cfb
des-ede-ofb des-ede3 des-ede3-cbc des-ede3-cfb
des-ede3-ofb des-ofb des3 desx
rc2 rc2-40-cbc rc2-64-cbc rc2-cbc
rc2-cfb rc2-ecb rc2-ofb rc4
rc4-40 seed seed-cbc seed-cfb
Unfortunately, its certificate collection is in a proprietary format, which is not of much use to
others as is. If you don’t mind getting the collection via a third party, the Curl project provides
a regularly updated conversion in Privacy-Enhanced Mail (PEM) format, which you can use
directly:
http://curl.haxx.se/docs/caextract.html
If you’d rather work directly with Mozilla, you can convert its data using the same tool that
the Curl project is using. You’ll find more information about it in the following section.
Note
If you have an itch to write your own conversion script, note that Mozilla’s root
certificate file is not a simple list of certificates. Although most of the certificates are
those that are considered trusted, there are also some that are explicitly disallowed.
Additionally, some certificates may only be considered trusted for certain types of
usage. The Perl script I describe here is smart enough to know the difference.
At this point, what you have is a root store with all trusted certificates in the same file. This
will work fine if you’re only going to be using it with, say, the s_client tool. In that case, all
you need to do is point the -CAfile switch to your root store. Replacing the root store on a
server will require more work, depending on what operating system is used.
On Ubuntu, for example, you’ll need to replace the contents of the /etc/ssl/certs folder.
Ubuntu ships with a tool called update-ca-certificates that might work. Alternatively, you
6
Mozilla CA Certificate Store (Mozilla; 9 August 2020)
Manual Conversion
To convert Mozilla’s root store, the Curl project uses a Perl script originally written by Guenter
Knauf. This script is part of the Curl project, but you can download it directly by following
this link:
https://raw.githubusercontent.com/curl/curl/master/lib/mk-ca-bundle.pl
After you download and run the script, it will fetch the certificate data from Mozilla and
convert it to the PEM format:
$ ./mk-ca-bundle.pl
SHA256 of old file: 0
Downloading certdata.txt ...
Get certdata with curl!
[...]
Downloaded certdata.txt
SHA256 of new file: cc6408bd4be7fbfb8699bdb40ccb7f6de5780d681d87785ea362646e4dad5e8…
e
Processing 'certdata.txt' ...
Done (138 CA certs processed, 30 skipped).
If you keep previously downloaded certificate data around, the script will use it to determine
what changed and process only the updates.
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:
7
A small number of organizations will have very strict security requirements that require the private keys to be protected at any cost. For them,
the solution is to invest in a Hardware Security Module (HSM), which is a type of product specifically designed to make key extraction impossi-
ble, even with physical access to the server. To make this work, HSMs not only generate and store keys, but also perform all necessary operations
(e.g., signature generation). HSMs are typically very expensive.
Key Generation 9
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).
Warning
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 perfor-
mance of RSA verification. You may come across advice to choose 3 as your pub-
lic 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 for-
mat,8 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 rsa command:
8
You will often see advice to generate private keys using the genrsa command. Indeed, the earlier versions of this very book used this com-
mand in the examples. However, genrsa is a legacy command and should no longer be used. There is an entire new family of commands that
deal with private keys in a unified manner (i.e., one command for all private key operations, no matter the algorithm). You should also be aware
that genrsa outputs keys in a legacy format. Here’s how to tell them apart: if you see BEGIN ENCRYPTED PRIVATE KEY at the top of the file,
you’re dealing with PKCS #8, which is the new format. If you see BEGIN RSA PRIVATE KEY, that’s the legacy format.
If you need to have just the public part of a key separately, you can do that with the following
rsa command:
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.
Key Generation 11
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 perfor-
mance. 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 dif-
ferent 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-----
Note
According to Section 5.4.1 of RFC 2985,9 challenge password is an optional field that
was intended for use during certificate revocation as a way of identifying the original
entity that had requested the certificate. If entered, the password will be included
verbatim in the CSR and communicated to the CA. It’s rare to find a CA that relies on
this field; all instructions I’ve seen recommend leaving it alone. Having a challenge
password does not increase the security of the CSR in any way. Further, this field
should not be confused with the key passphrase, which is a separate feature.
After a CSR is generated, use it to sign your own certificate and/or send it to a public CA
and ask it to sign the certificate. Both approaches are described in the following sections. But
before you do that, it’s a good idea to double-check that the CSR is correct. Here’s how:
$ openssl req -text -in fd.csr -noout
Certificate Request:
Data:
Version: 1 (0x0)
Subject: C = GB, L = London, O = Feisty Duck Ltd, CN = www.feistyduck.com
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:8a:d5:de:69:30:c7:77:b0:a0:54:f7:b3:34:9a:
96:1c:23:81:e3:9c:0c:81:a6:8a:a5:14:76:f4:4c:
9
RFC 2985: PKCS #9: Selected Object Classes and Attribute Types Version 2.0 (M. Nystrom and B. Kaliski, November 2000)
Note
Unless you’re using some form of public key pinning and wish to continue using
the existing key, it’s best practice to generate a new key every time you apply for a
new certificate. Key generation is quick and inexpensive and reduces your exposure
in case of a compromise that went undetected.
[req]
prompt = no
distinguished_name = dn
req_extensions = ext
input_password = PASSPHRASE
[dn]
CN = www.feistyduck.com
emailAddress = [email protected]
[ext]
subjectAltName = DNS:www.feistyduck.com,DNS:feistyduck.com
Now you can create the CSR directly from the command line:
$ openssl req -new -config fd.cnf -key fd.key -out fd.csr
You don’t actually have to create a CSR in a separate step. The following command creates a
self-signed certificate starting with a key alone:
$ openssl req -new -x509 -days 365 -key fd.key -out fd.crt
If you don’t wish to be asked any questions, use the -subj switch to provide the certificate
subject information on the command line:
$ openssl req -new -x509 -days 365 -key fd.key -out fd.crt \
-subj "/C=GB/L=London/O=Feisty Duck Ltd/CN=www.feistyduck.com"
10
To be honest, getting a valid public certificate quickly has become much easier since Let’s Encrypt started offering them for free in an auto-
mated fashion. We’re now seeing the rise of operating systems and even software packages that seamlessly integrate with Let’s Encrypt to pro-
vide public certificates out of the box. We’re not very far from the moment when creating self-signed certificates will be the option that requires
more work.
Warning
When a certificate contains alternative names, all common names are ignored. Newer
certificates produced by CAs may not even include any common names. For that
reason, include all desired hostnames on the alternative names list.
First, place the extension information in a separate text file. I’m going to call it fd.ext. In the
file, specify the name of the extension (subjectAltName) and list the desired hostnames, as in
the following example:
subjectAltName = DNS:*.feistyduck.com, DNS:feistyduck.com
Then, when using the x509 command to issue a certificate, refer to the file using the -extfile
switch:
$ openssl x509 -req -days 365 \
-in fd.csr -signkey fd.key -out fd.crt \
-extfile fd.ext
The rest of the process is no different from before. But when you examine the generated
certificate afterward (see the next section), you’ll find that it contains the SAN extension:
X509v3 extensions:
X509v3 Subject Alternative Name:
DNS:*.feistyduck.com, DNS:feistyduck.com
Examining Certificates
Certificates don’t look like much in a text editor, but they contain a great deal of information;
you just need to know how to unpack it. The x509 command does just that, so let’s use it to
look at the self-signed certificates you generated.
In the following example, I use the -text switch to print certificate contents and -noout to
reduce clutter by not printing the encoded certificate itself (which is the default behavior):
$ openssl x509 -text -in fd.crt -noout
Certificate:
Self-signed certificates usually contain only the most basic certificate data, and most of it is
self-explanatory. In essence, there’s the main body of the certificate, to which a signature is
added. By comparison, certificates issued by public CAs are much more interesting, as they
contain a number of additional fields (via the X.509 extension mechanism).
The main differences are going to be in the X.509 extensions, which contain a great deal of
very interesting information. Let’s examine what’s in the extensions and why it’s there, in no
particular order.
The Basic Constraints extension is used to mark certificates as belonging to a CA, giving them
the ability to sign other certificates. Non-CA certificates will either have this extension omit-
ted or will have the value of CA set to FALSE. This extension is critical, which means that all
software-consuming certificates must understand its meaning.
X509v3 Basic Constraints: critical
CA:FALSE
The Key Usage (KU) and Extended Key Usage (EKU) extensions restrict what a certificate can
be used for. If these extensions are present, then only the listed uses are allowed. If the exten-
sions are not present, there are no use restrictions. What you see in this example is typical for
a web server certificate, which, for example, does not allow for code signing:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Server Authentication, TLS Web Client Authentication
The CRL Distribution Points extension lists the addresses where the CA’s Certificate Revoca-
tion List (CRL) information can be found. This information is important in cases in which
certificates need to be revoked. CRLs are CA-signed lists of revoked certificates, published at
regular time intervals (e.g., seven days). Let’s Encrypt doesn’t provide CRLs, so I took the
following snippet from another certificate:
X509v3 CRL Distribution Points:
Full Name:
URI:http://crl.starfieldtech.com/sfs3-20.crl
The Certificate Policies extension is used to indicate the policy under which the certificate
was issued. For example, this is where you can expect to find an indication of the type of
validation used to ascertain the identity of the owner. Extended validation (EV) indicators
can be found (as in the example that follows). These indicators are in the form of unique
object identifiers (OIDs), some of which are generic and some specific to the issuing CA.
In the following example, the OID 2.23.140.1.2.1 indicates a domain-validated certificate. In
addition, this extension often contains one or more Certificate Policy Statement (CPS) points,
which are usually web pages or PDF documents.
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
Policy: 1.3.6.1.4.1.44947.1.1.1
CPS: http://cps.letsencrypt.org
The Authority Information Access (AIA) extension usually contains two important pieces of
information. First, it lists the address of the CA’s Online Certificate Status Protocol (OCSP)
responder, which can be used to check for certificate revocation in real time. The exten-
sion may also contain a link to where the issuer’s certificate (the next certificate in the
chain) can be found. These days, server certificates are rarely signed directly by trusted root
certificates, which means that users must include one or more intermediate certificates in
their configuration. Mistakes are easy to make and will invalidate the certificates. Some clients
will use the information provided in this extension to fix an incomplete certificate chain, but
many clients won’t.
Authority Information Access:
OCSP - URI:http://ocsp.int-x3.letsencrypt.org
CA Issuers - URI:http://cert.int-x3.letsencrypt.org/
The Subject Key Identifier and Authority Key Identifier extensions establish unique subject and
authority key identifiers, respectively. The value specified in the Authority Key Identifier ex-
tension of a certificate must match the value specified in the Subject Key Identifier extension
in the issuing certificate. This information is very useful during the certification path-build-
ing process, in which a client is trying to find all possible paths from a leaf (server) certificate
to a trusted root. Certification authorities will often use one private key with more than one
certificate, and this field allows software to reliably identify which certificate can be matched
The Subject Alternative Name extension is used to list all the hostnames for which the
certificate is valid. This extension used to be optional; if it isn’t present, clients fall back to
using the information provided in the common name (CN), which is part of the Subject field.
If the extension is present, then the content of the CN field is ignored during validation.
X509v3 Subject Alternative Name:
DNS:www.feistyduck.com, DNS:feistyduck.com
Finally, the most recent addition is the Certificate Transparency (CT) extension, which is used
to carry proof of logging to various public CT logs. Depending on the certificate lifetime, you
can expect to see anywhere from two to five Signed Certificate Timestamps (SCTs). There isn’t
a single set of unified requirements for the numbers and types of SCTs that are necessary to
recognize a certificate as valid. Technically, it’s up to every client to specify what they expect.
In practice, Chrome was the first browser to require CT and other clients are likely to follow
its lead.11
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 5E:A7:73:F9:DF:56:C0:E7:B5:36:48:7D:D0:49:E0:32:
7A:91:9A:0C:84:A1:12:12:84:18:75:96:81:71:45:58
Timestamp : Aug 3 00:10:45.300 2020 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:21:00:BB:7F:D0:E1:E6:CD:4B:E7:79:30:AE:
BE:F6:50:4F:36:A4:F6:1D:65:21:1A:05:A9:B3:F0:53:
BA:FA:AC:6D:FB:02:20:52:23:B9:F9:B6:73:34:7F:3D:
7F:42:5C:E3:9D:3D:DA:D8:7F:B3:7E:21:0C:27:54:9B:
DA:E1:3F:0F:8E:09:60
[...]
11
Certificate Transparency in Chrome (Chromium; retrieved 15 August 2020)
The syntax is identical if you need to convert private keys between DER and PEM formats,
but different commands are used: rsa for RSA keys, and dsa for DSA keys. If you’re dealing
with the new PKCS #8 format, use the pkey tool.
The reverse conversion isn’t as straightforward. You can use a single command, but in that
case you’ll get the entire contents in a single file:
$ openssl pkcs12 -in fd.p12 -out fd.pem -nodes
Now, you must open the file fd.pem in your favorite editor and manually split it into individ-
ual key, certificate, and intermediate certificate files. While you’re doing that, you’ll notice
additional content provided before each component. For example:
Bag Attributes
localKeyID: E3 11 E4 F1 2C ED 11 66 41 1B B8 83 35 D2 DD 07 FC DE 28 76
subject=/1.3.6.1.4.1.311.60.2.1.3=GB/2.5.4.15=Private Organization…
/serialNumber=06694169/C=GB/ST=London/L=London/O=Feisty Duck Ltd…
/CN=www.feistyduck.com
issuer=/C=US/ST=Arizona/L=Scottsdale/O=Starfield Technologies, Inc./OU=http:/…
/certificates.starfieldtech.com/repository/CN=Starfield Secure Certification …
Authority
-----BEGIN CERTIFICATE-----
MIIF5zCCBM+gAwIBAgIHBG9JXlv9vTANBgkqhkiG9w0BAQUFADCB3DELMAkGA1UE
BhMCVVMxEDAOBgNVBAgTB0FyaXpvbmExEzARBgNVBAcTClNjb3R0c2RhbGUxJTAj
[...]
This additional metadata is very handy to quickly identify the certificates. Obviously, you
should ensure that the main certificate file contains the leaf server certificate and not some-
thing else. Further, you should also ensure that the intermediate certificates are provided in
Warning
The final conversion output shouldn’t contain anything apart from the encoded key
and certificates. Although some tools are smart enough to ignore what isn’t needed,
other tools are not. Leaving extra data in PEM files might result in problems that are
difficult to troubleshoot.
It’s possible to get OpenSSL to split the components for you, but doing so requires multiple
invocations of the pkcs12 command (including typing the bundle password each time):
$ openssl pkcs12 -in fd.p12 -nocerts -out fd.key -nodes
$ openssl pkcs12 -in fd.p12 -nokeys -clcerts -out fd.crt
$ openssl pkcs12 -in fd.p12 -nokeys -cacerts -out fd-chain.crt
This approach won’t save you much work. You must still examine each file to ensure that it
contains the correct contents and to remove the metadata.
PKCS #7 Conversion
To convert from PEM to PKCS #7, use the crl2pkcs7 command:
$ openssl crl2pkcs7 -nocrl -out fd.p7b -certfile fd.crt -certfile fd-chain.crt
To convert from PKCS #7 to PEM, use the pkcs7 command with the -print_certs switch:
openssl pkcs7 -in fd.p7b -print_certs -out fd.pem
Similar to the conversion from PKCS #12, you must now edit the fd.pem file to clean it up and
split it into the desired components.
Configuration
A common task in TLS server configuration is selecting which cipher suites to use. To commu-
nicate securely, TLS needs to decide which cryptographic primitives to use to achieve its goals
(e.g., confidentiality). This is done by selecting a suitable cipher suite, which makes a series
of decisions about how authentication, key exchange, encryption, and other operations are
done. Programs that rely on OpenSSL usually adopt the same approach to suite configuration
that OpenSSL uses, simply passing through the configuration options.
Before TLS 1.3, the usual server configuration would include cipher suite configuration and an
option for the server to prefer the stronger suites during the negotiation. Because of some dif-
ferences in the design of TLS 1.3 from earlier protocol versions, OpenSSL decided to configure
Configuration 23
it differently, increasing the complexity of server configuration. I’ll discuss this in the follow-
ing sections.
Coming up with a good suite configuration can be pretty time consuming, and there are a lot
of details to consider. I wrote this section to serve two goals. If you don’t want to spend a lot
of time learning how to use OpenSSL and how to rank cipher suites, simply use the default
configuration I provide. On the other hand, if you prefer to learn the ins and outs of OpenSSL
configuration, this section has the answers.
Tip
From OpenSSL 1.0.0, the ciphers command supports the uppercase -V switch to
provide extra-verbose output. In this mode, the output will also contain suite IDs,
which are always handy to have. For example, OpenSSL doesn’t always use the RFC
names for suites; in such cases, you must use the IDs to cross-check. In this section,
I use the lowercase -v because the output is easier to show in the book.
At this point you will observe a lot of output, consisting of everything your installation of
OpenSSL has to offer. In my case, there were 162 suites in the output. Let’s take a look at one
line:
Each line of output provides extended information on one suite. From left to right:
1. Suite name
2. Required minimum protocol version12
3. Key exchange algorithm
4. Authentication algorithm
5. Encryption algorithm and strength
6. MAC (integrity) algorithm
12
Some suites on the list show SSLv3 in the protocol column. This is nothing to worry about. It only means that the suite is compatible with this
old (and obsolete) protocol version. Your configuration will not downgrade to SSL 3.0 if these suites are used.
Note
You may notice that all TLS 1.3 suites have any under key exchange and authentica-
tion. This is because this protocol version moved these two aspects of the handshake
out of the cipher suites and into the protocol itself. It also removed all insecure algo-
rithms, so in this context any isn’t bad or insecure.
The most important aspect of security levels is knowing what not to use, and that’s the first two
levels. Level 0 imposes no restrictions and is potentially insecure (depending on what features
13
SSL_CTX_get_security_level man page (OpenSSL, retrieved 21 August 2020)
However, at level 4 there are only two suites in the output, because the 128-bit suites were
removed:
$ openssl ciphers -v -s -tls1_2 'EECDH+AESGCM @SECLEVEL=4'
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=RSA Enc=AESGCM(256) Mac=AEAD
You may have noticed that the previous example introduces the -tls1_2 switch, which outputs
only suites that can be negotiated with TLS 1.2. This switch, along with -tls1_3, -tls1_1, -
tls1, and -ssl3, is very useful for removing unwanted output when you’re interested in only
one protocol.
When they were adding this new configuration mechanism for TLS 1.3, OpenSSL developers
took an opportunity to simplify how suites are configured by removing a variety of tools and
keywords that can now be called legacy suite configuration. The only supported approach for
TLS 1.3 is to provide a colon-separated list of the suites you wish to support, in the order you
wish to support them. That’s all. For example:
Note
Even though there is a separate configuration string for TLS 1.3 suites, the
configuration is still affected by the security level configuration, which is specified in
the legacy configuration string.
How does this new approach to TLS 1.3 configuration affect real life? Depending on your
tools, you may now find yourself needing to use two configuration strings where previously
there was only one. In the Apache web server, the SSLCipherSuite directive has been extended
with an optional first parameter, enabling you to target the protocols you wish to configure.
So you could do something like this:
Not all tools have added support for TLS 1.3 suite configuration. Instead, you always get the
OpenSSL defaults. For most users, this is not yet a real problem because all TLS 1.3 suites are
strong. But if you want to do something out of the ordinary, perhaps enable the CCM suites
that are currently disabled by default, you’ll have to resort to using a workaround by changing
the OpenSSL defaults via a configuration file, which I will cover in the next section.
Now that we know how to change the defaults, the question instead becomes what to put into
the configuration file. For the syntax of configuration files and detailed information, it’s best
that you consult the official documentation.14 However, if you just need to reconfigure the
cipher suite configuration, take a look at the following example that does just that:
[default_conf]
ssl_conf = ssl_section
[ssl_section]
system_default = system_default_section
[system_default_section]
MinProtocol = TLSv1.2
CipherString = DEFAULT:@SECLEVEL=2
Ciphersuites = TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256
Options = ServerPreference,PrioritizeChaCha
This configuration file specifies the minimum supported protocol, security level, legacy cipher
suite configuration, and TLS 1.3 suite configuration, and it also enables special ChaCha20
14
config man page (OpenSSL, retrieved 21 August 2020)
TLS_AES_128_GCM_SHA256
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_256_GCM_SHA384
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES128-SHA
ECDHE-ECDSA-AES256-SHA
ECDHE-ECDSA-AES128-SHA256
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-RSA-AES128-SHA
ECDHE-RSA-AES256-SHA
ECDHE-RSA-AES128-SHA256
ECDHE-RSA-AES256-SHA384
DHE-RSA-AES128-GCM-SHA256
DHE-RSA-CHACHA20-POLY1305
DHE-RSA-AES256-GCM-SHA384
DHE-RSA-AES128-SHA
DHE-RSA-AES256-SHA
DHE-RSA-AES128-SHA256
DHE-RSA-AES256-SHA256
15
SSL_CONF_cmd man page (OpenSSL, retrieved 21 August 2020)
Note
In practice, most systems don’t need to be configured to support the best possible
performance or mobile client experience. If you enjoy getting your TLS configuration
just right, then by all means follow all the advice from this section. Usually, however,
you shouldn’t spend too much time on the fine-tuning. If you find yourself with a
platform that doesn’t support TLS 1.3 suite configuration or that isn’t able to prior-
itize ChaCha20, just use the defaults and move on.
16
In TLS 1.3, key exchange and authentication are not controlled by cipher suites; negotiation of these aspects has been moved into the proto-
col. However, because robust forward security is a key feature in TLS 1.3, it’s not something we need to worry about when it comes to cipher suite
configuration.
17
Speeding up and Strengthening HTTPS Connections for Chrome on Android (Google Security Blog, 24 April 2014)
In practice, only 2,048-bit DH parameters make sense. Anything less is going to be weak or
insecure, while anything more is going to slow you down. DH parameters need not be secret.
In fact, there are some predefined groups (sometimes called well-known groups) that are rec-
ommended because they are known to have been securely generated.18
Rarely, you may encounter a situation, usually in a legacy environment, in which you need to
configure a server with 1,024-bit DH parameters. It’s essential that you don’t use a well-known
group in this case. The issue is that weak DH groups are susceptible to precomputation attacks,
which further downgrade their security. If you really must use a 1,024-bit DH parameters,
always generate your own unique group using OpenSSL.
Keywords
Cipher suite keywords are the basic building blocks of cipher suite configuration. Each suite
name (e.g., RC4-SHA) is a keyword that selects exactly one suite.19 All other keywords select
groups of suites according to some criteria. Keyword names are case-sensitive. In this section,
I will provide an overview of all cipher suite keywords supported by OpenSSL, one group at
a time.
Group keywords are shortcuts that select frequently used cipher suites. For example, HIGH will
select only very strong cipher suites.
18
RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for TLS (D. Gillmor, August 2016)
19
With recent OpenSSL releases, you can use the legacy suite names that are specific to OpenSSL, but also the standard suite names.
Generating DH Parameters 31
Table 1.2. Group keywords
Keyword Meaning
DEFAULT The default cipher list. This is determined at compile time and must be the first cipher
string specified.
COMPLEMENTOFDEFAULT The ciphers included in ALL, but not enabled by default. Note that this rule does not
cover eNULL, which is not included by ALL (use COMPLEMENTOFALL if necessary).
ALL All cipher suites except the eNULL ciphers, which must be explicitly enabled.
COMPLEMENTOFALL The cipher suites not enabled by ALL, currently eNULL.
HIGH “High”-encryption cipher suites. This currently means those with key lengths larger
than 128 bits, and some cipher suites with 128-bit keys.
MEDIUM “Medium”-encryption cipher suites, currently some of those using 128-bit encryption.
LOW “Low”-encryption cipher suites, currently those using 64- or 56-bit encryption algo-
rithms, but excluding export cipher suites. No longer supported. Insecure.
EXP, EXPORT Export encryption algorithms. Including 40- and 56-bit algorithms. No longer supported.
Insecure.
EXPORT40 40-bit export encryption algorithms. No longer supported. Insecure.
EXPORT56 56-bit export encryption algorithms. No longer supported. Insecure.
TLSv1.2, TLSv1.0, TLSv1, Cipher suites that require the specified protocol version. There are two keywords for
SSLv3, SSLv2 TLS 1.0 and no keywords for TLS 1.3 and TLS 1.1. These keywords do not affect protocol
configuration, just the suites.
Digest keywords select suites that use a particular digest algorithm. For example, SHA256 selects
all suites that rely on SHA256 for integrity validation.
Keyword Meaning
Note
The digest algorithm keywords select only suites that validate data integrity at the
protocol level. TLS 1.2 introduced support for authenticated encryption, which is a
mechanism that bundles encryption with integrity validation. When the so-called
AEAD (authenticated encryption with associated Data) suites are used, the protocol
doesn’t need to provide additional integrity verification. For this reason, you won’t
be able to use the digest algorithm keywords to select AEAD suites (currently, those
that have GCM in the name). The names of these suites do use SHA256 and SHA384
Authentication keywords select suites based on the authentication method they use. Today,
the RSA public key algorithm is still used by the majority of certificates, with ECDSA quickly
catching up.
Keyword Meaning
aDH Cipher suites effectively using DH authentication, i.e., the certificates carry DH keys.
Removed in 1.1.0.
aDSS, DSS Cipher suites using DSS authentication, i.e., the certificates carry DSS keys.
aECDH Cipher suites that use ECDH authentication. Removed in 1.1.0.
aECDSA, ECDSA Cipher suites that use ECDSA authentication.
aNULL Cipher suites offering no authentication. This is currently the anonymous DH algo-
rithms. Insecure.
aRSA Cipher suites using RSA authentication, i.e., the certificates carry RSA keys.
aPSK Cipher suites using PSK (Pre-Shared Key) authentication.
aSRP Cipher suites using SRP (Secure Remote Password) authentication.
Key exchange keywords select suites based on the key exchange algorithm. When it comes
to ephemeral Diffie-Hellman suites, OpenSSL is inconsistent in naming the suites and the
keywords. In the suite names, ephemeral suites tend to have an E at the end of the key exchange
algorithm (e.g., ECDHE-RSA-RC4-SHA and DHE-RSA-AES256-SHA), but in the keywords the E is at
the beginning (e.g., EECDH and EDH). The preferred names today are DHE and ECDHE; the other
keywords are supported for backward compatibility.
Keyword Meaning
Keyword Meaning
AES, AESCCM, AESCCM8, AESGCM Cipher suites using AES, AES CCM, and AES GCM.
ARIA, ARIA128, ARIA256 Cipher suites using ARIA.
CAMELLIA, CAMELLIA128, Cipher suites using Camellia. Obsolete.
CAMELLIA256
CHACHA20 Cipher suites using ChaCha20.
eNULL, NULL Cipher suites that don’t use encryption. Insecure.
IDEA Cipher suites using IDEA. Obsolete.
SEED Cipher suites using SEED. Obsolete.
3DES, DES, IDEA, RC2, RC4 No longer supported by default. Obsolete and insecure.
What remains is a number of suites that do not fit into any other category. The bulk of them
are related to the GOST standards, which are relevant for the countries that are part of the
Commonwealth of Independent States, formed after the breakup of the Soviet Union. The
GOST suites are defined but require the GOST engine to be activated. The GOST engine is
not part of the core OpenSSL since version 1.1.0.
Keyword Meaning
@SECLEVEL Configures the security level, which sets minimum security requirements.
@STRENGTH Sorts the current cipher suite list in order of encryption algorithm key length.
aGOST Cipher suites using GOST R 34.10 (either 2001 or 94) for authentication. Requires a
GOST-capable engine.
aGOST01 Cipher suites using GOST R 34.10-2001 authentication.
aGOST94 Cipher suites using GOST R 34.10-94 authentication. Obsolete. Use GOST R 34.10-2001
instead.
kGOST Cipher suites using VKO 34.10 key exchange, specified in RFC 4357.
GOST94 Cipher suites using HMAC based on GOST R 34.11-94.
GOST89MAC Cipher suites using GOST 28147-89 MAC instead of HMAC.
PSK Cipher suites using PSK in any capacity.
Combining Keywords
In most cases, you’ll use keywords by themselves, but it’s also possible to combine them to
select only suites that meet several requirements, by connecting two or more keywords with
The colon character is commonly used to separate keywords, but spaces and commas are
equally acceptable. The following command produces the same output as the previous exam-
ple:
Keyword Modifiers
Keyword modifiers are characters you can place at the beginning of each keyword in order
to change the default action (adding to the list) to something else. The following actions are
supported:
Append
Add suites to the end of the list. If any of the suites are already on the list, they will
remain in their present position. This is the default action, which is invoked when there
is no modifier in front of the keyword.
Delete (-)
Remove all matching suites from the list, potentially allowing some other keyword to
reintroduce them later.
Permanently delete (!)
Remove all matching suites from the list and prevent them from being added later by
another keyword. This modifier is useful for specifying all the suites you never want to
use, making further selection easier and preventing mistakes.
Sorting
The @STRENGTH keyword serves a special purpose: it will not introduce or remove any suites,
but it will sort them in order of descending cipher strength. Automatic sorting is an interesting
idea, but it makes sense only in a perfect world in which cipher suites can actually be compared
by cipher strength alone. In most cases, the highest-strength suites are not typically required.
You often have them in your configuration only to interoperate with picky clients.
Handling Errors
There are two types of errors you might experience while working on your configuration. The
first is a result of a typo or an attempt to use a keyword that does not exist:
$ openssl ciphers -v '@HIGH'
Error in cipher list
140460843755168:error:140E6118:SSL routines:SSL_CIPHER_PROCESS_RULESTR:invalid …
command:ssl_ciph.c:1317:
Performance
As you’re probably aware, computation speed is a significant limiting factor for any crypto-
graphic operation. OpenSSL comes with a built-in benchmarking tool that you can use to
get an idea about a system’s capabilities and limits. You can invoke the benchmark using the
speed command.
If you invoke speed without any parameters, OpenSSL produces a lot of output, little of which
will be of interest. A better approach is to test only those algorithms that are directly relevant
to you. For example, for usage in a secure web server, you might care about the performance
of RSA and ECDSA and will do something like this:
The first part of the resulting output consists of the OpenSSL version number and com-
pile-time configuration. This information is useful for record-keeping and if you’re testing
different versions of OpenSSL:
OpenSSL 1.1.1f 31 Mar 2020
built on: Mon Apr 20 11:53:50 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-P_ODHM/openssl-1.1.1f=. -fstack-protector-strong …
-Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -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 rest of the output contains the benchmark results. Let’s first take a look at the RSA key
operations:
sign verify sign/s verify/s
rsa 512 bits 0.000073s 0.000005s 13736.4 187091.4
rsa 1024 bits 0.000207s 0.000014s 4828.4 71797.6
rsa 2048 bits 0.000991s 0.000045s 1009.1 22220.4
rsa 3072 bits 0.004796s 0.000096s 208.5 10463.5
rsa 4096 bits 0.011073s 0.000165s 90.3 6054.5
rsa 7680 bits 0.090541s 0.000565s 11.0 1769.7
rsa 15360 bits 0.521500s 0.002204s 1.9 453.7
RSA is most commonly used at 2,048 bits. In my results, one CPU of the tested server can per-
form about 1,000 sign (server) operations and 22,000 verify (client) operations every second.
As for ECDSA, it’s typically only used at 256 bits. We can see that at this length, ECDSA can do
10 times as many signatures. On the other hand, it’s slower when it comes to the verifications,
at barely 6,500 operations per second:
sign verify sign/s verify/s
256 bits ecdsa (nistp256) 0.0000s 0.0002s 20508.1 6566.2
384 bits ecdsa (nistp384) 0.0017s 0.0013s 580.4 755.0
521 bits ecdsa (nistp521) 0.0006s 0.0012s 1711.5 840.8
In practice, you care more about the sign operations because servers are designed to provide
services to a great many clients. The clients, on the other hand, are typically communicating
with only a small number of servers at the same time. The fact that ECDSA is slower in this
scenario doesn’t matter much.
What’s this output of speed useful for? You should be able to compare how compile-time
options affect speed or how different versions of OpenSSL compare on the same platform.
Performance 37
If you’re thinking of switching servers, benchmarking OpenSSL can give you an idea of the
differences in computing power. You can also verify that the hardware acceleration is in place.
Using the benchmark results to estimate deployment performance is not straightforward be-
cause of the great number of factors that influence performance in real life. Further, many of
those factors lie outside TLS (e.g., HTTP keep-alive settings, caching, etc.). At best, you can
use these numbers only for a rough estimate.
But before you can do that, you need to consider something else. By default, the speed com-
mand will use only a single process. Most servers have multiple cores, so to find out how many
TLS operations are supported by the entire server, you must instruct speed to use several in-
stances in parallel. You can achieve this with the -multi switch. My server has two cores, so
that’s what I’m going to specify:
As expected, the performance is about two times better. I’m again looking at how many RSA
signatures can be completed per second, because this is the most CPU-intensive cryptographic
operation performed on a server and is thus always the first bottleneck. The result of 1,988
signatures/second (with a 2,048-bit key) tells us that this small server will most definitely
handle hundreds of brand-new TLS connections per second. (We have to assume that the
server will do other things, not only TLS handshakes.) In my case, that’s sufficient—with a
very healthy safety margin. Because I also have session resumption enabled on the server—
and that bypasses public encryption—I know that the performance will be even better.
When testing speed, it’s important to always enable hardware acceleration using the -evp
switch. If you don’t, the results can be vastly different. As an illustration, take a look at the
performance differences on a server that supports AES-NI hardware acceleration. I got the
following with a software-only implementation:
The performance is more than three times better with hardware acceleration:
When you’re looking at the speed of cryptographic operations, you should focus on the prim-
itives you will actually deploy. For example, CBC is obsolete, so you want to use AES in GCM
mode instead. And here we see how the GCM performance is three to four times better:
Then there is ChaCha20-Poly1305, which is a relatively recent addition. Its performance can’t
compete with hardware-accelerated AES, but it doesn’t need to; this authenticated cipher is
designed to be fast on mobile phones. Compare its speed to nonaccelerated AES-128-CBC
instead.
Note
Starting with OpenSSL 3.0, hardware acceleration is always used when supported by
the CPU, irrespective of the presence or absence of the -evp switch.
Creating a Root CA
Creating a new CA involves several steps: configuration, creation of a directory structure and
initialization of the key files, and finally generation of the root key and certificate. This section
describes the process as well as the common CA operations.
Root CA Configuration
Before we can actually create a CA, we need to prepare a configuration file (root-ca.conf) that
will tell OpenSSL exactly how we want things set up. Configuration files aren’t needed most
of the time, during normal usage, but they are essential when it comes to complex operations,
such as root CA creation. OpenSSL configuration files are powerful; before you proceed I
suggest that you familiarize yourself with their capabilities (man config on the command line).
20
OpenSSL CA configuration templates (Bulletproof TLS and PKI GitHub repository, retrieved 20 October 2021)
[ca_dn]
countryName = "GB"
organizationName = "Example"
commonName = "Root CA"
The second part directly controls the CA’s operation. For full information on each setting,
consult the documentation for the ca command (man ca on the command line). Most of the
settings are self-explanatory; we mostly tell OpenSSL where we want to keep our files. Because
this root CA is going to be used only for the issuance of subordinate CAs, I chose to have
the certificates valid for 10 years. For the signature algorithm, the secure SHA256 is used by
default.
The default policy (policy_c_o_match) is configured so that all certificates issued from this CA
have the countryName and organizationName fields that match that of the CA. This wouldn’t
be normally done by a public CA, but it’s appropriate for a private CA:
[ca_default]
home = .
database = $home/db/index
serial = $home/db/serial
crlnumber = $home/db/crlnumber
certificate = $home/$name.crt
private_key = $home/private/$name.key
RANDFILE = $home/private/random
new_certs_dir = $home/certs
unique_subject = no
copy_extensions = none
default_days = 3650
default_crl_days = 365
default_md = sha256
policy = policy_c_o_match
[policy_c_o_match]
countryName = match
Creating a Root CA 41
stateOrProvinceName = optional
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
The third part contains the configuration for the req command, which is going to be used
only once, during the creation of the self-signed root certificate. The most important parts are
in the extensions: the basicConstraints extension indicates that the certificate is a CA, and
keyUsage contains the appropriate settings for this scenario:
[req]
default_bits = 4096
encrypt_key = yes
default_md = sha256
utf8 = yes
string_mask = utf8only
prompt = no
distinguished_name = ca_dn
req_extensions = ca_ext
[ca_ext]
basicConstraints = critical,CA:true
keyUsage = critical,keyCertSign,cRLSign
subjectKeyIdentifier = hash
The fourth part of the configuration file contains information that will be used during the
construction of certificates issued by the root CA. All certificates will be CAs, as indicated
by the basicConstraints extension, but we set pathlen to zero, which means that further
subordinate CAs are not allowed.
All subordinate CAs are going to be constrained, which means that the certificates they is-
sue will be valid only for a subset of domain names and restricted uses. First, the extended-
KeyUsage extension specifies only clientAuth and serverAuth, which is TLS client and server
authentication. Second, the nameConstraints extension limits the allowed hostnames only to
example.com and example.org domain names. In theory, this setup enables you to give control
over the subordinate CAs to someone else but still be safe in knowing that they can’t issue
certificates for arbitrary hostnames. If you wanted, you could restrict each subordinate CA
to a small domain namespace. The requirement to exclude the two IP address ranges comes
from the CA/Browser Forum’s Baseline Requirements, which have a definition for technically
constrained subordinate CAs.21
In practice, name constraints are not entirely practical, because some major platforms don’t
currently recognize the nameConstraints extension. If you mark this extension as critical, such
21
Baseline Requirements (The CA/Browser Forum, retrieved 9 July 2014)
[crl_info]
URI.0 = $crl_url
[issuer_info]
caIssuers;URI.0 = $aia_url
OCSP;URI.0 = $ocsp_url
[name_constraints]
permitted;DNS.0=example.com
permitted;DNS.1=example.org
excluded;IP.0=0.0.0.0/0.0.0.0
excluded;IP.1=0:0:0:0:0:0:0:0/0:0:0:0:0:0:0:0
The fifth and final part of the configuration specifies the extensions to be used with the
certificate for OCSP response signing. In order to be able to run an OCSP responder, we gen-
erate a special certificate and delegate the OCSP signing capability to it. This certificate is not
a CA, which you can see from the extensions:
[ocsp_ext]
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
extendedKeyUsage = OCSPSigning
keyUsage = critical,digitalSignature
subjectKeyIdentifier = hash
Creating a Root CA 43
$ touch db/index
$ openssl rand -hex 16 > db/serial
$ echo 1001 > db/crlnumber
Note
When creating a new CA certificate, it’s important to initialize the certificate serial
numbers with a random number generator, as I do in this section. This is very useful
if you ever end up creating and deploying multiple CA certificates with the same
distinguished name (common if you make a mistake and need to start over); conflicts
will be avoided, because the certificates will have different serial numbers.
Root CA Generation
We take two steps to create the root CA. First, we generate the key and the CSR. All the neces-
sary information will be picked up from the configuration file when we use the -config switch:
$ openssl req -new \
-config root-ca.conf \
-out root-ca.csr \
-keyout private/root-ca.key
In the second step, we create a self-signed certificate. The -extensions switch points to the
ca_ext section in the configuration file, which activates the extensions that are appropriate
for a root CA:
$ openssl ca -selfsign \
-config root-ca.conf \
-in root-ca.csr \
-out root-ca.crt \
-extensions ca_ext
Root CA Operations
To generate a CRL from the new CA, use the -gencrl switch of the ca command:
$ openssl ca -gencrl \
-config root-ca.conf \
-out root-ca.crl
To issue a certificate, invoke the ca command with the desired parameters. It’s important that
the -extensions switch points to the correct section in the configuration file (e.g., you don’t
want to create another root CA).
$ openssl ca \
-config root-ca.conf \
-in sub-ca.csr \
-out sub-ca.crt \
-extensions sub_ca_ext
To revoke a certificate, use the -revoke switch of the ca command; you’ll need to have a copy
of the certificate you wish to revoke. Because all certificates are stored in the certs/ directory,
you only need to know the serial number. If you have a distinguished name, you can look for
the serial number in the database.
Choose the correct reason for the value in the -crl_reason switch. The value can be one of
the following: unspecified, keyCompromise, CACompromise, affiliationChanged, superseded,
cessationOfOperation, certificateHold, and removeFromCRL.
$ openssl ca \
Creating a Root CA 45
-config root-ca.conf \
-revoke certs/1002.pem \
-crl_reason keyCompromise
Second, use the root CA to issue a certificate. The value of the -extensions switch specifies
ocsp_ext, which ensures that extensions appropriate for OCSP signing are set. I reduced the
lifetime of the new certificate to 365 days (from the default of 3,650). Because these OCSP
certificates don’t contain revocation information, they can’t be revoked. For that reason, you
want to keep the lifetime as short as possible. A good choice is 30 days, provided you are
prepared to generate a fresh certificate that often:
$ openssl ca \
-config root-ca.conf \
-in root-ocsp.csr \
-out root-ocsp.crt \
-extensions ocsp_ext \
-days 30
Now you have everything ready to start the OCSP responder. For testing, you can do it from
the same machine on which the root CA resides. However, for production you must move the
OCSP responder key and certificate elsewhere:
$ openssl ocsp \
-port 9080
-index db/index \
-rsigner root-ocsp.crt \
-rkey private/root-ocsp.key \
-CA root-ca.crt \
-text
You can test the operation of the OCSP responder using the following command line:
$ openssl ocsp \
-issuer root-ca.crt \
-CAfile root-ca.crt \
In the output, verify OK means that the signatures were correctly verified, and good means
that the certificate hasn’t been revoked.
Response verify OK
root-ocsp.crt: good
This Update: Jul 9 18:45:34 2014 GMT
Creating a Subordinate CA
The process of subordinate CA generation largely mirrors the root CA process. In this section,
I will only highlight the differences where appropriate. For everything else, refer to the pre-
vious section.
Subordinate CA Configuration
To generate a configuration file (sub-ca.conf) for the subordinate CA, start with the file we
used for the root CA and make the changes listed in this section. We’ll change the name to sub-
ca and use a different distinguished name. We’ll put the OCSP responder on a different port,
but only because the ocsp command doesn’t understand virtual hosts. If you used a proper
web server for the OCSP responder, you could avoid using special ports altogether. The default
lifetime of new certificates will be 365 days, and we’ll generate a fresh CRL once every 30 days.
The change of copy_extensions to copy means that extensions from the CSR will be copied
into the certificate, but only if they are not already set in our configuration. With this change,
whoever is preparing the CSR can put the required alternative names in it, and the information
from there will be picked up and placed in the certificate. This feature is somewhat dangerous
(you’re allowing someone else to have limited direct control over what goes into a certificate),
but I think it’s fine for smaller environments:
[default]
name = sub-ca
ocsp_url = http://ocsp.$name.$domain_suffix:9081
[ca_dn]
countryName = "GB"
organizationName = "Example"
commonName = "Sub CA"
[ca_default]
default_days = 365
default_crl_days = 30
copy_extensions = copy
Creating a Subordinate CA 47
At the end of the configuration file, we’ll add two new profiles, one each for client and server
certificates. The only difference is in the keyUsage and extendedKeyUsage extensions. Note that
we specify the basicConstraints extension but set it to false. We’re doing this because we’re
copying extensions from the CSR. If we left this extension out, we might end up using one
specified in the CSR:
[server_ext]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
crlDistributionPoints = @crl_info
extendedKeyUsage = clientAuth,serverAuth
keyUsage = critical,digitalSignature,keyEncipherment
subjectKeyIdentifier = hash
[client_ext]
authorityInfoAccess = @issuer_info
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
crlDistributionPoints = @crl_info
extendedKeyUsage = clientAuth
keyUsage = critical,digitalSignature
subjectKeyIdentifier = hash
After you’re happy with the configuration file, create a directory structure following the same
process as for the root CA. Just use a different directory name, for example, sub-ca.
Subordinate CA Generation
As before, we take two steps to create the subordinate CA. First, we generate the key and the
CSR. All the necessary information will be picked up from the configuration file when we use
the -config switch.
$ openssl req -new \
-config sub-ca.conf \
-out sub-ca.csr \
-keyout private/sub-ca.key
In the second step, we get the root CA to issue a certificate. The -extensions switch points
to the sub_ca_ext section in the configuration file, which activates the extensions that are
appropriate for the subordinate CA.
$ openssl ca \
-config root-ca.conf \
-in sub-ca.csr \
-out sub-ca.crt \
Subordinate CA Operations
To issue a server certificate, process a CSR while specifying server_ext in the -extensions
switch:
$ openssl ca \
-config sub-ca.conf \
-in server.csr \
-out server.crt \
-extensions server_ext
To issue a client certificate, process a CSR while specifying client_ext in the -extensions
switch:
$ openssl ca \
-config sub-ca.conf \
-in client.csr \
-out client.crt \
-extensions client_ext
Note
When a new certificate is requested, all its information will be presented to you for
verification before the operation is completed. You should always ensure that every-
thing is in order, but especially if you’re working with a CSR that someone else
prepared. Pay special attention to the certificate distinguished name and the basic-
Constraints and subjectAlternativeName extensions.
CRL generation and certificate revocation are the same as for the root CA. The only thing
different about the OCSP responder is the port; the subordinate CA should use 9081 instead.
It’s recommended that the responder uses its own certificate, which avoids keeping the sub-
ordinate CA on a public server.
Creating a Subordinate CA 49
2 Testing TLS with OpenSSL
Due to the large number of protocol features and implementation quirks, it’s sometimes
difficult to determine the exact configuration and features of secure servers. Although many
tools exist for this purpose, it’s often difficult to know exactly how they work, and that some-
times makes it difficult to fully trust their results. Even though I spent years testing secure
servers and have access to good tools, when I really want to understand what is going on, I
resort to using OpenSSL and Wireshark. I am not saying that you should use OpenSSL for
everyday testing; on the contrary, you should find an automated tool that you trust. For online
testing, I recommend Hardenize;1 for offline work, consider testssl.sh.2 But when you really
need to be certain of something, the only way is to get your hands dirty with OpenSSL.
1
Hardenize (retrieved 31 August 2020)
2
testssl.sh (retrieved 29 August 2020)
51
enable-ssl2 \
enable-ssl3 \
enable-weak-ssl-ciphers
Throughout this chapter, I will refer to these two versions of OpenSSL as new and old. That’s
how you’ll know which version to use for the testing. Refer to the previous chapter for more
information on how to configure and install OpenSSL.
Notice that you had to supply the hostname twice. The -connect switch is used to establish
the TCP connection, but -servername is used to specify the hostname sent at the TLS level.
Starting with OpenSSL 1.1.1, the s_client tool automatically configures the latter. You’ll still
need to use the -servername switch if (1) you’re using an earlier version of OpenSSL, (2) you’re
connecting to an IP address, or (3) the TLS host needs to be different. Use the -noservername
switch to avoid sending hostname information in the TLS handshake.
Once you type the command, you’re going to see a lot of diagnostic output (more about that
in a moment) followed by an opportunity to type whatever you want. Because we’re talking to
an HTTP server, the most sensible thing to do is to submit an HTTP request. In the following
example, I use a HEAD request because it instructs the server not to send the response body:
HEAD / HTTP/1.0
Host: www.feistyduck.com
HTTP/1.1 200 OK
Date: Mon, 24 Aug 2020 16:38:02 GMT
Server: Apache
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Cache-control: no-cache, must-revalidate
Content-Type: text/html;charset=UTF-8
Transfer-Encoding: chunked
Set-Cookie: JSESSIONID=882D48C8842EA82E3F3AFACC4425A695; Path=/; Secure; HttpOnly
Connection: close
read:errno=0
Now we know that the TLS communication layer is working: we got through to the HTTP
server, submitted a request, and received a response back. Let’s go back to the diagnostic out-
put. The first couple of lines will show the information about the server certificate:
CONNECTED(00000003)
depth=2 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Certification Authority
verify return:1
depth=1 C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Domain Validation Secure Server CA
verify return:1
depth=0 OU = Domain Control Validated, OU = PositiveSSL, CN = www.feistyduck.com
verify return:1
The next section in the output lists all the certificates presented by the server in the order in
which they were delivered:
Certificate chain
0 s:OU = Domain Control Validated, OU = PositiveSSL, CN = www.feistyduck.com
i:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Domain Validation Secure Server CA
1 s:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Domain Validation Secure Server CA
i:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Certification Authority
For each certificate, the first line shows the subject and the second line shows the issuer in-
formation.
This part is very useful when you need to see exactly what certificates are sent; browser
certificate viewers typically display reconstructed certificate chains that can be almost com-
pletely different from the presented ones. To determine if the chain is nominally correct, you
might wish to verify that the subjects and issuers match. You start with the leaf (web serv-
er) certificate at the top, and then you go down the list, matching the issuer of the current
certificate to the subject of the next. The last issuer you see can point to some root certificate
that is not in the chain, or—if the self-signed root is included—it can point to itself.
The next item in the output is the server certificate; it’s a lot of text, but I’m going to remove
most of it for brevity:
Note
By default, the s_client tool shows just the leaf certificate. If you wish to obtain the
entire chain, use the -showcerts switch.
If you want to have a better look at the certificate, you’ll first need to copy it from the output
and store it in a separate file. I’ll discuss how to do that in the next section.
The following is a lot of information about the TLS connection, most of which is self-explana-
tory:
---
No client certificate CA names sent
Peer signing digest: SHA512
Peer signature type: RSA
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3624 bytes and written 446 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES128-GCM-SHA256
Session-ID: 73FC4831AF053C46291C2D8CC90BF7F1D5B12178E488FBB4DC49A302B870E8DE
Session-ID-ctx:
Master-Key: E60DA9C6669C2C7DFFD8A3AD2CD17405CC0B9B69C4184469D779A9BA19A6FD4B3D6…
02A023BD8B23F8D9A9FF2CBB5DDF7
PSK identity: None
The most important information here is the protocol version (TLS 1.2) and cipher suite used
(ECDHE-RSA-AES128-GCM-SHA256). Do note that protocol information appears in two locations,
which is potentially confusing when different versions are shown. The first location describes
the minimum protocol requirement with the negotiated cipher suite, while the second loca-
tion points to the actual protocol version currently being negotiated. You will see a difference
in protocol versions with some older cipher suites—for example:
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES128-SHA
The selected suite could be used with SSL 3.0, but it’s used with TLS 1.2 on this connection:
Protocol : TLSv1.2
Cipher : DHE-RSA-AES128-SHAs
You can also determine that the server has issued to you a session ID and a TLS session ticket
(a way of resuming sessions without having the server maintain state) and that secure rene-
gotiation is supported.
Note
If you’re connecting to a TLS 1.3 server, the output may be different. Sometimes you
will observe less information initially, with additional information arriving later in
bursts. This behavior depends on the implementation and reflects the changes in TLS
1.3, which transmits session tickets as separate protocol messages that are sent only
Certificate Verification
Just because you are able to connect to a TLS server, that doesn’t mean that the service is
configured correctly, even if the server supports all the right protocols and cipher suites. It is
equally important that the configured certificate matches the correct DNS names.
By default, the s_client tool reports but otherwise ignores certificate issues. Further, before
you begin to trust its judgment you need to be confident that it can recognize a valid certificate
when it sees one. This is especially true when you’re using a custom-compiled binary.
In the example from the previous section, the verification status code (shown on the penulti-
mate line) was 0, which means that the verification has been successful. If you’re connecting
to a server that has a valid public certificate but you see status 20 instead, that probably means
that trusted roots haven’t been correctly configured:
At this point, if you don’t wish to fix your OpenSSL installation, you can instead use the -
CApath switch to point to the location where the roots are kept. For example:
If you instead have a single file with the roots in it, use the -CAfile switch:
Even if you get a successful status code at this point, that doesn’t mean that the certificate
is correctly configured. That’s because the s_client tool doesn’t check that the certificate
is correct for the given hostname; you have to tell it to do that manually and tell it which
hostname to use:
Otherwise, you’ll see the familiar status code 0. In the rare instance that you need to verify a
certificate that has been issued for an IP address instead of a hostname, you’ll need to use the
-verify_ip switch for the verification.
At the time of writing, the supported protocols in recent OpenSSL releases are smtp, pop3,
imap, ftp, xmpp, xmpp-server, irc, postgres, mysql, lmtp, nntp, sieve, and ldap. There is less
choice with OpenSSL 1.0.2g: smtp, pop3, imap, ftp, and xmpp.
Some protocols require the client to provide their names. For example, for SMTP, OpenSSL
will use mail.example.com by default, but you can specify the correct value with the -name
switch. If you’re testing XMPP, you may need to specify the correct server name; you can do
this with the -xmpphost switch.
The purpose of the echo command at the beginning is to separate your shell from s_client.
If you don’t do that, s_client will wait for your input until the server times out (which may
potentially take a very long time).
By default, s_client will print only the leaf certificate; if you want to print the entire chain,
give it the -showcerts switch. With that switch enabled, the previous command line will place
all the certificates in the same file.
$ echo | openssl s_client -showcerts -connect www.feistyduck.com:443 2>&1 | sed …
--quiet '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > feistyduck.chain
Another useful trick is to pipe the output of s_client directly to the x509 tool. The following
command shows detailed server information, along with its SHA256 fingerprint:
$ echo | openssl s_client -connect www.feistyduck.com:443 2>&1 | openssl x509 …
-noout -text -fingerprint -sha256
Note
Connecting to remote TLS servers and reviewing their certificates is a pretty common
operation, but you shouldn’t spend your time remembering and typing these long
commands. Instead, invest in writing a couple of shell functions that will package
this functionality into easy-to-use commands.
If you need to test support for specific protocol versions, you have two options. You can explic-
itly choose one protocol to test by supplying one of the -ssl2, -ssl3, -tls1, -tls1_1, -tls1_2,
or tls1_3 switches. Naturally, each switch requires support for a specific protocol version in
the testing tool. If you want to exclude a particular protocol from the testing, there is a family
of switches that disable protocols (e.g., -no_tls_1_2 for TLS 1.2). Sometimes that may be the
better approach. Starting with OpenSSL 1.1.0, there are two new options, -min_protocol and
-max_protocol, which control the minimum and maximum protocol version, respectively.
For example, here’s the output you might get when testing a server that doesn’t support a
certain protocol version:
$ openssl s_client -connect www.example.com:443 -tls1_2
CONNECTED(00000003)
140455015261856:error:1408F10B:SSL routines:SSL3_GET_RECORD:wrong version …
number:s3_pkt.c:340:
---
no peer certificate available
---
3
Do note that when connecting to a TLS 1.3 server, the protocol information may not appear immediately on the connection. Instead, it will be
printed when the session ticket is received, which may take a couple of seconds with some servers.
Understanding if a server supports SSL 2.0 may sometimes require more work, due to the fact
that this old and very insecure version of the SSL protocol uses a different handshake from that
used from SSL 3.0 onward. Although servers that support only SSL 2.0 should now be very
rare, to check this eventuality, you’ll need to submit a separate check using the -ssl2 switch.
Another protocol difference is that SSL 2.0 servers are sometimes seen without any configured
cipher suites. In that case, although SSL 2.0 is supported, technically speaking, any handshake
attempts will still fail. You should treat this situation as misconfiguration.
The output will naturally be different if you pick a suite that is not supported:
$ echo | openssl s_client -connect www.hardenize.com:443 -tls1_3 -ciphersuites TLS…
_AES_128_CCM_SHA256 2>/dev/null | grep New
New, (NONE), Cipher is (NONE)
When you’re testing the configuration of TLS 1.2 and earlier protocol versions, use the -cipher
switch in combination with -no_tls1_3 (assuming you’re using a version of OpenSSL that
supports TLS 1.3):
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher AESGCM …
2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES128-GCM-SHA256
As you can see in the previous example, when testing TLS 1.2 and earlier you don’t have to
specify only one cipher suite, but in that case you will need to observe what has been nego-
tiated. If you want to probe further, you can always tweak the command line to remove the
previously negotiated suite:
$ echo | openssl s_client -connect www.hardenize.com:443 -no_tls1_3 -cipher …
'AESGCM:!ECDHE-ECDSA-AES128-GCM-SHA256' 2>/dev/null | grep New
New, TLSv1.2, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384
Even though you won’t be testing for a great many suites manually, there is a quick way to
determine if a particular server supports any of the many bad cryptographic primitives. To
do this, use your old OpenSSL version and list all the bad cipher suite keywords, like this:
$ echo | openssl s_client -connect example.com:443 -cipher '3DES DES RC2 RC4 IDEA …
SEED CAMELLIA MD5 aNULL eNULL EXPORT LOW' 2>/dev/null | grep New
New, TLSv1/SSLv3, Cipher is DHE-RSA-CAMELLIA256-SHA
Another good test is to see if a server supports the RSA key exchange that doesn’t support
forward secrecy:
$ echo | /opt/openssl-1.0.2g/bin/openssl s_client -connect example.com:443 -cipher …
kRSA 2>/dev/null | grep New
New, TLSv1/SSLv3, Cipher is AES128-GCM-SHA256
Ideally, you’d get a handshake failure here, but it’s not terrible if you don’t, provided the server
uses the RSA key exchange only as a matter of last resort. You can check this by offering suites
with forward secrecy as your least preferred option:
Note
Because cipher suite preference doesn’t matter much with TLS 1.3, some stacks don’t
even support it with this protocol, even if they do with earlier protocol versions. Thus,
for the best results, you will want to test separately for TLS 1.3 and everything else—
or separately for every supported protocol. This is another case in which automation
is the better choice.
To test for server suite preference, you first need to have some idea of what suites are supported.
For example, you could have the complete list of supported suites. Alternatively, you can probe
the server with different suite types—for example, those that use ECDHE versus DHE or RSA
key exchange.
With two suites in hand, you need to initiate two connections, first offering one of the suites
as your first choice, then the other:
If you see the same suite negotiated on both connections, that means that the server is
configured to actively select negotiated suites. Otherwise, it isn’t. The server in the previous
example is one of those TLS 1.3 servers that doesn’t enforce preference. That very same server
4
Out of five cipher suites that TLS 1.3 supports, OpenSSL enables only three by default. The remaining two are CCM suites, which are intended
for use with embedded systems, in which every little bit of performance and battery life matters. These suites are not worth using for other use
cases—especially the CCM_8 suite, which reduces the strength of authentication.
When it comes to server suite preference testing, the ChaCha20 suites are best avoided. This
is because some servers support another type of preference, where they treat AES-GCM and
ChaCha20 suites as equal in terms of security and respect client preference as a special case.
The idea is that the client will prefer the faster cipher suite, which is typically ChaCha20 for
mobile devices and AES-GCM for desktops.
That said, with servers that support this type of preference, you may want to test if it’s working
correctly. To do that, you’ll need to use three supported cipher suites and three tests. The pur-
pose of the first two tests is to establish that the server selects its favorite suite when ChaCha20
is not involved:
If you see that the server responds with the same suite in both cases, you can submit another
test with a supported ChaCha20 suite first. If you see the server selecting it, you know it’s
configured to support the client-preferred suite:
On success, you will see the named group in the output, because that’s the group that was se-
lected for the handshake. On failure, you may see no output, which means that the handshake
failed. Alternatively, the server, unable to negotiate an ECDHE suite, may fall back to a DHE
suite, indicated by the following output:
If you need to test for named group preference, you need to offer two or more named groups,
with your preferred one last. If you see it negotiated, that will mean that the server actively
chooses the group it considers most appropriate. Use colons to separate the groups and be
aware that the names are case-sensitive.
Note
You can get the complete list of elliptic curves supported by OpenSSL using the ec-
param tool and the -list_curves switch. To that list, add X25519 and X448. Support
for finite field groups is currently not available but should arrive with OpenSSL 3.0.
5
RFC 7919, which came out in 2016, redefined the elliptic_curves TLS extension to support finite field groups and changed the extension
name to supported_groups. Although this extension applies to TLS 1.2, support for it is not widespread.
This output contains two endorsements, one per certificate. Having two endorsements is not
unusual. For example, perhaps you might have a service that uses two certificates (e.g., one
with an RSA key and another with an ECDSA key), or you have a backup certificate, or you’re
simply in a transitional period when you’re switching certificates. The three numbers at the
beginning indicate that the endorsement targets the certificate directly (3) via its public key
(1) and a SHA256 hash (1). The rest of the data is the hash itself.
To test, you connect to the SMTP service while providing the DANE data using the -
dane_tlsa_domain and -dane_tlsa_rrdata switches:
6
New Adventures in DNSSEC and DANE (Jan Schaumann, retrieved 2 October 2020)
If the verification is successful, you will see something like this in the output:
---
SSL handshake has read 5209 bytes and written 433 bytes
Verification: OK
Verified peername: *.protonmail.ch
DANE TLSA 3 1 1 ...8f7d10a4469a947acc8399e1 matched EE certificate at depth 0
---
If you’d like to test for validation failure, just break the supplied hash. The result will be similar
to the following output:
---
SSL handshake has read 5209 bytes and written 433 bytes
Verification error: No matching DANE TLSA records
---
For the best results, when testing DANE in this way, always provide all known TLSA records
(one per -dane_tlsa_rrdata switch). If you do, services that use multiple certificates simul-
taneously will check out no matter what certificate is negotiated. For TLS 1.2 and earlier, it’s
possible to force a particular certificate via a choice of client-supported cipher suites (the -ci-
pher switch). TLS 1.3 suites are different, and for this protocol version you would need to use
the -sigalgs switch with a value such as ecdsa_secp256r1_sha256 or rsa_pss_rsae_sha256.
Note
Due to a bug in OpenSSL, at the time of writing session resumption testing doesn’t
work in combination with TLS 1.3. Until the bug is resolved,7 the best you can do is
test the earlier protocol versions. Use the -no_tls1_3 switch.
The previous command will produce a sea of output, most of which you won’t care about.
The key parts are the information about new and reused sessions. There should be only one
new session at the beginning, indicated by the following line:
7
s_client -reconnect Option Is Broken with TLSv1.3 (OpenSSL, retrieved 31 August 2020)
Most of the time, you don’t want to look at all that output and want an answer quickly. You
can get it using the following command line:
$ echo | openssl s_client -connect www.feistyduck.com:443 -reconnect 2> /dev/null …
| grep 'New\|Reuse'
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Note
If you don’t want to include session tickets in the test—for example, because not all
clients support this feature yet—you can disable this method of resumption using
the -no_ticket switch. This option doesn’t apply to TLS 1.3.
Finally, to connect again using the same session state, use the -sess_in switch:
$ openssl s_client -connect www.feistyduck.com:443 -sess_in sess.pem
Keeping the state across connections in this way gives you more control and enables you to
completely change connection parameters from one connection to another. For example, you
could connect to one server on your first attempt, then another server on your second. This
may be of use when you need to test if session resumption is correctly implemented on a
web server cluster. Manual control of your connections allows you to spread them over time,
perhaps testing for session timeouts and ticket key rotation.
The first certificate in the output will be the one belonging to the server. If the certificate chain
is properly configured, the second certificate will be that of the issuer. To confirm, check that
the issuer of the first certificate matches the subject of the second:
Certificate chain
0 s:OU = Domain Control Validated, OU = PositiveSSL, CN = www.feistyduck.com
i:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Domain Validation Secure Server CA
-----BEGIN CERTIFICATE-----
MIIFUzCCBDugAwIBAgIRAPR/CbWZEksfCIRqxNcesPIwDQYJKoZIhvcNAQELBQAw
[...]
zbQXjVsc3E1THfFZWRzDPsU4fN/1iGlbrcAWa2sFfhJXrCDfAowFJ8A1n9jMiNEG
WfQfGgA2ar2xUtsqA7Re6XlXOlwBPuQ=
-----END CERTIFICATE-----
1 s:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Domain Validation Secure Server CA
i:C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA Limited, CN = …
COMODO RSA Certification Authority
-----BEGIN CERTIFICATE-----
MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
[...]
If the second certificate isn’t the right one, check the rest of the chain; some servers don’t serve
the chain in the correct order. If you can’t find the issuer certificate in the chain, you’ll have
to find it somewhere else. One way to do that is to look for the Authority Information Access
extension in the leaf certificate:
If the CA Issuers information is present, it should contain the URL of the issuer certificate. If
the issuer certificate information isn’t available, you can try to open the site in a browser, let
it reconstruct the chain, and download the issuing certificate from its certificate viewer. If all
that fails, you can look for the certificate in your trust store or visit the CA’s web site.
If you already have the certificates and just need to know the address of the OCSP responder,
use the -ocsp_uri switch with the x509 command as a shortcut:
You want to look for two things in the response. First, check that the response itself is valid
(Response verify OK in the previous example), and second, check what the response said.
When you see good as the status, that means that the certificate hasn’t been revoked. The status
will be revoked for revoked certificates.
Note
The warning message about the missing nonce is telling you that OpenSSL wanted
to use a nonce as a protection against replay attacks, but the server in question did
not reply with one. This generally happens because CAs want to improve the per-
formance of their OCSP responders. When they disable the nonce protection (the
standard allows it), OCSP responses can be produced (usually in batch), cached, and
reused for a period of time.
You may encounter OCSP responders that do not respond successfully to the previous com-
mand line. The following suggestions may help in such situations.
Do not request a nonce
Some servers cannot handle nonce requests and respond with errors. OpenSSL will
request a nonce by default. To disable nonces, use the -no_nonce command-line switch.
Supply a Host request header
Although most OCSP servers respond to HTTP requests that don’t specify the correct
hostname in the Host header, some don’t. If you encounter an error message that in-
cludes an HTTP error code (e.g., 404), try adding the hostname to your OCSP request.
You can do this with the help of the -header switch.
With the previous two points in mind, the final command to use is the following:
$ openssl ocsp -issuer issuer.crt -cert fd.crt -url http://ocsp.comodoca.com …
-CAfile issuer.crt -no_nonce -header Host ocsp.comodoca.com
The OCSP-related information will be displayed at the very beginning of the connection out-
put. For example, with a server that does not support stapling you will see this line near the
top of the output:
CONNECTED(00000003)
OCSP response: no response sent
With a server that does support stapling, you will see the entire OCSP response in the output:
OCSP Response Data:
OCSP Response Status: successful (0x0)
Response Type: Basic OCSP Response
Version: 1 (0x0)
Responder Id: 90AF6A3A945A0BD890EA125673DF43B43A28DAE7
Produced At: Aug 30 22:35:12 2020 GMT
Responses:
Certificate ID:
Hash Algorithm: sha1
Issuer Name Hash: 7AE13EE8A0C42A2CB428CBE7A605461940E2A1E9
Issuer Key Hash: 90AF6A3A945A0BD890EA125673DF43B43A28DAE7
Serial Number: F47F09B599124B1F08846AC4D71EB0F2
Cert Status: good
This Update: Aug 30 22:35:12 2020 GMT
Next Update: Sep 6 22:35:12 2020 GMT
The certificate status good means that the certificate has not been revoked.
Verify that the CRL is valid (i.e., signed by the issuer certificate):
$ openssl crl -in comodo.crl -inform DER -CAfile issuer.crt -noout
verify OK
Now, determine the serial number of the certificate you wish to check:
$ openssl x509 -in fd.crt -noout -serial
serial=F47F09B599124B1F08846AC4D71EB0F2
At this point, you can convert the CRL into a human-readable format and inspect it manually:
$ openssl crl -in comodo.crl -inform DER -text -noout
Certificate Revocation List (CRL):
Version 2 (0x1)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = GB, ST = Greater Manchester, L = Salford, O = COMODO CA …
Limited, CN = COMODO RSA Domain Validation Secure Server CA
Last Update: Aug 31 07:52:03 2020 GMT
Next Update: Sep 7 07:52:03 2020 GMT
CRL extensions:
X509v3 Authority Key Identifier:
keyid:90:AF:6A:3A:94:5A:0B:D8:90:EA:12:56:73:DF:43:B4:3A:28:DA:E7
The CRL starts with some metadata, which is followed by a list of revoked certificates, and
it ends with a signature (which we verified in the previous step). If the serial number of the
server certificate is on the list, that means it had been revoked.
If you don’t want to look for the serial number visually (some CRLs can be quite long), grep
for it, but be careful that your formatting matches that used by the crl tool. For example:
$ openssl crl -in comodo.crl -inform DER -text -noout | grep F47F09B599124B1F08846A…
C4D71EB0F2
Testing Renegotiation
In TLS, renegotiation is a failed feature that was responsible for several protocol weaknesses,
some of which are quite easy to exploit. TLS 1.3 no longer supports renegotiation, but there
are still older servers out there that support it with earlier protocol revisions.
The s_client tool has a couple of features that can assist you with manual testing of renego-
tiation. First of all, when you connect, the tool will report if the remote server supports secure
renegotiation. This is because a server that supports secure renegotiation indicates its support
for it via a special TLS extension that is exchanged during the handshake phase. When support
is available, the output may look like this:
New, TLSv1/SSLv3, Cipher is AES256-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
[...]
Note
Because TLS 1.3 doesn’t support renegotiation, the s_client tool will always give a
negative answer if this protocol version is negotiated. To ensure reliable results, use
the -no_tls1_3 switch to force negotiation of an earlier protocol version.
When renegotiation is taking place, the server will send its certificates to the client again.
You can see the verification of the certificate chain in the output. The next line after that
continues with the Host request header. Seeing the web server’s response is the proof that
renegotiation is supported. Because of the various ways the renegotiation issue was addressed
in various versions of SSL/TLS libraries, servers that do not support renegotiation may break
the connection or may keep it open but refuse to continue to talk over it (which usually results
in a timeout).
A server that does not support renegotiation will flatly refuse the second handshake on the
connection:
HEAD / HTTP/1.0
Testing Renegotiation 73
R
RENEGOTIATING
140003560109728:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake …
failure:s3_pkt.c:592:
At the time of writing, the default behavior for OpenSSL is to connect to servers that don’t
support secure renegotiation; it will also accept both secure and insecure renegotiation, opting
for whatever the server is able to do. If renegotiation is successful with a server that doesn’t
support secure renegotiation, you will know that the server supports insecure client-initiated
renegotiation.
Note
The most reliable way to test for insecure renegotiation is to use the method described
in this section, but with a version of OpenSSL that was released before the discovery
of insecure renegotiation (e.g., 0.9.8k). I mention this because there is a small number
of servers that support both secure and insecure renegotiation. This vulnerability is
difficult to detect with modern versions of OpenSSL, which always prefer the secure
option.
8
Bugs in Heartbleed detection scripts (Shannon Simpson and Adrian Hayter, 14 April 2014)
A server that does not return the heartbeat extension is not vulnerable to Heartbleed. To test if
a server responds to heartbeat requests, use the -msg switch to request that protocol messages
are shown, connect to the server, wait until the handshake completes, then type B and press
return:
$ openssl s_client -connect www.feistyduck.com:443 -tlsextdebug -msg
[...]
---
B
HEARTBEATING
>>> TLS 1.2 [length 0025], HeartbeatRequest
01 00 12 00 00 3c 83 1a 9f 1a 5c 84 aa 86 9e 20
c7 a2 ac d7 6f f0 c9 63 9b d5 85 bf 9a 47 61 27
d5 22 4c 70 75
<<< TLS 1.2 [length 0025], HeartbeatResponse
02 00 12 00 00 3c 83 1a 9f 1a 5c 84 aa 86 9e 20
c7 a2 ac d7 6f 52 4c ee b3 d8 a1 75 9a 6b bd 74
f8 60 32 99 1c
read R BLOCK
This output shows a complete heartbeat request and response. The second and third bytes
in both heartbeat messages specify payload length. We submitted a payload of 18 bytes (12
hexadecimal) and the server responded with a payload of the same size. In both cases there
were also additional 16 bytes of padding. The first two bytes in the payload make the sequence
number, which OpenSSL uses to match responses to requests. The remaining payload bytes
and the padding are just random data.
To detect a vulnerable server, you’ll have to prepare a special version of OpenSSL that sends
an incorrect payload length. Vulnerable servers take the declared payload length and respond
with that many bytes irrespective of the length of the actual payload provided.
At this point, you have to decide if you want to build an invasive test (which exploits the
server by retrieving some data from the process) or a noninvasive test. This will depend on
your circumstances. If you have permission for your testing activities, use the invasive test.
With it, you’ll be able to see exactly what is returned, and there won’t be room for errors.
For example, some versions of GnuTLS support Heartbeat and will respond to requests with
incorrect payload length, but they will not actually return server data. A noninvasive test can’t
reliably diagnose that situation.
The following patch against OpenSSL 1.0.1h creates a noninvasive version of the test:
--- t1_lib.c.original 2014-07-04 17:29:35.092000000 +0100
+++ t1_lib.c 2014-07-04 17:31:44.528000000 +0100
@@ -2583,6 +2583,7 @@
#endif
To build a noninvasive test, increase payload length by up to 16 bytes, or the length of the
padding. When a vulnerable server responds to such a request, it will return the padding
but nothing else. To build an invasive test, increase the payload length by, say, 32 bytes. A
vulnerable server will respond with a payload of 50 bytes (18 bytes sent by OpenSSL by default,
plus your 32 bytes) and send 16 bytes of padding. By increasing the declared length of the
payload in this way, a vulnerable server will return up to 64 KB of data. A server not vulnerable
to Heartbleed will not respond.
To produce your own Heartbleed testing tool, unpack a fresh copy of OpenSSL source code,
edit ssl/t1_lib.c to make the change as in the patch, compile as usual, but don’t install. The
resulting openssl binary will be placed in the apps/ subdirectory. Because it is statically com-
piled, you can rename it to something like openssl-heartbleed and move it to its permanent
location.
Here’s an example of the output you’d get with a vulnerable server that returns 16 bytes of
server data (in bold):
B
HEARTBEATING
>>> TLS 1.2 [length 0025], HeartbeatRequest
01 00 32 00 00 7c e8 f5 62 35 03 bb 00 34 19 4d
57 7e f1 e5 90 6e 71 a9 26 85 96 1c c4 2b eb d5
If you want to see more data retrieved in a single response, increase the payload length, re-
compile, and test again. Alternatively, to retrieve another batch of the same size, use the B
command again.
Servers that support export suites might actually offer even weaker DH parameters. To check
for that possibility, connect using your old OpenSSL9 while offering only export DHE suites:
$ openssl s_client -connect www.feistyduck.com:443 -cipher kEDH+EXPORT
This command should fail with well-configured servers. Otherwise, you’ll probably see the
server offering to negotiate insecure 512-bit DH parameters.
9
Support for EXPORT cipher suites was removed in OpenSSL 1.1.0.