证书校验过程需要先构造证书链,然后在进行签名、吊销等校验,在构造证书链过程中会根据证书的issuer编码值,在所有设置的可信或非可信证书区域(默认先可信区域查找)查找对应的subject编码值与被校验issuer编码值相等的上级CA证书,从而形成证书链。这边要求:
RFC 5280 PKIX Certificate and CRL Profile
7.1. Internationalized Names in Distinguished Names
Conforming implementations MUST use the LDAP StringPrep profile (including insignificant space handling), as specified in RFC4518,
as the basis for comparison of distinguished name attributes encoded
in either PrintableString or UTF8String. Conforming implementations
MUST support name comparisons using caseIgnoreMatch. Support for
attribute types that use other equality matching rules is optional.
Before comparing names using the caseIgnoreMatch matching rule,
conforming implementations MUST perform the six-step string
preparation algorithm described in RFC4518 for each attribute of
type DirectoryString...
Two naming attributes match if the attribute types are the same and
the values of the attributes are an exact match after processing with
the string preparation algorithm. Two relative distinguished names
RDN1 and RDN2 match if they have the same number of naming attributes
and for each naming attribute in RDN1 there is a matching naming
attribute in RDN2. Two distinguished names DN1 and DN2 match if they
have the same number of RDNs, for each RDN in DN1 there is a matching
RDN in DN2, and the matching RDNs appear in the same order in both
DNs. A distinguished name DN1 is within the subtree defined by the
distinguished name DN2 if DN1 contains at least as many RDNs as DN2,
and DN1 and DN2 are a match when trailing RDNs in DN1 are ignored.
环境信息
case_order_test$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 20.04.6 LTS
Release: 20.04
Codename: focal
case_order_test$ openssl version
OpenSSL 1.1.1f 31 Mar 2020
case_order_test$
vim test.sh
#生成私钥
openssl ecparam -name prime256v1 -out root-key.pem -genkey
openssl ecparam -name prime256v1 -out grand-key.pem -genkey
#生成自签名根证书
subject="/CN=root/C=CN/ST=rootprovince/L=rootcity/O=rootorganization/OU=rootgroup";
openssl req -new -extensions v3_ca -key root-key.pem -out root-cert.csr -subj "${subject}";
openssl x509 -req -days 3650 -extensions v3_ca -extfile /etc/ssl/openssl.cnf -signkey root-key.pem -in root-cert.csr -out root-cert.pem;
#自校验通过
openssl verify -CAfile root-cert.pem root-cert.pem;
#subject大小写不同的自签名根证书, key是相同的
subject2="/CN=root/C=CN/ST=rootprovince/L=rootcity/O=ROOTorganization/OU=rootgroup";
openssl req -new -extensions v3_ca -key root-key.pem -out root-cert2.csr -subj "${subject2}";
openssl x509 -req -days 3650 -extensions v3_ca -extfile /etc/ssl/openssl.cnf -signkey root-key.pem -in root-cert2.csr -out root-cert2.pem;
#自校验通过
openssl verify -CAfile root-cert2.pem root-cert2.pem;
#subject顺序不同的根证书, key是相同的
subject3="/C=CN/CN=root/ST=rootprovince/L=rootcity/O=rootorganization/OU=rootgroup";
openssl req -new -extensions v3_ca -key root-key.pem -out root-cert3.csr -subj "${subject3}";
openssl x509 -req -days 3650 -extensions v3_ca -extfile /etc/ssl/openssl.cnf -signkey root-key.pem -in root-cert3.csr -out root-cert3.pem;
#自校验通过
openssl verify -CAfile root-cert3.pem root-cert3.pem;
#root颁发一个grand-cert证书
openssl req -new -key grand-key.pem -out grand-cert.csr -subj "${subject//root/grand}";
openssl x509 -req -days 3650 -extensions v3_ca -extfile /etc/ssl/openssl.cnf -CA root-cert.pem -CAkey root-key.pem -CAserial root-cert.srl -CAcreateserial -in grand-cert.csr -out grand-cert.pem;
#证书校验通过
openssl verify -CAfile root-cert.pem grand-cert.pem;
#证书校验通过,subject大小写不同
openssl verify -CAfile root-cert2.pem grand-cert.pem;
#证书校验失败,subject属性顺序不同
openssl verify -CAfile root-cert3.pem grand-cert.pem;
#校验失败,如果被校验证书是自签名证书,则要求找到的CA完全相同
openssl verify -CAfile root-cert.pem root-cert2.pem
test.sh 运行输出如下:
Signature ok
subject=CN = root, C = CN, ST = rootprovince, L = rootcity, O = rootorganization, OU = rootgroup
Getting Private key
root-cert.pem: OK
Signature ok
subject=CN = root, C = CN, ST = rootprovince, L = rootcity, O = ROOTorganization, OU = rootgroup
Getting Private key
root-cert2.pem: OK
Signature ok
subject=C = CN, CN = root, ST = rootprovince, L = rootcity, O = rootorganization, OU = rootgroup
Getting Private key
root-cert3.pem: OK
Signature ok
subject=CN = grand, C = CN, ST = grandprovince, L = grandcity, O = grandorganization, OU = grandgroup
Getting CA Private Key
grand-cert.pem: OK
grand-cert.pem: OK
CN = grand, C = CN, ST = grandprovince, L = grandcity, O = grandorganization, OU = grandgroup
error 20 at 0 depth lookup: unable to get local issuer certificate
error grand-cert.pem: verification failed
CN = root, C = CN, ST = rootprovince, L = rootcity, O = ROOTorganization, OU = rootgroup
error 18 at 0 depth lookup: self signed certificate
error root-cert2.pem: verification failed
调试用的是openssl 1.1.1q源码
证书链构造对比issuer和上级CA的subject的核心堆栈
libcrypto.so.1.1!X509_NAME_cmp(const X509_NAME * a, const X509_NAME * b) (openssl-1.1.1q/crypto/x509/x509_cmp.c:183)
libcrypto.so.1.1!X509_subject_name_cmp(const X509 * a, const X509 * b) (openssl-1.1.1q/crypto/x509/x509_cmp.c:71)
libcrypto.so.1.1!x509_object_cmp(const X509_OBJECT * const * a, const X509_OBJECT * const * b) (openssl-1.1.1q/crypto/x509/x509_lu.c:148)
libcrypto.so.1.1!OBJ_bsearch_ex_(const void * key, const void * base_, int num, int size, int (*)(const void *, const void *) cmp, int flags) (openssl-1.1.1q/crypto/objects/obj_dat.c:605)
libcrypto.so.1.1!internal_find(OPENSSL_STACK * st, const void * data, int ret_val_options) (openssl-1.1.1q/crypto/stack/stack.c:310)
libcrypto.so.1.1!OPENSSL_sk_find(OPENSSL_STACK * st, const void * data) (openssl-1.1.1q/crypto/stack/stack.c:318)
libcrypto.so.1.1!sk_X509_OBJECT_find(struct stack_st_X509_OBJECT * sk, X509_OBJECT * ptr) (openssl-1.1.1q/include/openssl/x509_vfy.h:58)
libcrypto.so.1.1!x509_object_idx_cnt(struct stack_st_X509_OBJECT * h, X509_LOOKUP_TYPE type, X509_NAME * name, int * pnmatch) (openssl-1.1.1q/crypto/x509/x509_lu.c:497)
libcrypto.so.1.1!X509_OBJECT_idx_by_subject(struct stack_st_X509_OBJECT * h, X509_LOOKUP_TYPE type, X509_NAME * name) (openssl-1.1.1q/crypto/x509/x509_lu.c:516)
libcrypto.so.1.1!X509_OBJECT_retrieve_by_subject(struct stack_st_X509_OBJECT * h, X509_LOOKUP_TYPE type, X509_NAME * name) (openssl-1.1.1q/crypto/x509/x509_lu.c:524)
libcrypto.so.1.1!X509_STORE_CTX_get_by_subject(X509_STORE_CTX * vs, X509_LOOKUP_TYPE type, X509_NAME * name, X509_OBJECT * ret) (openssl-1.1.1q/crypto/x509/x509_lu.c:305)
libcrypto.so.1.1!X509_STORE_CTX_get1_issuer(X509 ** issuer, X509_STORE_CTX * ctx, X509 * x) (openssl-1.1.1q/crypto/x509/x509_lu.c:688)
libcrypto.so.1.1!get_issuer(X509 ** issuer, X509_STORE_CTX * ctx, X509 * cert) (openssl-1.1.1q/crypto/x509/x509_vfy.c:2922)
libcrypto.so.1.1!build_chain(X509_STORE_CTX * ctx) (openssl-1.1.1q/crypto/x509/x509_vfy.c:3071)
libcrypto.so.1.1!verify_chain(X509_STORE_CTX * ctx) (openssl-1.1.1q/crypto/x509/x509_vfy.c:217)
libcrypto.so.1.1!X509_verify_cert(X509_STORE_CTX * ctx) (openssl-1.1.1q/crypto/x509/x509_vfy.c:303)
check(X509_STORE * ctx, const char * file, struct stack_st_X509 * uchain, struct stack_st_X509 * tchain, struct stack_st_X509_CRL * crls, int show_chain) (openssl-1.1.1q/apps/verify.c:237)
verify_main(int argc, char ** argv) (openssl-1.1.1q/apps/verify.c:190)
do_cmd(struct lhash_st_FUNCTION * prog, int argc, char ** argv) (openssl-1.1.1q/apps/openssl.c:568)
main(int argc, char ** argv) (openssl-1.1.1q/apps/openssl.c:188)
其中X509_NAME_cmp如下
int X509_NAME_cmp(const X509_NAME *a, const X509_NAME *b)
{
int ret;
/* Ensure canonical encoding is present and up to date */
if (!a->canon_enc || a->modified) {
ret = i2d_X509_NAME((X509_NAME *)a, NULL);
if (ret < 0)
return -2;
}
if (!b->canon_enc || b->modified) {
ret = i2d_X509_NAME((X509_NAME *)b, NULL);
if (ret < 0)
return -2;
}
ret = a->canon_enclen - b->canon_enclen;
if (ret != 0 || a->canon_enclen == 0)
return ret;
//主要就是这边对比
return memcmp(a->canon_enc, b->canon_enc, a->canon_enclen);
}
安装Hex Editor扩展插件,断点断下可以观察a->canon和b->canon的值,在变量窗口对应变量值的右侧的16进制图标打开进行查看。
其中证书的canon的值是在证书加载完的时候就已经写入了。
计算root-cert2.pem和root-cert.pem的subject哈希是相同的,都是转化成小写后的计算的哈希,即
case_order_test$ openssl x509 -in root-cert.pem -noout -subject_hash
bead1505
case_order_test$ openssl x509 -in root-cert2.pem -noout -subject_hash
bead1505
case_order_test$
使用命令调试(case_order_test$ openssl x509 -in root-cert.pem -noout -subject_hash), 如下
X509 *load_cert(const char *file, int format, const char *cert_descrip)
{
X509 *x = NULL;
BIO *cert;
if (format == FORMAT_HTTP) {
#if !defined(OPENSSL_NO_OCSP) && !defined(OPENSSL_NO_SOCK)
load_cert_crl_http(file, &x, NULL);
#endif
return x;
}
if (file == NULL) {
unbuffer(stdin);
cert = dup_bio_in(format);
} else {
cert = bio_open_default(file, 'r', format);
}
if (cert == NULL)
goto end;
if (format == FORMAT_ASN1) {
x = d2i_X509_bio(cert, NULL);
} else if (format == FORMAT_PEM) {
//下面这个读取后canon就有值了
x = PEM_read_bio_X509_AUX(cert, NULL,
(pem_password_cb *)password_callback, NULL);
} else if (format == FORMAT_PKCS12) {
if (!load_pkcs12(cert, cert_descrip, NULL, NULL, NULL, &x, NULL))
goto end;
} else {
BIO_printf(bio_err, "bad input format specified for %s\n", cert_descrip);
goto end;
}
end:
if (x == NULL) {
BIO_printf(bio_err, "unable to load certificate\n");
ERR_print_errors(bio_err);
}
BIO_free(cert);
return x;
}
//堆栈:
load_cert(const char * file, int format, const char * cert_descrip) (openssl-1.1.1q/apps/apps.c:679)
check(X509_STORE * ctx, const char * file, struct stack_st_X509 * uchain, struct stack_st_X509 * tchain, struct stack_st_X509_CRL * crls, int show_chain) (openssl-1.1.1q/apps/verify.c:215)
verify_main(int argc, char ** argv) (openssl-1.1.1q/apps/verify.c:190)
do_cmd(struct lhash_st_FUNCTION * prog, int argc, char ** argv) (openssl-1.1.1q/apps/openssl.c:568)
main(int argc, char ** argv) (openssl-1.1.1q/apps/openssl.c:188)
hexdump的x->cert_info->subject->canon_enc的值如下:
对比如下命令的值:
$ openssl x509 -in root-cert2.pem -outform DER | openssl asn1parse -inform DER -i
0:d=0 hl=4 l= 575 cons: SEQUENCE
4:d=1 hl=4 l= 485 cons: SEQUENCE
8:d=2 hl=2 l= 3 cons: cont [ 0 ]
10:d=3 hl=2 l= 1 prim: INTEGER :02
13:d=2 hl=2 l= 20 prim: INTEGER :289A28DF91B4B843D62318AE24DAABEBD01C1B2A
35:d=2 hl=2 l= 10 cons: SEQUENCE
37:d=3 hl=2 l= 8 prim: OBJECT :ecdsa-with-SHA256
47:d=2 hl=2 l= 117 cons: SEQUENCE
49:d=3 hl=2 l= 13 cons: SET
51:d=4 hl=2 l= 11 cons: SEQUENCE
53:d=5 hl=2 l= 3 prim: OBJECT :commonName
58:d=5 hl=2 l= 4 prim: UTF8STRING :root
64:d=3 hl=2 l= 11 cons: SET
66:d=4 hl=2 l= 9 cons: SEQUENCE
68:d=5 hl=2 l= 3 prim: OBJECT :countryName
73:d=5 hl=2 l= 2 prim: PRINTABLESTRING :CN
77:d=3 hl=2 l= 21 cons: SET
79:d=4 hl=2 l= 19 cons: SEQUENCE
81:d=5 hl=2 l= 3 prim: OBJECT :stateOrProvinceName
86:d=5 hl=2 l= 12 prim: UTF8STRING :rootprovince
100:d=3 hl=2 l= 17 cons: SET
102:d=4 hl=2 l= 15 cons: SEQUENCE
104:d=5 hl=2 l= 3 prim: OBJECT :localityName
109:d=5 hl=2 l= 8 prim: UTF8STRING :rootcity
119:d=3 hl=2 l= 25 cons: SET
121:d=4 hl=2 l= 23 cons: SEQUENCE
123:d=5 hl=2 l= 3 prim: OBJECT :organizationName
128:d=5 hl=2 l= 16 prim: UTF8STRING :ROOTorganization
146:d=3 hl=2 l= 18 cons: SET
148:d=4 hl=2 l= 16 cons: SEQUENCE
150:d=5 hl=2 l= 3 prim: OBJECT :organizationalUnitName
155:d=5 hl=2 l= 9 prim: UTF8STRING :rootgroup
166:d=2 hl=2 l= 30 cons: SEQUENCE
168:d=3 hl=2 l= 13 prim: UTCTIME :250825085420Z
183:d=3 hl=2 l= 13 prim: UTCTIME :350823085420Z
#如上命令可以看出subject的偏移在47, dump对应的16进制如下,
这个是der编码值,开始两个字节30表示SEQUENCE,而75表示长度,
后面的0x75个字节的数据与上面截图中显示的canon_enc的值是相同的,
除了把值保留原来的大小写,而不是像canon_enc全部转化成小写,
看下面的ROOTorganization字符串
$ openssl x509 -in root-cert2.pem -outform DER | openssl asn1parse -inform DER -strparse 47 -i -noout -out - | hexdump -C
00000000 30 75 31 0d 30 0b 06 03 55 04 03 0c 04 72 6f 6f |0u1.0...U....roo|
00000010 74 31 0b 30 09 06 03 55 04 06 13 02 43 4e 31 15 |t1.0...U....CN1.|
00000020 30 13 06 03 55 04 08 0c 0c 72 6f 6f 74 70 72 6f |0...U....rootpro|
00000030 76 69 6e 63 65 31 11 30 0f 06 03 55 04 07 0c 08 |vince1.0...U....|
00000040 72 6f 6f 74 63 69 74 79 31 19 30 17 06 03 55 04 |rootcity1.0...U.|
00000050 0a 0c 10 52 4f 4f 54 6f 72 67 61 6e 69 7a 61 74 |...ROOTorganizat|
00000060 69 6f 6e 31 12 30 10 06 03 55 04 0b 0c 09 72 6f |ion1.0...U....ro|
00000070 6f 74 67 72 6f 75 70 |otgroup|
00000077
上面的subject属性没有可见的key字符串,因为显示的属性key字段都是OBJECT IDENTIFIER标签和对象标识符来标识,而显示的属性值都是小写,对比下面下面的subject各属性
case_order_test$ openssl x509 -in root-cert2.pem -noout -subject
subject=CN = root, C = CN, ST = rootprovince, L = rootcity, O = ROOTorganization, OU = rootgroup
来自ai解码如下,可以对比上面的strparse:
这段编码是证书的subject的DER编码,具体是证书主题(Subject)字段的ASN.1 DER编码形式。
DER编码是一种二进制编码格式,用于表示ASN.1定义的数据结构。ASN.1(Abstract Syntax Notation One)是一种用于描述数据结构的标准语言,常用于网络协议和加密证书中。
在这个例子中,证书的subject字段包含了多个相对可识别名(RDN,Relative Distinguished Name)属性,每个属性都由一个类型和一个值组成。
下面是对这段编码的逐项解释:
30 75:这是一个SEQUENCE类型的标签,表示接下来的数据是一个序列。75是序列的长度(以字节为单位)。
31 0d:这是一个SET类型的标签,表示接下来的数据是一个集合。0d是集合的长度。
30 0b:这是一个SEQUENCE类型的标签,表示接下来的数据是一个序列。0b是序列的长度。
06 03:这是一个OBJECT IDENTIFIER类型的标签,表示接下来的数据是一个对象标识符。03是对象标识符的长度。
55 04 03:这是对象标识符的具体值,表示commonName(CN)属性。
0c 04:这是一个UTF8String类型的标签,表示接下来的数据是一个UTF-8编码的字符串。04是字符串的长度。
72 6f 6f 74:这是字符串的具体值,即root
....
剩余字段类似
调试命令:
openssl verify -CAfile root-cert.pem root-cert2.pem
核心堆栈:
libcrypto.so.1.1!X509_cmp(const X509 * a, const X509 * b) (openssl-1.1.1q/crypto/x509/x509_cmp.c:149)
libcrypto.so.1.1!build_chain(X509_STORE_CTX * ctx) (openssl-1.1.1q/crypto/x509/x509_vfy.c:3141)
libcrypto.so.1.1!verify_chain(X509_STORE_CTX * ctx) (openssl-1.1.1q/crypto/x509/x509_vfy.c:217)
libcrypto.so.1.1!X509_verify_cert(X509_STORE_CTX * ctx) (openssl-1.1.1q/crypto/x509/x509_vfy.c:303)
check(X509_STORE * ctx, const char * file, struct stack_st_X509 * uchain, struct stack_st_X509 * tchain, struct stack_st_X509_CRL * crls, int show_chain) (openssl-1.1.1q/apps/verify.c:237)
verify_main(int argc, char ** argv) (openssl-1.1.1q/apps/verify.c:190)
do_cmd(struct lhash_st_FUNCTION * prog, int argc, char ** argv) (openssl-1.1.1q/apps/openssl.c:568)
main(int argc, char ** argv) (openssl-1.1.1q/apps/openssl.c:188)
/*
* Compare two certificates: they must be identical for this to work. NB:
* Although "cmp" operations are generally prototyped to take "const"
* arguments (eg. for use in STACKs), the way X509 handling is - these
* operations may involve ensuring the hashes are up-to-date and ensuring
* certain cert information is cached. So this is the point where the
* "depth-first" constification tree has to halt with an evil cast.
*/
int X509_cmp(const X509 *a, const X509 *b)
{
int rv = 0;
if (a == b) /* for efficiency */
return 0;
/* try to make sure hash is valid */
(void)X509_check_purpose((X509 *)a, -1, 0);
(void)X509_check_purpose((X509 *)b, -1, 0);
if ((a->ex_flags & EXFLAG_NO_FINGERPRINT) == 0
&& (b->ex_flags & EXFLAG_NO_FINGERPRINT) == 0)
//如下比较证书指纹的时候不同,证书指纹,是证书der编码的sha1哈希(默认)
rv = memcmp(a->sha1_hash, b->sha1_hash, SHA_DIGEST_LENGTH);
if (rv != 0)
return rv;
/* Check for match against stored encoding too */
if (!a->cert_info.enc.modified && !b->cert_info.enc.modified) {
if (a->cert_info.enc.len < b->cert_info.enc.len)
return -1;
if (a->cert_info.enc.len > b->cert_info.enc.len)
return 1;
return memcmp(a->cert_info.enc.enc, b->cert_info.enc.enc,
a->cert_info.enc.len);
}
return rv;
}
计算证书指纹,可以知道root-cert2.pem和root-cert.pem是不同的:
#若重新生成,因为证书有效时间不同,算出来也会不同
$ openssl x509 -in root-cert2.pem -noout -fingerprint
SHA1 Fingerprint=B7:1F:6B:2A:8E:C4:12:DB:18:0D:94:EF:CB:99:77:80:49:F6:3D:60
$ openssl x509 -in root-cert.pem -noout -fingerprint
SHA1 Fingerprint=BB:DC:E7:4B:6A:CF:AB:24:AC:42:9A:3C:E9:78:91:33:BF:AE:AF:2B
x509.c中代码片段:
else if (fingerprint == i) {
int j;
unsigned int n;
unsigned char md[EVP_MAX_MD_SIZE];
const EVP_MD *fdig = digest;
if (fdig == NULL)
fdig = EVP_sha1();
if (!X509_digest(x, fdig, md, &n)) {
BIO_printf(bio_err, "out of memory\n");
goto end;
}
BIO_printf(out, "%s Fingerprint=",
OBJ_nid2sn(EVP_MD_type(fdig)));
for (j = 0; j < (int)n; j++) {
BIO_printf(out, "%02X%c", md[j], (j + 1 == (int)n)
? '\n' : ':');
}
}
通过调试分析会发现,证书指纹是整个证书的der编码的哈希值,并且subject和issuer中的大小写保持原样并不会转化成小写。对比如下命令的输出就可以看出来:
$ openssl x509 -in root-cert2.pem -noout -fingerprint
SHA1 Fingerprint=B7:1F:6B:2A:8E:C4:12:DB:18:0D:94:EF:CB:99:77:80:49:F6:3D:60
$ openssl x509 -in root-cert2.pem -outform DER | sha1sum | awk '{print $1}' | xxd -r -p | xxd -p | sed 's/\(..\)/\1:/g; s/:$//' | tr '[:lower:]' '[:upper:]'
B7:1F:6B:2A:8E:C4:12:DB:18:0D:94:EF:CB:99:77:80:49:F6:3D:60
openssl x509 -in root-cert2.pem -outform DER | hexdump -C
从生成证书的过程可以看出两个证书有几个不同,可以用命令 openssl x509 -in root-cert.pem -noout -text命令查看。
一个serial是随机生成的不相同,虽然证书的有效期是365天,但是证书的Not Before的时间取自当前系统的时间,两个命令生成的时间有间隔所以Not Before和Not After也不同,最后即使这些字段全部相同,由于ECDSA的签名中有随机数(会随机选择曲线上的点),故最后的签名结果也不同,要生成指纹相同的两个证书,必须修改ECDSA签名算法的源码固定随机数了。这边省略。
相关脚本如下:
test2.sh
#生成私钥
openssl ecparam -name prime256v1 -out root-key.pem -genkey
#生成证书请求
subject="/CN=root/C=CN/ST=rootprovince/L=rootcity/O=rootorganization/OU=rootgroup";
openssl req -new -extensions v3_ca -key root-key.pem -out root-cert.csr -subj "${subject}";
#创建CA相关文件仓库
mkdir -p demoCA/newcerts
touch demoCA/index.txt
#创建序列号起始编号,ca每签发一个证书会递增
echo 01 > demoCA/serial
#签发自签名根证书,使用-startdate和-enddate来固定Not Before和Not After的时间
openssl ca -batch -config /etc/ssl/openssl.cnf \
-in root-cert.csr \
-out root-cert.pem \
-startdate 20000101000000Z \
-enddate 21001231235959Z \
-extensions v3_ca \
-keyfile root-key.pem \
-selfsign
#创建CA相关文件仓库
rm -rf demoCA
mkdir -p demoCA/newcerts
touch demoCA/index.txt
#创建序列号起始编号,ca每签发一个证书会递增
echo 01 > demoCA/serial
#签发自签名根证书,证书请求、序列号等所有参数都一样,再次签发
openssl ca -batch -config /etc/ssl/openssl.cnf \
-in root-cert.csr \
-out root-cert1.pem \
-startdate 20000101000000Z \
-enddate 21001231235959Z \
-extensions v3_ca \
-keyfile root-key.pem \
-selfsign
#由于ECDSA签名有随机数,即使相同的数据,多次签名结果也不同
diff <(openssl x509 -in root-cert.pem -noout -text) <(openssl x509 -in root-cert1.pem -noout -text)
echo root-cert.pem: $(openssl x509 -in root-cert.pem -noout -fingerprint)
echo root-cert1.pem: $(openssl x509 -in root-cert1.pem -noout -fingerprint)
#生成证书请求, subject的root大写
subject="/CN=ROOT/C=CN/ST=ROOTprovince/L=ROOTcity/O=ROOTorganization/OU=ROOTgroup";
openssl req -new -extensions v3_ca -key root-key.pem -out root-cert2.csr -subj "${subject}";
#创建CA相关文件仓库
rm -rf demoCA
mkdir -p demoCA/newcerts
touch demoCA/index.txt
#创建序列号起始编号,ca每签发一个证书会递增
echo 01 > demoCA/serial
#签发自签名根证书,请求使用大写ROOT的subject, 使用-startdate和-enddate来固定Not Before和Not After的时间
openssl ca -batch -config /etc/ssl/openssl.cnf \
-in root-cert2.csr \
-out root-cert2.pem \
-startdate 20000101000000Z \
-enddate 21001231235959Z \
-extensions v3_ca \
-keyfile root-key.pem \
-selfsign
#对比差异
diff <(openssl x509 -in root-cert.pem -noout -text) <(openssl x509 -in root-cert2.pem -noout -text)
输入如下:
$ ./test2.sh
Using configuration from /etc/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Jan 1 00:00:00 2000 GMT
Not After : Dec 31 23:59:59 2100 GMT
Subject:
countryName = CN
stateOrProvinceName = rootprovince
organizationName = rootorganization
organizationalUnitName = rootgroup
commonName = root
X509v3 extensions:
X509v3 Subject Key Identifier:
35:47:C5:6A:24:CE:17:39:ED:44:98:06:6D:33:CE:AD:FC:EA:4E:3A
X509v3 Authority Key Identifier:
keyid:35:47:C5:6A:24:CE:17:39:ED:44:98:06:6D:33:CE:AD:FC:EA:4E:3A
X509v3 Basic Constraints: critical
CA:TRUE
Certificate is to be certified until Dec 31 23:59:59 2100 GMT (27516 days)
Write out database with 1 new entries
Data Base Updated
Using configuration from /etc/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Jan 1 00:00:00 2000 GMT
Not After : Dec 31 23:59:59 2100 GMT
Subject:
countryName = CN
stateOrProvinceName = rootprovince
organizationName = rootorganization
organizationalUnitName = rootgroup
commonName = root
X509v3 extensions:
X509v3 Subject Key Identifier:
35:47:C5:6A:24:CE:17:39:ED:44:98:06:6D:33:CE:AD:FC:EA:4E:3A
X509v3 Authority Key Identifier:
keyid:35:47:C5:6A:24:CE:17:39:ED:44:98:06:6D:33:CE:AD:FC:EA:4E:3A
X509v3 Basic Constraints: critical
CA:TRUE
Certificate is to be certified until Dec 31 23:59:59 2100 GMT (27516 days)
Write out database with 1 new entries
Data Base Updated
31,34c31,34
< 30:45:02:21:00:9f:3e:7f:40:3f:4c:55:b5:ec:93:bc:01:e7:
< d7:35:08:99:1d:0f:88:9a:ac:79:d3:9f:c0:7f:0b:2d:fe:a3:
< 01:02:20:55:3f:61:f7:e0:22:de:60:56:34:f2:cc:2f:3f:b2:
< bc:96:c4:32:99:ad:33:03:ec:8d:14:b1:37:0b:58:e3:37
---
> 30:45:02:20:04:0f:c5:dd:0b:30:17:b5:84:f3:75:af:5c:21:
> c6:23:fb:82:40:f2:05:01:57:1c:7c:ae:44:5b:af:1d:01:2b:
> 02:21:00:bf:7b:88:1b:2d:9f:db:05:d7:20:9a:34:39:62:88:
> 0f:77:64:f1:47:d4:9e:97:22:f4:97:55:eb:31:0a:6d:8b
root-cert.pem: SHA1 Fingerprint=8F:8F:2F:2D:23:90:6A:68:B2:FD:F9:B1:A1:09:56:02:90:D3:61:2F
root-cert1.pem: SHA1 Fingerprint=D2:C8:E9:06:7F:CD:81:83:7F:0F:8F:AA:0A:07:F8:93:A0:4A:AE:E4
Using configuration from /etc/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
Serial Number: 1 (0x1)
Validity
Not Before: Jan 1 00:00:00 2000 GMT
Not After : Dec 31 23:59:59 2100 GMT
Subject:
countryName = CN
stateOrProvinceName = ROOTprovince
organizationName = ROOTorganization
organizationalUnitName = ROOTgroup
commonName = ROOT
X509v3 extensions:
X509v3 Subject Key Identifier:
35:47:C5:6A:24:CE:17:39:ED:44:98:06:6D:33:CE:AD:FC:EA:4E:3A
X509v3 Authority Key Identifier:
keyid:35:47:C5:6A:24:CE:17:39:ED:44:98:06:6D:33:CE:AD:FC:EA:4E:3A
X509v3 Basic Constraints: critical
CA:TRUE
Certificate is to be certified until Dec 31 23:59:59 2100 GMT (27516 days)
Write out database with 1 new entries
Data Base Updated
6c6
< Issuer: C = CN, ST = rootprovince, O = rootorganization, OU = rootgroup, CN = root
---
> Issuer: C = CN, ST = ROOTprovince, O = ROOTorganization, OU = ROOTgroup, CN = ROOT
10c10
< Subject: C = CN, ST = rootprovince, O = rootorganization, OU = rootgroup, CN = root
---
> Subject: C = CN, ST = ROOTprovince, O = ROOTorganization, OU = ROOTgroup, CN = ROOT
31,34c31,34
< 30:45:02:21:00:9f:3e:7f:40:3f:4c:55:b5:ec:93:bc:01:e7:
< d7:35:08:99:1d:0f:88:9a:ac:79:d3:9f:c0:7f:0b:2d:fe:a3:
< 01:02:20:55:3f:61:f7:e0:22:de:60:56:34:f2:cc:2f:3f:b2:
< bc:96:c4:32:99:ad:33:03:ec:8d:14:b1:37:0b:58:e3:37
---
> 30:45:02:20:21:2b:c6:ad:f1:cc:34:a0:ff:40:61:c9:78:82:
> e8:08:58:d5:f9:e6:da:cb:45:44:9a:01:cb:75:82:07:de:7d:
> 02:21:00:d1:39:1c:75:36:09:30:23:bb:62:b8:28:ac:fd:c7:
> e7:93:3e:38:d7:90:04:9d:81:71:3c:35:39:dc:bb:4e:f5
下载openssl源码, 编译调试版本
./config --debug
make
然后在vs code工程根目录添加.vscode/lauch.json启动配置文件,然后直接按F5开启调试运行
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 启动",
"type": "cppdbg",
"request": "launch",
"program": "/home/x/code/third/openssl-1.1.1q//apps/openssl",
//"program": "${workspaceFolder}/build/my_tls",
"args": [
"verify",
"-CAfile",
"root-cert.pem",
"root-cert2.pem"
],
"stopAtEntry": true,
//"cwd": "/tmp/case_order_test",
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "LD_LIBRARY_PATH",
"value": "${LD_LIBRARY_PATH}:/home/x/code/third/openssl-1.1.1q/"
}
],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "设置调试符号路径",
"text": "set solib-search-path /home/x/code/third/aarch64/openssl-1.1.1q/:/home/x/Downloads/test/my_server_client/:/home/x/code/nxp-s32g/libp11-0.4.11",
"ignoreFailures": true
},
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}
如果需要自己编写openssl应用代码调试分析可以编写CMake使用RPATH路径绑定链接调试版本的openssl库,如下是本人分析openssl 3.x版本provider的CMake的demo样例,
在openssl3.x的源码下建立一个子目录my_demo,创建cmake工程文件使用的cmake
cmake_minimum_required(VERSION 3.10)
project(MyOpenSSLProvider CXX)
#设置调试
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 设置 OpenSSL 路径
set(OPENSSL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}/..)
set(OPENSSL_INCLUDE_DIR ${OPENSSL_ROOT_DIR}/include)
set(OPENSSL_CRYPTO_LIBRARY ${OPENSSL_ROOT_DIR}/libcrypto.so)
set(OPENSSL_SSL_LIBRARY ${OPENSSL_ROOT_DIR}/libssl.so)
# 包含 OpenSSL 头文件目录
include_directories(
${OPENSSL_INCLUDE_DIR} ${OPENSSL_ROOT_DIR}
${OPENSSL_ROOT_DIR}/providers/common/include ${OPENSSL_ROOT_DIR}/crypto/ec
${OPENSSL_ROOT_DIR}/providers/implementations/include)
# 创建可执行文件
add_executable(my_provider src/my_provider.cpp)
# 链接 OpenSSL 库
target_link_libraries(my_provider ${OPENSSL_ROOT_DIR}/libssl.so
${OPENSSL_ROOT_DIR}/libcrypto.so)
# 设置运行时链接路径
set_target_properties(
my_provider
PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE
INSTALL_RPATH "${OPENSSL_ROOT_DIR}"
INSTALL_RPATH_USE_LINK_PATH FALSE)
target_compile_definitions(my_provider PRIVATE OPENSSL_SUPPRESS_DEPRECATED)
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。