您现在的位置是:首页 >学无止境 >openssl req.c代码分析网站首页学无止境

openssl req.c代码分析

ughome 2023-05-12 20:15:03
简介openssl req.c源码分析

OpenSSL req.c分析

此实现包括生成秘钥对,发证,生成证书请求, 签名验签。

  1. 加载配置文件模板,如果加载失败,退出. 默认配置文件使用环境变量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;
    
  2. 从配置文件中获取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);
  3. 从配置文件的req区块获取默认摘要算法

  4. 获取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. 生成秘钥对

    • 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)

  6. 从配置文件req区获取encrypt_rsa_key 及encrypt_key字段,如果此字段是no,那么将加密key的对称算法置空

  7. 输出私钥 PEM_write_bio_PrivateKey(out, pkey, cipher,NULL, 0, NULL, passout)

  8. 如果没有newreq参数 ,从输入文件中加载证书请求

    • d2i_X509_REQ_bio
    • PEM_read_bio_X509_REQ
  9. 如果有参数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
      		
      
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。