I had a task to create a container image based on Debian 12 (Bookworm) which had the OpenSSL FIPS 140-2 module. By default, OpenSSL version 3.0.11 (current version on Debian 12 as of writing) does not have the FIPS module built in it. OpenSSL 3.0 requires that the module be built from the version that has a valid FIPS certificate, which was 3.0.9 at the time of writing. A few things required to get this done, - OpenSSL 3.0 installed from deb package - Locally built OpenSSL 3.0.9 from source code - Modified openssl.cnf to enable FIPS mode - Set environment variable OPENSSL_CONF # OpenSSL 3.0 installed from deb package $ apt update && apt install --no-install-recommends --no-install-suggests -y openssl # Locally built OpenSSL 3.0.9 from source code These steps are simplified specific version of the process provided in [Need steps to compile openssl-debian-openssl-3.0.11-1_deb12u2 with FIPS complaint openssl-3.0.8](https://github.com/openssl/openssl/discussions/23154#discussioncomment-7966194). We build everything but only install the FIPS module. $ curl -O https://www.openssl.org/source/old/3.0/openssl-3.0.9.tar.gz $ tar xvzf openssl-3.0.9.tar.gz $ cd openssl-3.0.9 $ ./Configure enable-fips --prefix=usr --openssldir=/usr/lib/ssl --libdir=lib/x86_64-linux-gnu $ make $ sudo make install_fips # Modified openssl.cnf to enable FIPS mode $ sudo cp /usr/lib/ssl/fipsmodule.cnf /etc/ssl/fipsmodule.cnf Create /etc/ssl/openssl-fips.cnf which will enable the FIPS module. It is a modified copy of the stock /etc/ssl/openssl.cnf file that comes with the openssl deb package. $ sed \ -e '51d' \ -e '52i ##' \ -e '52i ## https://www.openssl.org/docs/manmaster/man7/fips_module.html' \ -e '52i ##' \ -e '52i .include /etc/ssl/fipsmodule.cnf' \ -e '54d' \ -e '55i providers = provider_sect' \ -e '55i alg_section = algorithm_sect' \ -e '57d' \ -e '58d' \ -e '59i [provider_sect]' \ -e '59i default = default_sect' \ -e '59i \\n' \ -e '61d' \ -e '62i fips = fips_sect' \ -e '71d' \ -e '72d' \ -e '73i [default_sect]' \ -e '73i activate = 1' \ -e '73i [algorithm_sect]' \ -e '73i default_properties = fips=yes' \ -e '73i \\n' \ /etc/ssl/openssl.cnf | sudo tee /etc/ssl/openssl-fips.cnf In the `sed` command above, we are inserting lines in specific places and removing specific lines. This is specific to the openssl deb package version 3.0.11-1 and may not work for other versions. This result could be achieved by regex search and replace as well but that is left up to you. # Set environment variable OPENSSL_CONF `openssl` uses environment variable OPENSSL_CONF to set the default configuration file. By default this variable is not set and in Debian the default file is /etc/ssl/openssl.cnf. To switch to the FIPS config file we created above, set the environment variable, $ export OPENSSL_CONF=/etc/ssl/openssl-fips.cnf # Verify FIPS is enabled ## Check md5 MD5 will work when FIPS is _not_ enabled, $ openssl md5 /dev/null MD5(/dev/null)= d41d8cd98f00b204e9800998ecf8427e MD5 will not work when FIPS is enabled, $ openssl md5 /dev/null Error setting digest ## Check supported ciphers CHACHA20_POLY1305 is _not_ supported in FIPS module. If it is present, openssl is _not_ using FIPS. For example, in the output below we see CHACHA20_POLY1305 is present, thus FIPS is not enabled, $ openssl ciphers | tr ':' '\n' | sort AES128-GCM-SHA256 AES128-SHA AES128-SHA256 AES256-GCM-SHA384 AES256-SHA AES256-SHA256 DHE-PSK-AES128-CBC-SHA DHE-PSK-AES128-CBC-SHA256 DHE-PSK-AES128-GCM-SHA256 DHE-PSK-AES256-CBC-SHA DHE-PSK-AES256-CBC-SHA384 DHE-PSK-AES256-GCM-SHA384 DHE-PSK-CHACHA20-POLY1305 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-SHA DHE-RSA-AES256-SHA256 DHE-RSA-CHACHA20-POLY1305 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-ECDSA-CHACHA20-POLY1305 ECDHE-PSK-AES128-CBC-SHA ECDHE-PSK-AES128-CBC-SHA256 ECDHE-PSK-AES256-CBC-SHA ECDHE-PSK-AES256-CBC-SHA384 ECDHE-PSK-CHACHA20-POLY1305 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA ECDHE-RSA-AES256-SHA384 ECDHE-RSA-CHACHA20-POLY1305 PSK-AES128-CBC-SHA PSK-AES128-CBC-SHA256 PSK-AES128-GCM-SHA256 PSK-AES256-CBC-SHA PSK-AES256-CBC-SHA384 PSK-AES256-GCM-SHA384 PSK-CHACHA20-POLY1305 RSA-PSK-AES128-CBC-SHA RSA-PSK-AES128-CBC-SHA256 RSA-PSK-AES128-GCM-SHA256 RSA-PSK-AES256-CBC-SHA RSA-PSK-AES256-CBC-SHA384 RSA-PSK-AES256-GCM-SHA384 RSA-PSK-CHACHA20-POLY1305 SRP-AES-128-CBC-SHA SRP-AES-256-CBC-SHA SRP-RSA-AES-128-CBC-SHA SRP-RSA-AES-256-CBC-SHA TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 In the output below CHACHA20_POLY1305 is not present, thus FIPS is enabled, $ openssl ciphers | tr ':' '\n' | sort AES128-GCM-SHA256 AES128-SHA AES128-SHA256 AES256-GCM-SHA384 AES256-SHA AES256-SHA256 DHE-PSK-AES128-CBC-SHA DHE-PSK-AES128-CBC-SHA256 DHE-PSK-AES128-GCM-SHA256 DHE-PSK-AES256-CBC-SHA DHE-PSK-AES256-CBC-SHA384 DHE-PSK-AES256-GCM-SHA384 DHE-RSA-AES128-GCM-SHA256 DHE-RSA-AES128-SHA DHE-RSA-AES128-SHA256 DHE-RSA-AES256-GCM-SHA384 DHE-RSA-AES256-SHA DHE-RSA-AES256-SHA256 ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES128-SHA ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES256-SHA ECDHE-ECDSA-AES256-SHA384 ECDHE-PSK-AES128-CBC-SHA ECDHE-PSK-AES128-CBC-SHA256 ECDHE-PSK-AES256-CBC-SHA ECDHE-PSK-AES256-CBC-SHA384 ECDHE-RSA-AES128-GCM-SHA256 ECDHE-RSA-AES128-SHA ECDHE-RSA-AES128-SHA256 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-SHA ECDHE-RSA-AES256-SHA384 PSK-AES128-CBC-SHA PSK-AES128-CBC-SHA256 PSK-AES128-GCM-SHA256 PSK-AES256-CBC-SHA PSK-AES256-CBC-SHA384 PSK-AES256-GCM-SHA384 RSA-PSK-AES128-CBC-SHA RSA-PSK-AES128-CBC-SHA256 RSA-PSK-AES128-GCM-SHA256 RSA-PSK-AES256-CBC-SHA RSA-PSK-AES256-CBC-SHA384 RSA-PSK-AES256-GCM-SHA384 SRP-AES-128-CBC-SHA SRP-AES-256-CBC-SHA SRP-RSA-AES-128-CBC-SHA SRP-RSA-AES-256-CBC-SHA TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 ## Verify https ciphers If you have configured a web server with TLS, for example nginx, you can also verify the ciphers it supports. If CHACHA20_POLY1305 is not present then FIPS is enabled. In our example below, google.com has _not_ enabled FIPS so we see CHACHA20_POLY1305. $ nmap --script ssl-enum-ciphers -p 443 google.com Starting Nmap 7.95 ( https://nmap.org ) at 2024-05-07 18:26 PDT Nmap scan report for google.com (142.250.217.78) Host is up (0.013s latency). Other addresses for google.com (not scanned): 2607:f8b0:400a:80a::200e rDNS record for 142.250.217.78: sea09s29-in-f14.1e100.net PORT STATE SERVICE 443/tcp open https | ssl-enum-ciphers: | TLSv1.0: | ciphers: | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C | compressors: | NULL | cipher preference: server | warnings: | 64-bit block cipher 3DES vulnerable to SWEET32 attack | TLSv1.1: | ciphers: | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C | compressors: | NULL | cipher preference: server | warnings: | 64-bit block cipher 3DES vulnerable to SWEET32 attack | TLSv1.2: | ciphers: | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | TLS_RSA_WITH_3DES_EDE_CBC_SHA (rsa 2048) - C | TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A | TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A | TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A | compressors: | NULL | cipher preference: client | warnings: | 64-bit block cipher 3DES vulnerable to SWEET32 attack | TLSv1.3: | ciphers: | TLS_AKE_WITH_AES_128_GCM_SHA256 (ecdh_x25519) - A | TLS_AKE_WITH_AES_256_GCM_SHA384 (ecdh_x25519) - A | TLS_AKE_WITH_CHACHA20_POLY1305_SHA256 (ecdh_x25519) - A | cipher preference: client |_ least strength: C Nmap done: 1 IP address (1 host up) scanned in 1.62 seconds