如何为 Python 打包的 libcrypto 和 libssl 启用 FIPS 模式?

Hus*_*bar 3 python ssl ctypes openssl python-3.x

我有一个 Python 应用程序,它与 Python 和 Libcrypto 以及 LibSSL 共享对象一起打包。该应用程序是使用 Openssl Fips Module 2.0 构建的。Python 的请求模块和 urllib3 在后台使用这些共享对象来发出 TLS 请求。

我在构建应用程序的环境中启用了OPENSSL_FIPS标志。现在,如果要检查共享对象是否在我将它们从开发环境中取出并放入另一台机器时启用了 fips 模式,我该怎么做?

如何检查 fips 模式是否启用?如果不是,我如何为这些共享对象启用 fips 模式?

可能有帮助的其他详细信息:

OpenSSL 版本:1.0.2h(从源代码构建)

Fips 模块:2.0.12(从源代码构建)

蟒蛇:3.6

操作系统:Ubuntu 16.04 LTS

如果需要任何其他详细信息,请告诉我。

谢谢!

Cri*_*ati 6

我已经使用常规标志构建了OpenSSL-fips模块(例如:no-asmshared、禁用了一些古老的密码):

[cfati@cfati-ubtu16x64-0:~/Work/Dev/StackOverflow/q049320993]> ~/sopr.sh
*** Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ***

[064bit-prompt]> ls ssl/build/bin ssl/build/lib
ssl/build/bin:
c_rehash  openssl

ssl/build/lib:
engines  libcrypto.a  libcrypto.so  libcrypto.so.1.0.0  libssl.a  libssl.so  libssl.so.1.0.0  pkgconfig
Run Code Online (Sandbox Code Playgroud)

并开始玩弄它:

[064bit-prompt]> ssl/build/bin/openssl version
OpenSSL 1.0.2h-fips  3 May 2016 (Library: OpenSSL 1.0.2g  1 Mar 2016)
Run Code Online (Sandbox Code Playgroud)

请注意“ (库:OpenSSL 1.0.2g 2016 年 3 月 1 日) ”部分。那个(存在)表明openssl可执行文件是好的(预期版本),但它使用了错误的libcrypto(它是系统上默认安装的那个 - 在/lib 下- 通常那个不是用FIPS构建的支持)。
它必须加载我们的库,这是通过设置LD_LIBRARY_PATH来完成的(同样的行为也可以通过在构建OpenSSL时设置 env var 来实现,这会在openssl 中设置rpath 可执行文件,但我忘记了,我不想再次构建它):

[064bit-prompt]> LD_LIBRARY_PATH=ssl/build/lib ssl/build/bin/openssl version
OpenSSL 1.0.2h-fips  3 May 2016
Run Code Online (Sandbox Code Playgroud)

现在,设置成功,让我们深入研究OPENSSL_FIPS 环境变量

[064bit-prompt]> LD_LIBRARY_PATH=ssl/build/lib ssl/build/bin/openssl md5 ./code.py
MD5(./code.py)= d41d8cd98f00b204e9800998ecf8427e
[064bit-prompt]> LD_LIBRARY_PATH=ssl/build/lib ssl/build/bin/openssl sha1 ./code.py
SHA1(./code.py)= da39a3ee5e6b4b0d3255bfef95601890afd80709
[064bit-prompt]> OPENSSL_FIPS=1 LD_LIBRARY_PATH=ssl/build/lib ssl/build/bin/openssl sha1 ./code.py
SHA1(./code.py)= da39a3ee5e6b4b0d3255bfef95601890afd80709
[064bit-prompt]> OPENSSL_FIPS=1 LD_LIBRARY_PATH=ssl/build/lib ssl/build/bin/openssl md5 ./code.py
Error setting digest md5
139778679649944:error:060A80A3:digital envelope routines:FIPS_DIGESTINIT:disabled for fips:fips_md.c:180:
Run Code Online (Sandbox Code Playgroud)

正如从上面看到的,MD5散列行为是由影响OPENSSL_FIPS 的环境变量(当FIPS模式打开时,它的使用是不允许的)。

注意事项

  • 最有可能的是,较新的openssl-fips版本也会禁用sha1,因为它被认为是弱的,所以不变量应该切换到sha2哈希函数系列之一(例如sha256)或者更好的sha3(旧的OpenSSL版本可能没有它)
  • 从我的PoV来看,这有点过于严格了,因为在某些情况下可能需要散列算法用于不关心安全性的目的,并且仍然必须使用更复杂(也很耗时)的允许算法

