本文主要介绍如果在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); }