为什么 OpenSSL 在由自签名的不受信任的证书颁发时说该证书受信任?

Why OpenSSL says the certificate is trusted when it is issued by self-signed untrusted certificate?

为了测试,我有 3 个证书:一个受信任的自签名根证书,一个不受信任的自签名证书和一个由不受信任的自签名证书颁发的证书(因此也不应受信任)。但是,如果我使用 OpenSSL (1.1.1d) 验证最后一个证书,它会说它是有效的。我原以为验证会失败,因为它被追踪到的根是不可信的。我做错了什么?

这是代码片段(不要介意泄漏或遗漏检查,它仅用于此演示):

const char* const rootPem =
"-----BEGIN CERTIFICATE-----\n"
"MIIB3jCCAYSgAwIBAgIJAPHf5UibUrNZMAoGCCqGSM49BAMCMEIxCzAJBgNVBAYT\n"
"AkdCMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYDVQQKDAlBbGljZSBMdGQxDTALBgNV\n"
"BAMMBFJPT1QwHhcNMjAwMjE5MTc1NDE5WhcNMjEwMjE4MTc1NDE5WjBCMQswCQYD\n"
"VQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDESMBAGA1UECgwJQWxpY2UgTHRkMQ0w\n"
"CwYDVQQDDARST09UMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAErwct49VH1jQZ\n"
"kA8bZsENcn5kRjVabz9ZjewKXO1QmjE3Uqz9GjBaaH+H+OsZmVST+NQjX4XF6g/K\n"
"o1BmXoqs+qNjMGEwHQYDVR0OBBYEFO91oq/ncaT3AiZC3q0jHmptro3LMB8GA1Ud\n"
"IwQYMBaAFO91oq/ncaT3AiZC3q0jHmptro3LMA8GA1UdEwEB/wQFMAMBAf8wDgYD\n"
"VR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0gAMEUCIDy3A4BP27GviTbXBtDrxvQD\n"
"0y0KwkjawXTb13euL9YGAiEA583y9AAUagppAejYTDIsurdFdRulUqVzPy6H5JCq\n"
"yb8=\n"
"-----END CERTIFICATE-----\n";

const char* const fakeRootPem =
"-----BEGIN CERTIFICATE-----\n"
"MIIB4zCCAYqgAwIBAgIJAI9ickj5XUpqMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT\n"
"AkdCMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYDVQQKDAlBbGljZSBMdGQxEDAOBgNV\n"
"BAMMB0ZBS0VfQ0EwHhcNMjAwMjI3MTAyNjQ2WhcNMjEwMjI2MTAyNjQ2WjBFMQsw\n"
"CQYDVQQGEwJHQjEQMA4GA1UECAwHRW5nbGFuZDESMBAGA1UECgwJQWxpY2UgTHRk\n"
"MRAwDgYDVQQDDAdGQUtFX0NBMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMD93\n"
"rwESh/AZLTrvWStRAujiHePnID2zuYnXSkOhh0KiIs+tOyQmQXv16gwYvRF1X0v7\n"
"Q6cWK4pOe1No5U5tlaNjMGEwHQYDVR0OBBYEFGfD4rQgCqDvO1U9Tvg/qVHrIaTn\n"
"MB8GA1UdIwQYMBaAFGfD4rQgCqDvO1U9Tvg/qVHrIaTnMA8GA1UdEwEB/wQFMAMB\n"
"Af8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIE+cGjmuzPvPE2Kh\n"
"uyP7mH2k7CBu99nQ9P0X+ecd5uynAiAa2g1+uweZ/lLbrH32dfnvNlfa29uTqVjX\n"
"SEXodEo78Q==\n"
"-----END CERTIFICATE-----\n";

