C++的sha256WithRSA签名

最近我公司有一个项目(C++)和其它公司(Java)合作
出现的问题: java中的公私钥加解密都是调用其API函数,而C++中好像没有这方面的知识(至少以前我没有相关经验)
有一个.pfx文件和两个.cer文件


1 从.pfx中获取key文件

方法1

原版PFX证书
openssl pkcs12 -in myssl.pfx -nodes -out server.pem (这里的pem格式为明文可以打开)

提取私钥 
openssl rsa -in server.pem -out server.key

提出公钥
openssl x509 -in server.pem -out server.crt

方法2, 我使用的该方法

1)提取密钥对 
openssl pkcs12 -in 1.pfx -nocerts -nodes -out 1.key 
//如果pfx证书已加密,会提示输入密码。如果cer证书没有安装,则密码没法验证
2)从密钥对提取私钥 
openssl rsa -in 1.key -out 1_pri.key

(3)从密钥对提取公钥 
openssl rsa -in 1.key -pubout -out 1_pub.key

(4)因为RSA算法使用的是pkcs8模式补足,需要对提取的私钥进一步处理 
openssl pkcs8 -topk8 -inform PEM -in 1_pri.key -outform PEM -nocrypt -out 1_pri_pkcs8.key

最终当前目录下的1_pri_pkcs8.key就是我们需要的文件了

2 获取cer文件

有一个奇怪的问题是:
我理解的是一般.der格式的为二进制文件, .cer为base64明文格式的,主要是因为这之前我所看到的文件格式都是这样的导致的,后来别的公司用了.cer的文件为二进制的文件,这就使我理解有点怪

搜索资料的说是:CER = .crt的替代形式(Microsoft Convention)您可以在微软系统环境下将.crt转换为.cer(.both DER编码的.cer,或base64 [PEM]编码的.cer)。

C++的sha256WithRSA签名_第1张图片

我字面上理解是说
.crt .cer .der 既可以是二进制的DER编码也可以是base64的PEM编码
也就是说这方面的后缀定义并未明确规定


  • 搜索的资料

1).CRT 扩展名

.CRT = CRT扩展用于证书。 证书可以被编码为二进制DER或ASCII PEM。 CER和CRT扩展几乎是同义词。 最常见的于Unix 或类Unix系统。

2).CER扩展名

CER = .crt的替代形式(Microsoft Convention)您可以在微软系统环境下将.crt转换为.cer(.both DER编码的.cer,或base64 [PEM]编码的.cer)。

可参考:https://support.comodo.com/index.php?/Knowledgebase/Article/View/361/17/how-do-i-convert-crt-file-into-the-microsoft-cer-format

.cer文件扩展名也被IE识别为 一个运行MS cryptoAPI命令的命令(特别是rundll32.exe cryptext.dll,CryptExtOpenCER),该命令显示用于导入和/或查看证书内容的对话框。

3).KEY 扩展名
 .KEY = KEY扩展名用于公钥和私钥PKCS#8。 键可以被编码为二进制DER或ASCII PEM。

新的问题: 让我们提供一个公钥, 一般提供一个public.key(PEM编码)就可以了,但对方要public.cer(其实要DER编码)

可以直接使用站长工具:
https://www.sslshopper.com/ssl-converter.html
C++的sha256WithRSA签名_第2张图片
C++的sha256WithRSA签名_第3张图片
生成好的.der 直接 改后缀名为 .cer 给对方就可以使用

也可以自己使用命令搞定

// Convert PFX to PEM
openssl pkcs12 -in certificate.pfx -out certificate.cer -nodes

// Convert PEM to DER
openssl x509 -outform der -in certificate.pem -out certificate.der

1)查看PEM编码证书
openssl x509 -in cert.pem -text -noout

openssl x509 -in cert.cer -text -noout

openssl x509 -in cert.crt -text -noout

如果您遇到这个错误,这意味着您正在尝试查看DER编码的证书,并需要使用“查看DER编码证书”中的命令。
unable to load certificate

12626:error:0906D06C:PEMroutines:PEM_read_bio:no start line:pem_lib.c:647:Expecting: TRUSTEDCERTIFICATE

2)查看DER编码证书
openssl x509 -in certificate.der -inform der -text -noout

如果您遇到以下错误,则表示您尝试使用DER编码证书的命令查看PEM编码证书。在“查看PEM编码的证书”中使用命令
unable to load certificate

13978:error:0D0680A8:asn1 encodingroutines:ASN1_CHECK_TLEN:wrong tag:tasn_dec.c:1306:

13978:error:0D07803A:asn1 encodingroutines:ASN1_ITEM_EX_D2I:nested asn1 error:tasn_dec.c:380:Type=X509


// 这里使用该种方法查看
openssl.exe x509 -in 1_public.der -inform der -text -noout

C++的sha256WithRSA签名_第4张图片


// base64编码
char * Base64Encode(const char * input, int length, bool with_new_line)
{
	BIO * bmem = NULL;
	BIO * b64 = NULL;
	BUF_MEM * bptr = NULL;

	b64 = BIO_new(BIO_f_base64());
	if(!with_new_line) {
		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
	}
	bmem = BIO_new(BIO_s_mem());
	b64 = BIO_push(b64, bmem);
	BIO_write(b64, input, length);
	BIO_flush(b64);
	BIO_get_mem_ptr(b64, &bptr);

	char * buff = (char *)malloc(bptr->length + 1);
	memcpy(buff, bptr->data, bptr->length);
	buff[bptr->length] = 0;

	BIO_free_all(b64);

	return buff;
}

