我有两个极其相似的自签名证书,是通过两种不同的方法生成的。
为了测试它们,我有:
- 在我的主机文件中为 local.mydomain.com 添加了一个条目
- 设置一个 nginx 服务器以在端口 443 上使用正在测试的证书和关联的私钥监听该域(然后我切换证书并重新启动 nginx 进行比较)
- 连接到nginx
openssl s_client -connect local.mydomain.com -CAfile /path/to/the/ca/cert.pem
一个证书失败:
CONNECTED(00000003)
depth=0 CN = local.mydomain.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = local.mydomain.com
verify error:num=21:unable to verify the first certificate
verify return:1
---
Certificate chain
0 s:/CN=local.mydomain.com
i:/CN=local.mydomain.com
---
一个证书成功:
CONNECTED(00000003)
depth=0 CN = local.mydomain.com
verify return:1
---
Certificate chain
0 s:/CN = local.mydomain.com
i:/CN = local.mydomain.com
---
我将证书的详细信息与 openssl x509 -in /path/to/the/ca/cert.pem -text -noout
失败的证书:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
47:dc:02:c7:11:fc:8e:96:45:22:aa:6b:23:79:32:ca
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=local.mydomain.com
Validity
Not Before: Nov 18 11:55:31 2016 GMT
Not After : Nov 18 12:15:31 2017 GMT
Subject: CN=local.mydomain.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
<stuff>
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication, TLS Web Server Authentication
X509v3 Subject Alternative Name:
DNS:local.mydomain.com
X509v3 Subject Key Identifier:
6D:4F:AF:E4:60:23:72:E5:83:27:91:7D:1D:5F:E9:7C:D9:B6:00:2A
Signature Algorithm: sha256WithRSAEncryption
<stuff>
工作证书:
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
9b:6b:3d:a3:b9:a3:a4:b4
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=local.mydomain.com
Validity
Not Before: Nov 19 13:27:30 2016 GMT
Not After : Nov 19 13:27:30 2017 GMT
Subject: CN=local.mydomain.com
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
<stuff>
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Subject Key Identifier:
03:E7:DA:AA:2E:CC:23:ED:C5:07:3D:E1:33:86:F5:22:D4:76:EB:CB
X509v3 Authority Key Identifier:
keyid:03:E7:DA:AA:2E:CC:23:ED:C5:07:3D:E1:33:86:F5:22:D4:76:EB:CB
X509v3 Basic Constraints:
CA:TRUE
Signature Algorithm: sha256WithRSAEncryption
57<stuff>
看这个最明显的区别是工作证书CA:TRUE
低于X509v3 Basic Constraints
. 但是,从网上阅读我的印象是,自签名证书并不意味着是 CA,特别是这表明它们通常不会是:
那里的答案说自签名不涉及CA。但是也许openssl需要自签名证书才能设置?或者证书可能以其他相关方式有所不同?
更新:
我花了一些时间尝试 printf 调试 openssl 的内部结构,但我并不了解它。在文件 v3_purp.c 中有以下宏:
#define ku_reject(x, usage) \
(((x)->ex_flags & EXFLAG_KUSAGE) && !((x)->ex_kusage & (usage)))
它用于检查自签名证书的这段代码中:
/* Does subject name match issuer ? */
if (X509_check_akid(x, x->akid) == X509_V_OK &&
!ku_reject(x, KU_KEY_CERT_SIGN))
x->ex_flags |= EXFLAG_SS;
在失败证书的情况下(x)->ex_flags & EXFLAG_KUSAGE
等于 2
EXFLAG_KUSAGE
在这里为失败的证书设置,早先在同一个文件中:
static void x509v3_cache_extensions(X509 *x)
{
......
if ((usage = X509_get_ext_d2i(x, NID_key_usage, NULL, NULL))) {
if (usage->length > 0) {
x->ex_kusage = usage->data[0];
if (usage->length > 1)
x->ex_kusage |= usage->data[1] << 8;
} else
x->ex_kusage = 0;
x->ex_flags |= EXFLAG_KUSAGE;
ASN1_BIT_STRING_free(usage);
}
....
所以看起来问题是失败的证书具有X509v3 Key Usage
扩展名,但没有在该扩展名中指定 KU_KEY_CERT_SIGN?
更新 2:
根据https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3:
“当主题公钥用于验证公钥证书上的签名时,keyCertSign 位被断言。如果 keyCertSign 位被断言,那么基本约束扩展(第 4.2.1.9 节)中的 cA 位也必须被断言。”
因此,基本约束中的 CA 位不必存在,但如果您在证书中包含 X509v3 密钥使用部分,则根据 openssl 代码库,您必须指定 keyCertSign,并且根据 RFC,如果您确实指定了 keyCertSign,那么您还必须包括CA位的基本约束?