最近我公司有一个项目(C++)和其它公司(Java)合作
出现的问题: java中的公私钥加解密都是调用其API函数,而C++中好像没有这方面的知识(至少以前我没有相关经验)
有一个.pfx文件和两个.cer文件
方法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就是我们需要的文件了
有一个奇怪的问题是:
我理解的是一般.der格式的为二进制文件, .cer为base64明文格式的,主要是因为这之前我所看到的文件格式都是这样的导致的,后来别的公司用了.cer的文件为二进制的文件,这就使我理解有点怪
搜索资料的说是:CER = .crt的替代形式(Microsoft Convention)您可以在微软系统环境下将.crt转换为.cer(.both DER编码的.cer,或base64 [PEM]编码的.cer)。
我字面上理解是说
.crt .cer .der 既可以是二进制的DER编码也可以是base64的PEM编码
也就是说这方面的后缀定义并未明确规定
.CRT = CRT扩展用于证书。 证书可以被编码为二进制DER或ASCII PEM。 CER和CRT扩展几乎是同义词。 最常见的于Unix 或类Unix系统。
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),该命令显示用于导入和/或查看证书内容的对话框。
.KEY = KEY扩展名用于公钥和私钥PKCS#8。 键可以被编码为二进制DER或ASCII PEM。
新的问题: 让我们提供一个公钥, 一般提供一个public.key(PEM编码)就可以了,但对方要public.cer(其实要DER编码)
可以直接使用站长工具:
https://www.sslshopper.com/ssl-converter.html
生成好的.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
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
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
// 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;
}