您现在的位置是:首页 >学无止境 >openssl req.c代码分析网站首页学无止境
openssl req.c代码分析
OpenSSL req.c分析
此实现包括生成秘钥对,发证,生成证书请求, 签名验签。
-
加载配置文件模板,如果加载失败,退出. 默认配置文件使用环境变量OPENSSL_CONF 或者从宏OPENSSLDIR 获取OPENSSL的安装目录,然后获取配置文件
long errorline = -1; CONF *conf; int i; conf = NCONF_new(NULL); i = NCONF_load_bio(conf, in, &errorline); if (i > 0) return conf;
-
从配置文件中获取oid_file,文件内容格式Line format:<OID:isdigit or ‘.’]>,解析此文件
p = NCONF_get_string(req_conf, NULL, "oid_file");
- OBJ_txt2obj(const char *s int no_name),此函数可以解析OID,OID是按照1.2.34343这格式,
- 先计算这种格式数据内容占用的内存空间。方法a2d_ASN1_OBJECT(NULL, 0, s, -1);
- j = ASN1_object_size(0, i, V_ASN1_OBJECT); 计算对象占用的空间
- 分配总长度为j的内存空间p
- 【TLV】向分配的空间中写入tag和lenght 方法ASN1_put_object(&p, 0, i, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
- 写入内容 a2d_ASN1_OBJECT(p, i, s, -1);
- 生成object 调用方法 op = d2i_ASN1_OBJECT(NULL, &cp, j);
-
从配置文件的req区块获取默认摘要算法
-
获取OID
-
4.1 如果传入扩展为空,从配置文件req区块获取扩展x509_extensions
- extension = X509_EXTENSION_create_by_OBJ(NULL, obj, crit, oct);
- X509v3_add_ext(sk, ext, -1)
- 这个栈是在NCONF中的
-
4.2 如果有额外的配置文件,从额外的配置文件中也获取oid
-
4.3 从req区的req_extensions 获取OID
-
-
生成秘钥对
-
5.1 set_keygen_ctx方法分析,外部选项 {“newkey”, OPT_NEWKEY, ‘s’, “Specify as type:bits”}, 指定了秘钥的算法和位长度,分析此字符串,找到对应的算法
字符串截取判断
- 如果此字符串为空,认为是RSA类型
- 如果字符串的首个字符大于0小于9 认为是RSA,并认为此字符串代表了key的长度
- 如果此字符串前6个字符是param: 认为冒号后面是参数文件,给paramfile赋值
- 如果字符串是被冒号隔开的,那么获取冒号前面的字符串,认为是某种key类型,去标准pkey算法中查找ameth = EVP_PKEY_asn1_find_str(&tmpeng, gstr, len);
然后获取对应的算法的类型EVP_PKEY_asn1_get0_info(NULL, pkey_type, NULL, NULL, NULL, ameth); 如果类型是RSA认为冒号后面的是key的长度,否则认为冒号
后面的是参数文件
后续处理 - 如果参数文件不为空,读取参数文件 EVP_PKEY *param = PEM_read_bio_Parameters(pbio, NULL);
- 如果param为空,X509 *x = PEM_read_bio_X509(pbio, NULL, NULL, NULL); param = X509_get_pubkey(x);
- 创建evp_pkey_ctx,其中pkey_type就是EVP_PKEY_RSA之类的东西
if (param != NULL) { gctx = EVP_PKEY_CTX_new(param, keygen_engine); *pkeylen = EVP_PKEY_bits(param); EVP_PKEY_free(param); } else { gctx = EVP_PKEY_CTX_new_id(*pkey_type, keygen_engine); }
- 初始化 EVP_PKEY_keygen_init(gctx)
- 如果是RSA设置key长度EVP_PKEY_CTX_set_rsa_keygen_bits(gctx, keylen)
-
5.2 如果有key的选项,设置之pkey_ctrl_string(genctx, genopt)
-
5.3 生成秘钥对 EVP_PKEY_keygen(genctx, &pkey)
-
-
从配置文件req区获取encrypt_rsa_key 及encrypt_key字段,如果此字段是no,那么将加密key的对称算法置空
-
输出私钥 PEM_write_bio_PrivateKey(out, pkey, cipher,NULL, 0, NULL, passout)
-
如果没有newreq参数 ,从输入文件中加载证书请求
- d2i_X509_REQ_bio
- PEM_read_bio_X509_REQ
-
如果有参数newreq或者x509不为空
- 9.1 生成证书请求
-
从配置文件获取prompt参数,如果为no那就是不提示,如果是yes 会提示输入一些生成证书请求的信息
-
从配置文件获取distinguished_name参数,得到DN栈
-
从配置文件获取attributes参数,获取属性栈
-
设置证书请求版本号X509_REQ_set_version
-
如果用户传入了subj参数通过传入的subj,格式为/type0=value0/type1=value1/type2=… 解析生成
-
X509_NAME *n = X509_NAME_new() nid = OBJ_txt2nid(typestr) X509_NAME_add_entry_by_NID(n, nid, chtype, valstr, strlen((char *)valstr), -1, ismulti ? -1 : 0) X509_REQ_set_subject_name(req, n)
- 如果没有提示,从配置文件中获取dn和属性对证书请求对象进行填充
- 如果有提示,并没有发现里面有提示输入的代码,有一些对值的大小进行校验的代码
只处理配置项的name的后缀为_min,_max,_default,_value的项目 - 设置证书请求的公钥 X509_REQ_set_pubkey
- 签名
EVP_MD_CTX *mctx = EVP_MD_CTX_new(); EVP_DigestSignInit, EVP_PKEY_CTX_ctrl_str(ctx, stmp, vtmp) X509_REQ_sign_ctx
-
9.2 生成证书
- X509_new()
- 设置版本号X509_set_version(x509ss, 2)
- 设置序列号 X509_set_serialNumber
- 设置签发者X509_set_issuer_name
- 设置时效
int set_cert_times(X509 *x, const char *startdate, const char *enddate, int days) { if (startdate == NULL || strcmp(startdate, "today") == 0) { if (X509_gmtime_adj(X509_getm_notBefore(x), 0) == NULL) return 0; } else { if (!ASN1_TIME_set_string_X509(X509_getm_notBefore(x), startdate)) return 0; } if (enddate == NULL) { if (X509_time_adj_ex(X509_getm_notAfter(x), days, 0, NULL) == NULL) return 0; } else if (!ASN1_TIME_set_string_X509(X509_getm_notAfter(x), enddate)) { return 0; } return 1; }
- 设置主体 X509_set_subject_name
- 设置公钥 X509_set_pubkey
- 设置扩展,同证书请求
- 签名
EVP_MD_CTX *mctx = EVP_MD_CTX_new(); EVP_DigestSignInit EVP_PKEY_CTX_ctrl_str(ctx, stmp, vtmp) X509_sign_ctx
- 9.1 生成证书请求