const char* const fakeLeafPem =
"-----BEGIN CERTIFICATE-----\n"
"MIIB6TCCAZCgAwIBAgIRAOzOwVz92cGF99cokc68Gy8wCgYIKoZIzj0EAwIwQjEL\n"
"MAkGA1UEBhMCR0IxEDAOBgNVBAgMB0VuZ2xhbmQxEjAQBgNVBAoMCUFsaWNlIEx0\n"
"ZDENMAsGA1UEAwwEUk9PVDAeFw0yMDAyMjcxMDI5MDRaFw0yMTAzMDgxMDI5MDRa\n"
"MEYxCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdFbmdsYW5kMRIwEAYDVQQKDAlBbGlj\n"
"ZSBMdGQxETAPBgNVBAMMCEZBS0VfQ0ExMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n"
"QgAEOAAD+GG/lNYAYcakWcp3CwT2gMoHH4UZyyzLsr1Ge9QAkEU8eGrdJCwMNA6O\n"
"PVc7+UYPyd8k6opxODOphNKwV6NjMGEwHQYDVR0OBBYEFF+aTSzJFHSFg1r1pNlE\n"
"+1Z6Edi1MB8GA1UdIwQYMBaAFO91oq/ncaT3AiZC3q0jHmptro3LMA8GA1UdEwEB\n"
"/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMCA0cAMEQCIE7IyY+M\n"
"tXdik8VLsyVJpdgV02VcUpDWECtQ5zBFaqfKAiApHJoxdwGtfyN2gkhcV8uQcjjW\n"
"ccI6BV7gTn/3F8UIsg==\n"
"-----END CERTIFICATE-----\n";

// trusted
BIO* bio = BIO_new(BIO_s_mem());
BIO_write(bio, rootPem, strlen(rootPem));
X509* trusted_root = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
X509_STORE* store = X509_STORE_new();
X509_STORE_add_cert(store, trusted_root);

// untrusted root
bio = BIO_new(BIO_s_mem());
BIO_write(bio, fakeRootPem, strlen(fakeRootPem));
X509* untrusted_root = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
STACK_OF(X509)* untrusted_chain = sk_X509_new_null();
sk_X509_push(untrusted_chain, untrusted_root);

// validation of untrusted cert
bio = BIO_new(BIO_s_mem());
BIO_write(bio, fakeLeafPem, strlen(fakeLeafPem));
X509* untrusted_cert = PEM_read_bio_X509(bio, nullptr, nullptr, nullptr);
X509_STORE_CTX* ctx = X509_STORE_CTX_new();
X509_STORE_CTX_init(ctx, store, untrusted_cert, untrusted_chain);
int verified = X509_verify_cert(ctx);

printf("Certificate is %s\n", (verified == 1) ? " VALID" : "NOT VALID");

我也尝试过使用 X509_STORE_CTX_set0_untrusted 传递不受信任的链,但得到了相同的结果。

看来您没有测试您认为正在测试的东西。

将以下内容添加到代码段的末尾以获取有关您的证书的更多信息

#define PRINT_NAMES(cert) \
    printf("\n" #cert " subject name: "); \
    X509_NAME_print_ex_fp(stdout, X509_get_subject_name(cert), 0, 0); \
    printf("\n" #cert " issuer name: "); \
    X509_NAME_print_ex_fp(stdout, X509_get_issuer_name(cert), 0, 0); \
    printf("\n");

PRINT_NAMES(trusted_root);
PRINT_NAMES(untrusted_root);
PRINT_NAMES(untrusted_cert);

结果

trusted_root subject name: C=GB, ST=England, O=Alice Ltd, CN=ROOT
trusted_root issuer name: C=GB, ST=England, O=Alice Ltd, CN=ROOT

untrusted_root subject name: C=GB, ST=England, O=Alice Ltd, CN=FAKE_CA
untrusted_root issuer name: C=GB, ST=England, O=Alice Ltd, CN=FAKE_CA

untrusted_cert subject name: C=GB, ST=England, O=Alice Ltd, CN=FAKE_CA1
untrusted_cert issuer name: C=GB, ST=England, O=Alice Ltd, CN=ROOT

所以untrusted_cert实际上是由trusted_root发出的,这就解释了结果。

为了快速测试您想要测试的内容,您可以翻转两个字符串文字的名称,因此将第一个定义为 fakeRootPem,将第二个定义为 rootPem。这将使 ROOT 在您的代码中不受信任并 FAKE_CA 受信任,并因此产生 desired/expected 输出。