C++使用OpenSSL证书API
程序员文章站
2022-07-12 22:56:29
...
本文主要介绍如果在C++中使用OpenSSL的证书相关API。(基于OpenSSL 1.0.2k版本,不同版本可能API会有一些差异,但大体应该类似)
使用下面方法前,需要全局调用一次(无需多次调用)
OpenSSL_add_all_algorithms();
1、生成公私钥对
BIGNUM *bne = NULL;
int bits = RSAKeyBits;
unsigned long e = RSA_F4;
int ret = 0;
bne = BN_new();
int r = BN_set_word(bne,e);
if(r != 1){
goto free_all;
}
if(m_rsa)
{
RSA_free(m_rsa);
m_rsa = NULL;
}
// m_rsa是成员变量,用于存储公私钥对
m_rsa = RSA_new();
r = RSA_generate_key_ex(m_rsa, bits, bne, NULL);
if(r != 1){
goto free_all;
}
free_all:
BN_free(bne);
2、生成CSR
X509 *x509 = NULL;
X509_NAME *subject = NULL;
BIO *bio = NULL;
X509_REQ *x509Req = NULL;
char *szCSR = NULL;
// 提取私钥
EVP_PKEY_assign_RSA(m_pKey, m_rsa);
x509 = X509_new();
X509_set_pubkey(x509, m_pKey);
// 设置属性
subject = X509_get_subject_name(x509);
// 国家
X509_NAME_add_entry_by_txt(subject, SN_countryName, MBSTRING_UTF8,
(unsigned char *)"CN", -1, -1, 0);
// 省份
X509_NAME_add_entry_by_txt(subject, SN_stateOrProvinceName, MBSTRING_UTF8,
(unsigned char *)"GuangDong", -1, -1, 0);
// 城市
X509_NAME_add_entry_by_txt(subject, SN_localityName, MBSTRING_UTF8,
(unsigned char *)"ShenZhen", -1, -1, 0);
X509_set_subject_name(x509, subject);
x509Req = X509_to_X509_REQ(x509, m_pKey, ShaFunc);
if(!x509Req)
{
goto free_all;
}
// 可视化输出
bio = BIO_new(BIO_s_mem());
PEM_write_bio_X509_REQ(bio, x509Req);
if(bio->num_write == 0)
{
goto free_all;
}
szCSR = (char*)malloc(bio->num_write+1);
if(!szCSR)
{
goto free_all;
}
memset(szCSR, 0, bio->num_write+1);
BIO_read(bio, szCSR, bio->num_write);
free_all:
if(x509)
X509_free(x509);
if(x509Req)
X509_REQ_free(x509Req);
if(bio)
BIO_free(bio);
if(szCSR)
free(szCSR);
3、生成证书
void x509FromCertString(string cert, X509 **pX509)
{
if(cert.length() == 0)
return;
handleCertLineBreak(cert);
BIO *bio = NULL;
X509 *x509 = NULL;
const char *certData = cert.c_str();
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, certData);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
*pX509 = x509;
BIO_free(bio);
}
bool checkX509Data(X509 *x509)
{
// 校验**和证书是否匹配
if(!X509_check_private_key(x509, m_pKey))
{
return false;
}
// 根证书校验
if(m_rootCert == NULL)
{
BIO *bio = BIO_new_file("cert/RootCA.cer","r");
if(!bio)
{
return false;
}
PEM_read_bio_X509(bio, &m_rootCert, NULL, NULL);
BIO_free(bio);
if(m_rootCert == NULL)
{
return false;
}
}
EVP_PKEY *pubKey = X509_get_pubkey(m_rootCert);
if(X509_verify(x509, pubKey) != 1)
{
return false;
}
return true;
}
// p12
void importCert(string cert, string pass, string path)
{
if(cert.length() == 0 || path.length() == 0)
return;
int ret = 0;
BIO *bio = NULL;
X509 *x509 = NULL;
PKCS12 *p12 = NULL;
BIO *bioW = NULL;
const char *certData = cert.c_str();
bio = BIO_new(BIO_s_mem());
BIO_puts(bio, certData);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
// 校验数据
ret = checkX509Data(x509);
if(ret)
{
goto free_all;
}
p12 = PKCS12_create((char*)pass.c_str(), CertAlias, m_pKey, x509, NULL, 0, 0, 0, 0, 0);
if(!p12)
{
goto free_all;
}
bioW = BIO_new_file(path.c_str(),"wb");
if(!bioW)
{
goto free_all;
}
// 写入文件
if ( i2d_PKCS12_bio(bioW,p12) != 1)
{
goto free_all;
}
free_all:
if(x509)
X509_free(x509);
if(p12)
PKCS12_free(p12);
if(bioW)
BIO_free(bioW);
}
// crt
void importCert(string cert, string path)
{
if(cert.length() == 0 || path.length() == 0)
return;
int ret = 0;
X509 *x509 = NULL;
BIO *bioW = NULL;
ret = x509FromCertString(cert, &x509);
if(ret)
{
goto free_all;
}
ret = checkX509Data(x509, false);
if(ret)
{
goto free_all;
}
bioW = BIO_new_file(path.c_str(),"wb");
if(!bioW)
{
goto free_all;
}
// 写入文件
if ( i2d_X509_bio(bioW,x509) != 1)
{
goto free_all;
}
free_all:
if(x509)
X509_free(x509);
if(bioW)
BIO_free(bioW);
}
4、展示证书信息
// p12
void getCertInfo(string path, string pass, string &info)
{
BIO *bio = NULL;
PKCS12 *p12 = NULL;
EVP_PKEY *pKey = NULL;
X509 *x509 = NULL;
BIO *x509Bio = NULL;
char *szCertInfo = NULL;
bio = BIO_new_file(path.c_str(),"r");
if(!bio)
{
goto free_all;
}
p12 = d2i_PKCS12_bio(bio, NULL);
if(!p12)
{
goto free_all;
}
if(PKCS12_parse(p12, pass.c_str(), &pKey, &x509, NULL) != 1)
{
goto free_all;
}
x509Bio = BIO_new(BIO_s_mem());
X509_print(x509Bio, x509);
if(x509Bio->num_write == 0)
{
goto free_all;
}
szCertInfo = (char*)malloc(x509Bio->num_write+1);
if(!szCertInfo)
{
goto free_all;
}
memset(szCertInfo, 0, x509Bio->num_write+1);
BIO_read(x509Bio, szCertInfo, x509Bio->num_write);
info = szCertInfo;
free_all:
if(bio)
BIO_free(bio);
if(p12)
PKCS12_free(p12);
if(x509Bio)
BIO_free(x509Bio);
if(szCertInfo)
free(szCertInfo);
}
// crt
int getCertInfo(string path, string &info)
{
BIO *bio = NULL;
X509 *x509 = NULL;
BIO *x509Bio = NULL;
char *szCertInfo = NULL;
bio = BIO_new_file(path.c_str(),"r");
if(!bio)
{
goto free_all;
}
x509 = d2i_X509_bio(bio, NULL);
if(!x509)
{
goto free_all;
}
x509Bio = BIO_new(BIO_s_mem());
X509_print(x509Bio, x509);
if(x509Bio->num_write == 0)
{
goto free_all;
}
szCertInfo = (char*)malloc(x509Bio->num_write+1);
if(!szCertInfo)
{
goto free_all;
}
memset(szCertInfo, 0, x509Bio->num_write+1);
BIO_read(x509Bio, szCertInfo, x509Bio->num_write);
info = szCertInfo;
free_all:
if(bio)
BIO_free(bio);
if(x509)
X509_free(x509);
if(x509Bio)
BIO_free(x509Bio);
if(szCertInfo)
free(szCertInfo);
}
5、读取证书和**
// p12
void p_getP12FromCertFile(const string filePath, PKCS12 **p12)
{
BIO *bio = NULL;
bio = BIO_new_file(filePath.c_str(),"r");
if(!bio)
{
goto free_all;
}
*p12 = d2i_PKCS12_bio(bio, NULL);
if(!*p12)
{
goto free_all;
}
free_all:
if(bio)
BIO_free(bio);
}
void p_getRSAKeyFromP12(PKCS12 *p12, const string pass, RSA **rsa)
{
EVP_PKEY *pKey = NULL;
X509 *x509 = NULL;
if(PKCS12_parse(p12, pass.c_str(), &pKey, &x509, NULL) != 1)
{
return;
}
*rsa = EVP_PKEY_get1_RSA(pKey);
}
// crt
void p_getX509FromCertFile(const string filePath, X509 **x509)
{
BIO *bio = NULL;
bio = BIO_new_file(filePath.c_str(),"r");
if(!bio)
{
goto free_all;
}
*x509 = d2i_X509_bio(bio, NULL);
if(!*x509)
{
goto free_all;
}
free_all:
if(bio)
BIO_free(bio);
}
void p_getRSAKeyFromX509(X509 *x509, RSA **rsa)
{
EVP_PKEY *pKey = X509_get_pubkey(x509);
if(!pKey)
{
return;
}
*rsa = EVP_PKEY_get1_RSA(pKey);
}
6、加解密
#define RSA_Data_Len 256
int p_rsaCrypt(bool bPubKey, bool bEncrypt, RSA *rsa, unsigned char inData[RSA_Data_Len], unsigned char outData[RSA_Data_Len])
{
memset(outData, 0, RSA_Data_Len);
int (*rsa_func)(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding);
if(bPubKey && bEncrypt)
{
rsa_func = &RSA_public_encrypt;
}
else if(bPubKey && !bEncrypt)
{
rsa_func = &RSA_public_decrypt;
}
else if(!bPubKey && bEncrypt)
{
rsa_func = &RSA_private_encrypt;
}
else
{
rsa_func = &RSA_private_decrypt;
}
int retLen = rsa_func(RSA_Data_Len, inData, outData, rsa, RSA_NO_PADDING);
if(retLen < RSA_Data_Len)
{
return 1;
}
return 0;
}
void rsaCrypt(bool bPubKey, bool bEncrypt, const string input, string &output)
{
int inLen;
char *szIn;
if(bEncrypt)
{
inLen = input.length();
szIn = (char*)input.c_str();
}
else
{
szIn = (char*)alloca(input.length());
if(!szIn)
{
return;
}
int ret = Base64_Decode(input.c_str(), input.length(), (unsigned char*)szIn, input.length(), &inLen);
if(ret)
{
return;
}
}
int round = (inLen+TP_RSA_Data_Len-1)/RSA_Data_Len;// 1024
int outLen = round*TP_RSA_Data_Len;
char *szOut = (char *)alloca(outLen);
if(!szOut)
{
return;
}
for(int i=0; i<round; i++)
{
int cryptLen = RSA_Data_Len;
if(i == round-1)
{
cryptLen = inLen-(round-1)*RSA_Data_Len;
}
char inBuf[RSA_Data_Len];
memset(inBuf, 0, RSA_Data_Len);
memcpy(inBuf, szIn+(i*RSA_Data_Len), cryptLen);
int ret = p_rsaCrypt(bPubKey, bEncrypt, m_rsa, (unsigned char*)inBuf, (unsigned char*)szOut+(i*TP_RSA_Data_Len));
if(ret)
{
return;
}
}
if(bEncrypt)
{
// base64
int base64Len = outLen*2;
char *szBase64 = (char*)alloca(base64Len);
int retLen;
int ret = Base64_Encode((unsigned char*)szOut, outLen, szBase64, base64Len, &retLen);
if(ret)
{
return;
}
output = szBase64;
}
else
{
output = szOut;
}
}
7、签名/验签
// 签名
int p_signWithSha256(RSA *rsa, unsigned char *inData, int inLen, unsigned char outData[RSA_Data_Len])
{
unsigned char sha256[SHA256_DIGEST_LENGTH];
memset(sha256, 0, sizeof(sha256));
// sha256
SHA256_CTX c;
SHA256_Init(&c);
SHA256_Update(&c, inData, inLen);
SHA256_Final(sha256, &c);
// rsa pri key encrypt
memset(outData, 0, TP_RSA_Data_Len);
// OPENSSL_PKCS1_OAEP_PADDING support is only for: PublicKey::encrypt() -> PrivateKey::decrypt()
int len = RSA_private_encrypt(sizeof(sha256), sha256, outData, rsa, RSA_PKCS1_PADDING);
if(len != RSA_Data_Len)
{
return 1;
}
return 0;
}
void signWithSha256(string src, string &sign)
{
unsigned char *bSrc = (unsigned char*)src.c_str();
int srcLen = src.length();
unsigned char bSign[TP_RSA_Data_Len];
memset(bSign, 0, sizeof(bSign));
int ret = p_signWithSha256(m_rsa, bSrc, srcLen, bSign);
if(ret)
{
return ret;
}
// base64
int base64Len = sizeof(bSign)*2;
char *szBase64 = (char*)alloca(base64Len);
int retLen;
ret = Base64_Encode(bSign, sizeof(bSign), szBase64, base64Len, &retLen);
if(ret)
{
return;
}
sign = szBase64;
}
// 验签
int p_verifySignWithSha256(RSA *rsa, unsigned char sign[RSA_Data_Len], unsigned char *src, int srcLen, bool *verify)
{
*verify = false;
// rsa pub key decrypt
unsigned char decSha256[SHA256_DIGEST_LENGTH];
memset(decSha256, 0, sizeof(decSha256));
int len = RSA_public_decrypt(RSA_Data_Len, sign, decSha256, rsa, RSA_PKCS1_PADDING);
if(len != sizeof(decSha256))
{
return 1;
}
// src sha256
unsigned char srcSha256[SHA256_DIGEST_LENGTH];
memset(srcSha256, 0, sizeof(srcSha256));
SHA256_CTX c;
SHA256_Init(&c);
SHA256_Update(&c, src, srcLen);
SHA256_Final(srcSha256, &c);
if(memcmp(decSha256, srcSha256, SHA256_DIGEST_LENGTH) == 0)
{
*verify = true;
}
return 0;
}
void verifySignWithSha256(string sign, string src, bool *verify)
{
unsigned char bSign[RSA_Data_Len];
memset(bSign, 0, sizeof(bSign));
int retLen;
int ret = Base64_Decode(sign.c_str(), sign.length(), bSign, sizeof(bSign), &retLen);
if(ret)
{
return;
}
p_verifySignWithSha256(m_rsa, bSign, (unsigned char *)src.c_str(), src.length(), verify);
}