提问人:Marek R 提问时间:11/3/2023 最后编辑:Marek R 更新时间:11/4/2023 访问量:45
OpenSSL 3.0.8 中奇怪的隐藏状态会更改PEM_read_bio_X509的行为
Strange hiden state in OpenSSL 3.0.8 whcih changes behavior of PEM_read_bio_X509
问:
背景
我有大型 C++ 多平台应用程序,它使用静态版本的 OpenSsl。目前它在版本中使用OpenSsl,一切正常。1.1.1t
现在我正在尝试升级到 OpenSsl 。出于这个原因,我需要使用旧版提供程序,否则我的测试检测到一些问题(加载 pfx 文件失败)。这解决了我拥有的多个测试的问题。3.0.8
问题
现在,我的一个测试仅在运行所有测试时失败。当我运行此测试时,只有它通过了。奇怪的是,被测代码非常简单,不使用任何全局状态。这可以解释这种不同的行为,具体取决于是否运行其他测试。只有隐藏的OpenSSL全局状态才能解释这一点。
被测代码是这样的(几乎完全复制粘贴):
class FileCertsReader
{
public:
~FileCertsReader() = default;
explicit FileCertsReader(boost::filesystem::path certificatesFile)
: mFilePath{std::move(caCertificatesFile)}
{
}
bool init()
{
mCertificates = utils::wrapUniqueThrow(BIO_new(BIO_s_file()));
if (!mCertificates)
{
return false;
}
if (!BIO_read_filename(mCertificates.get(), mFilePath.string().c_str()))
{
mCertificates.reset();
return false;
}
return true;
}
utils::UniquePtr<X509> enumerate()
{
return utils::wrapUnique(PEM_read_bio_X509(mCertificates.get(), nullptr, nullptr, nullptr));
}
private:
boost::filesystem::path mFilePath;
utils::UniquePtr<BIO> mCertificates;
};
在哪里,并且只是使用各自的自定义删除器。此代码小而简单。utils::wrapUnique
utils::UniquePtr<X509>
std::unique_ptr
现在测试很简单:
TEST_F(FileCertsReaderTest, enumerate)
{
auto store = std::make_unique<FileCertsReader>("single_file_ca_store.crt");
ASSERT_TRUE(!!store);
ASSERT_TRUE(store->init());
ASSERT_NO_FATAL_FAILURE(
expectCertWithName(store->enumerate(), "CN=emSign Root CA - C1;O=eMudhra Inc;OU=emSign PKI;C=US"));
ASSERT_NO_FATAL_FAILURE(expectCertWithName(
store->enumerate(), "CN=emSign Root CA - G1;O=eMudhra Technologies Limited;OU=emSign PKI;C=IN"));
ASSERT_THAT(store->enumerate(), IsNull());
auto sslError = ::ERR_get_error();
char errString[512];
ERR_error_string_n(sslError, errString, sizeof(errString));
EXPECT_EQ(ERR_LIB_PEM, ERR_GET_LIB(sslError)) << errString;
EXPECT_EQ(PEM_R_NO_START_LINE, ERR_GET_REASON(sslError)) << errString;
}
现在只是提醒你:
- 当使用 OpenSsl 1.1.1t 时,此测试始终通过,如果同时运行其他测试,则不会通过
- 当使用 gtest_filter 仅运行此测试时,此测试已通过
- 运行所有单元测试时,此测试将失败。在这种情况下,我在日志中看到:
...
[ RUN ] FileCertsReaderTest.enumerate
/home/user/repo/project/Src/UnitTests/security/FileCertsReaderTest.cpp:155: Failure
Expected equality of these values:
9
ERR_GET_LIB(err)
Which is: 13
error:068000A8:asn1 encoding routines::wrong tag
/home/user/repo/project/Src/UnitTests/security/FileCertsReaderTest.cpp:156: Failure
Expected equality of these values:
108
ERR_GET_REASON(err)
Which is: 168
error:068000A8:asn1 encoding routines::wrong tag
[ FAILED ] FileCertsReaderTest.enumerate (0 ms)
....
[----------] Global test environment tear-down
[==========] 1878 tests from 173 test suites ran. (24820 ms total)
[ PASSED ] 1875 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] FileCertsReaderTest.enumerate
如您所见,当到达证书列表末尾时,OpenSSL 报告的错误是不同的。
仅运行此测试时:
$ ./UnitTests --gtest_filter=FileCertsReaderTest.enumerate
Note: Google Test filter = FileCertsReaderTest.enumerate
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from FileCertsReaderTest
[ RUN ] FileCertsReaderTest.enumerate
[ OK ] FileCertsReaderTest.enumerate (0 ms)
[----------] 1 test from FileCertsReaderTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (1 ms total)
[ PASSED ] 1 test.
问题
- 这是OpenSSL的已知问题吗?有没有办法解决这个问题?
- 我有很多测试(大约 2k),有没有办法检测哪些测试对这个特定的测试有影响?我可以要求在其他测试套件之间运行这个有问题的测试吗?这样我就可以确定哪个其他测试对这个失败的测试有影响。
gtest
笔记
- 未检测到其他问题。没有泄漏,UB,所有其他测试在所有情况下都通过了。
- 这是我在 godbolt 上交付 MCRE 的尝试: https://godbolt.org/z/EdPz7hf1E https://godbolt.org/z/MaTY9q86Y 但正如我在问题中指出的那样,要进行繁殖测试,我需要找到其他影响有问题的测试的测试
- 以下是 OpenSSL 如何看待此证书用于测试:
$ openssl x509 -in test/single_file_ca_store.crt -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
ae:cf:00:ba:c4:cf:32:f8:43:b2
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = US, OU = emSign PKI, O = eMudhra Inc, CN = emSign Root CA - C1
Validity
Not Before: Feb 18 18:30:00 2018 GMT
Not After : Feb 18 18:30:00 2043 GMT
Subject: C = US, OU = emSign PKI, O = eMudhra Inc, CN = emSign Root CA - C1
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:cf:eb:a9:b9:f1:99:05:cc:d8:28:21:4a:f3:73:
34:51:84:56:10:f5:a0:4f:2c:12:e3:fa:13:9a:27:
d0:cf:f9:79:1a:74:5f:1d:79:39:fc:5b:f8:70:8e:
e0:92:52:f7:e4:25:f9:54:83:d9:1d:d3:c8:5a:85:
3f:5e:c7:b6:07:ee:3e:c0:ce:9a:af:ac:56:42:2a:
39:25:70:d6:bf:b5:7b:36:ad:ac:f6:73:dc:cd:d7:
1d:8a:83:a5:fb:2b:90:15:37:6b:1c:26:47:dc:3b:
29:56:93:6a:b3:c1:6a:3a:9d:3d:f5:c1:97:38:58:
05:8b:1c:11:e3:e4:b4:b8:5d:85:1d:83:fe:78:5f:
0b:45:68:18:48:a5:46:73:34:3b:fe:0f:c8:76:bb:
c7:18:f3:05:d1:86:f3:85:ed:e7:b9:d9:32:ad:55:
88:ce:a6:b6:91:b0:4f:ac:7e:15:23:96:f6:3f:f0:
20:34:16:de:0a:c6:c4:04:45:79:7f:a7:fd:be:d2:
a9:a5:af:9c:c5:23:2a:f7:3c:21:6c:bd:af:8f:4e:
c5:3a:b2:f3:34:12:fc:df:80:1a:49:a4:d4:a9:95:
f7:9e:89:5e:a2:89:ac:94:cb:a8:68:9b:af:8a:65:
27:cd:89:ee:dd:8c:b5:6b:29:70:43:a0:69:0b:e4:
b9:0f
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
FE:A1:E0:70:1E:2A:03:39:52:5A:42:BE:5C:91:85:7A:18:AA:4D:B5
X509v3 Key Usage: critical
Certificate Sign, CRL Sign
X509v3 Basic Constraints: critical
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
c2:4a:56:fa:15:21:7b:28:a2:e9:e5:1d:fb:f8:2d:c4:39:96:
41:4c:3b:27:2c:c4:6c:18:15:80:c6:ac:af:47:59:2f:26:0b:
e3:36:b0:ef:3b:fe:43:97:49:32:99:12:15:5b:df:11:29:ff:
ab:53:f8:bb:c1:78:0f:ac:9c:53:af:57:bd:68:8c:3d:69:33:
f0:a3:a0:23:63:3b:64:67:22:44:ad:d5:71:cb:56:2a:78:92:
a3:4f:12:31:36:36:e2:de:fe:00:c4:a3:60:0f:27:ad:a0:b0:
8a:b5:36:7a:52:a1:bd:27:f4:20:27:62:e8:4d:94:24:13:e4:
0a:04:e9:3c:ab:2e:c8:43:09:4a:c6:61:04:e5:49:34:7e:d3:
c4:c8:f5:0f:c0:aa:e9:ba:54:5e:f3:63:2b:4f:4f:50:d4:fe:
b9:7b:99:8c:3d:c0:2e:bc:02:2b:d3:c4:40:e4:8a:07:31:1e:
9b:ce:26:99:13:fb:11:ea:9a:22:0c:11:19:c7:5e:1b:81:50:
30:c8:96:12:6e:e7:cb:41:7f:91:3b:a2:47:b7:54:80:1b:dc:
00:cc:9a:90:ea:c3:c3:50:06:62:0c:30:c0:15:48:a7:a8:59:
7c:e1:ae:22:a2:e2:0a:7a:0f:fa:62:ab:52:4c:e1:f1:df:ca:
be:83:0d:42
-----BEGIN CERTIFICATE-----
MIIDczCCAlugAwIBAgILAK7PALrEzzL4Q7IwDQYJKoZIhvcNAQELBQAwVjELMAkG
A1UEBhMCVVMxEzARBgNVBAsTCmVtU2lnbiBQS0kxFDASBgNVBAoTC2VNdWRocmEg
SW5jMRwwGgYDVQQDExNlbVNpZ24gUm9vdCBDQSAtIEMxMB4XDTE4MDIxODE4MzAw
MFoXDTQzMDIxODE4MzAwMFowVjELMAkGA1UEBhMCVVMxEzARBgNVBAsTCmVtU2ln
biBQS0kxFDASBgNVBAoTC2VNdWRocmEgSW5jMRwwGgYDVQQDExNlbVNpZ24gUm9v
dCBDQSAtIEMxMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz+upufGZ
BczYKCFK83M0UYRWEPWgTywS4/oTmifQz/l5GnRfHXk5/Fv4cI7gklL35CX5VIPZ
HdPIWoU/Xse2B+4+wM6ar6xWQio5JXDWv7V7Nq2s9nPczdcdioOl+yuQFTdrHCZH
3DspVpNqs8FqOp099cGXOFgFixwR4+S0uF2FHYP+eF8LRWgYSKVGczQ7/g/IdrvH
GPMF0Ybzhe3nudkyrVWIzqa2kbBPrH4VI5b2P/AgNBbeCsbEBEV5f6f9vtKppa+c
xSMq9zwhbL2vj07FOrLzNBL834AaSaTUqZX3noleoomslMuoaJuvimUnzYnu3Yy1
aylwQ6BpC+S5DwIDAQABo0IwQDAdBgNVHQ4EFgQU/qHgcB4qAzlSWkK+XJGFehiq
TbUwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEL
BQADggEBAMJKVvoVIXsoounlHfv4LcQ5lkFMOycsxGwYFYDGrK9HWS8mC+M2sO87
/kOXSTKZEhVb3xEp/6tT+LvBeA+snFOvV71ojD1pM/CjoCNjO2RnIkSt1XHLVip4
kqNPEjE2NuLe/gDEo2APJ62gsIq1NnpSob0n9CAnYuhNlCQT5AoE6TyrLshDCUrG
YQTlSTR+08TI9Q/Aqum6VF7zYytPT1DU/rl7mYw9wC68AivTxEDkigcxHpvOJpkT
+xHqmiIMERnHXhuBUDDIlhJu58tBf5E7oke3VIAb3ADMmpDqw8NQBmIMMMAVSKeo
WXzhriKi4gp6D/piq1JM4fHfyr6DDUI=
-----END CERTIFICATE-----
答:
0赞
suchislife
11/4/2023
#1
OpenSSL 行为可能受到各种因素的影响,包括其在程序不同部分的初始化和配置。
下面是 C++ 中的一个示例,说明如何在 OpenSSL 3.0.8 中显式强制加载旧版提供程序,这可能有助于稳定行为:
#include <openssl/provider.h>
bool load_legacy_provider() {
OSSL_PROVIDER *legacy;
legacy = OSSL_PROVIDER_load(nullptr, "legacy");
if (legacy == nullptr) {
return false;
}
return true;
}
// Call this function before any OpenSSL operations in your tests
// to ensure the legacy provider is loaded.
if (!load_legacy_provider()) {
// Handle the error, the legacy provider could not be loaded.
}
评论
0赞
Marek R
11/4/2023
事实上,这已经完成了。我已经在“背景”部分链接了我的另一个问题,我在加载旧版提供程序时遇到了问题。提供的答案(加载旧版和默认提供程序)修复了我所拥有的大约 10 个测试。现在我只有这个测试有问题,它现在不稳定(根据情况通过失败)。事实上,在这个问题的第一个版本中,我包含了这种代码,但我已将其作为实际问题的噪音删除了
评论
openssl
openssl x509 -in /path/to/x509.pem -text