// base64解码
char * Base64Decode(char * input, int length, bool with_new_line)
{
	BIO * b64 = NULL;
	BIO * bmem = NULL;
	char * buffer = (char *)malloc(length);
	memset(buffer, 0, length);

	b64 = BIO_new(BIO_f_base64());
	if(!with_new_line) {
		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
	}
	bmem = BIO_new_mem_buf(input, length);
	bmem = BIO_push(b64, bmem);
	BIO_read(bmem, buffer, length);

	BIO_free_all(bmem);

	return buffer;
}

int my_load_cert(unsigned char *str, unsigned long *str_len,
	const char *verify_cert, const unsigned int cert_len)
{
	FILE *fp;
	fp = fopen(verify_cert, "rb");
	if ( NULL == fp)
	{
		fprintf(stderr, "fopen fail\n");
		return -1;
	}

	*str_len = fread(str, 1, cert_len, fp);
	fclose(fp);
	return 0;
}

std::string get_X509_serialNumber()
{
	unsigned char ca_der[MAX_LEGTH];
	unsigned long ca_der_len;

	my_load_cert(ca_der, &ca_der_len, "verify_sign.cer", MAX_LEGTH);

	X509 *x = der_to_x509((const unsigned char *)ca_der, ca_der_len);

	ASN1_INTEGER *bs = NULL;
	char *res = NULL;
	BIGNUM	 *bn = NULL;
	bs = X509_get_serialNumber(x);
	if (bs->length == 0) {
		printf("X509_get_serialNumber()  length=0 error!\n");
		return 0;
	}
	bn = ASN1_INTEGER_to_BN(bs, NULL);
	res = BN_bn2hex(bn);
	printf("serial = %s\n", res);

	unsigned long long num = hexToDec(res);

	std::string str_num;
	char cnum[50];
	sprintf(cnum, "%llu", num); // 将整数转换成字符串
	str_num=cnum; // 结果

	OPENSSL_free(res);
	res = NULL;
	BN_free(bn);
	bn = NULL;

	return str_num;
}


std::string testReadRSAFromPEM(std::string& data)
{	
	std::wstring signature =  stringToWstring(data);

	char* test = UnicodeToUtf8(signature.c_str());
	std::string sig_hex = mysha256(test);
	if(test != NULL)
	{
		free(test);
		test = NULL;
	}

	RSA *prikey = RSA_new();


	BIO* priio = BIO_new_file("1_pri_pkcs8.key", "rb");
	if (priio == NULL)
		return "";

	prikey = PEM_read_bio_RSAPrivateKey(priio, &prikey, NULL, NULL);
	int nLen = RSA_size(prikey);
	EVP_PKEY *evpKey = EVP_PKEY_new();//新建一个EVP_PKEY变量
	if(evpKey == NULL)
	{
		printf("EVP_PKEY_new err\n");
		RSA_free(prikey);
		BIO_free(priio);
		return "";
	}
	if(EVP_PKEY_set1_RSA(evpKey, prikey) != 1)	//保存RSA结构体到EVP_PKEY结构体
	{
		printf("EVP_PKEY_set1_RSA err\n");
		EVP_PKEY_free(evpKey);
		RSA_free(prikey);
		BIO_free(priio);
		return "";
	}

	EVP_MD_CTX mdctx;		//摘要算法上下文变量
	//以下是计算签名代码
	EVP_MD_CTX_init(&mdctx);//初始化摘要上下文
	if(!EVP_SignInit_ex(&mdctx, EVP_sha256(), NULL))//签名初始化,设置摘要算法
	{
		printf("err\n");
		EVP_PKEY_free(evpKey);
		RSA_free(prikey);
		BIO_free(priio);
		return "";
	}

	if(!EVP_SignUpdate(&mdctx, sig_hex.c_str(), sig_hex.size()))//计算签名(摘要)Update
	{
		printf("err\n");
		EVP_PKEY_free(evpKey);
		RSA_free(prikey);
		BIO_free(priio);
		return "";
	} 

	unsigned char sign_value[1024];	//保存签名值的数组
	unsigned int sign_len;			//签名值长度

	if(!EVP_SignFinal(&mdctx,sign_value,&sign_len,evpKey))	//签名输出
	{
		printf("err\n");
		EVP_PKEY_free(evpKey);
		RSA_free(prikey);
		BIO_free(priio);
		return "";
	}
	printf("消息\"%s\"的签名值是: \n",sig_hex);

	for(int i = 0; i < sign_len; i++)
	{
		if(i%16==0)
			printf("\n%08xH: ",i);
		printf("%02x ", sign_value[i]);	
	}
	printf("\n");	
	EVP_MD_CTX_cleanup(&mdctx);

	char* ret_base64 = Base64Encode((const char*)sign_value, sign_len, false);
	std::string retu;
	if (ret_base64 != NULL){
		std::string ret(ret_base64);
		free(ret_base64);
		retu = ret;
	}
	
	RSA_free(prikey);
	BIO_free(priio);

	return retu;
}

你可能感兴趣的:(C++)