由于OPENSSL_FIPS env var是在openssl可执行级别处理的,会被绕过(因为会直接使用libcrypto),所以对目前的情况没有用,所以我们必须更深入。这些是在加载的libcrypto实例中控制FIPS模式的函数:

它们将用于读/写FIPS模式。为了测试是否真的设置了FIPS模式,将使用md5哈希(来自上面的示例)。

代码.py

#!/usr/bin/env python3


import sys
import ssl
import ctypes


libcrypto = ctypes.CDLL("libcrypto.so.1.0.0")

fips_mode = libcrypto.FIPS_mode
fips_mode.argtypes = []
fips_mode.restype = ctypes.c_int

fips_mode_set = libcrypto.FIPS_mode_set
fips_mode_set.argtypes = [ctypes.c_int]
fips_mode_set.restype = ctypes.c_int

text = b""


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    print("OPENSSL_VERSION: {:s}".format(ssl.OPENSSL_VERSION))
    enable_fips = len(sys.argv) > 1

    print("FIPS_mode(): {:d}".format(fips_mode()))
    if enable_fips:
        print("FIPS_mode_set(1): {:d}".format(fips_mode_set(1)))
    print("FIPS_mode(): {:d}".format(fips_mode()))

    import hashlib
    print("SHA1: {:s}".format(hashlib.sha1(text).hexdigest()))
    print("MD5: {:s}".format(hashlib.md5(text).hexdigest()))
Run Code Online (Sandbox Code Playgroud)

注意事项

输出

[064bit-prompt]> LD_LIBRARY_PATH=ssl/build/lib ./code.py
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux

OPENSSL_VERSION: OpenSSL 1.0.2h-fips  3 May 2016
FIPS_mode(): 0
FIPS_mode(): 0
SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709
MD5: d41d8cd98f00b204e9800998ecf8427e
[064bit-prompt]> LD_LIBRARY_PATH=ssl/build/lib ./code.py 1
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux

OPENSSL_VERSION: OpenSSL 1.0.2h-fips  3 May 2016
FIPS_mode(): 0
FIPS_mode_set(1): 1
FIPS_mode(): 1
SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709
fips_md.c(149): OpenSSL internal error, assertion failed: Digest Final previous FIPS forbidden algorithm error ignored
Aborted (core dumped)
Run Code Online (Sandbox Code Playgroud)

正如所见,通过ctypes设置FIPS模式,确实设置了它。 我不知道它为什么会出现段错误,但是与md5相关的代码仅用于测试目的,因此在生产中不需要它。

我记得在某些Lnx版本(可能是基于RH的)上,也可以通过编辑一些条目(在/proc 下?)来设置FIPS模式(系统全局),但我不记得了。

一个更好的方法将是公开的Python为2个函数的包装。
检查[Python.Bugs]: FIPS_mode() 和 FIPS_mode_set() 函数在 Python (ssl) 中,我还提交了Python 3.4的补丁(它们由ssl模块公开),但基于以下原因被拒绝参数(其中 1 st 2 是相关的):

  1. FIPS是一个糟糕的标准
  2. OpenSSL将放弃对它的支持
  3. 它打破了一般性

您可以将其应用于Python 3.6(我认为它不会在OOTB 中工作,因为行号很可能已更改),并且(显然)您必须从源代码构建Python

底线



更新#0

它只是让我感到震惊,您在[SO]上遇到的行为:无法使用 Python ctypes [重复] 调用 libcrypto.so 的 FIPS_mode_set()也可能与加载错误的libcrypto有关(检查openssl version测试 w/ wo LD_LIBRARY_PATH从一开始)。
FIPS能够OpenSSL的将仍然出口2层的功能,但他们都只是简单地返回0。

[064bit-prompt]> ./code.py 1
Python 3.5.2 (default, Nov 23 2017, 16:37:01)
[GCC 5.4.0 20160609] on linux

OPENSSL_VERSION: OpenSSL 1.0.2g  1 Mar 2016
FIPS_mode(): 0
FIPS_mode_set(1): 0
FIPS_mode(): 0
SHA1: da39a3ee5e6b4b0d3255bfef95601890afd80709
MD5: d41d8cd98f00b204e9800998ecf8427e
Run Code Online (Sandbox Code Playgroud)

因此,请确保通过指定LD_LIBRARY_PATH加载正确的库!(还有其他方法,但这是最直接的一种)。