實現X509格式證書的鏈式校驗
// cert_public.cpp : Defines the exported functions for the DLL application. // #include "stdafx.h" #include <string.h> #include <stdio.h> #include <string> #include <stdarg.h> #include <openssl/pem.h> #include <openssl/x509.h> #include <openssl/x509v3.h> #include "sm_public.h" extern "C" { /***************************************************************************** * @brief : 通過X509格式的字符串得到一個X509內部結構對象 * @author : xiaomw * @date : 2019/8/13 * @inparam : plaintext X509格式的字符串 * @return : 成功返回非0 失敗返回0 *****************************************************************************/ CRYPT_SMDLL_C void* X509_Str2Object(const char *in) { BIO *bio_obj = NULL; X509* x509_obj = NULL; //根據字符串內容,構造一個BIO對象 bio_obj = BIO_new_mem_buf(in, strlen(in)); if (NULL == bio_obj){ return NULL; } //調用接口創建唄 x509_obj = (X509 *) PEM_ASN1_read_bio((d2i_of_void *)d2i_X509, PEM_STRING_X509, bio_obj, NULL, NULL, NULL); //釋放對象 BIO_free(bio_obj); return x509_obj; } X509* _X509_RootCertStr2Object(const char *in) { BIO *bio_obj = NULL; X509* x509_obj = NULL; X509_INFO *itmp = NULL; STACK_OF(X509_INFO) *inf = NULL; //根據字符串內容,構造一個BIO對象 bio_obj = BIO_new_mem_buf(in, strlen(in)); if (NULL == bio_obj){ return NULL; } inf = PEM_X509_INFO_read_bio(bio_obj, NULL, NULL, NULL); //釋放對象 BIO_free(bio_obj); if (NULL == inf){ return NULL; } for (int i = 0; i < sk_X509_INFO_num(inf); i++) { itmp = sk_X509_INFO_value(inf, i); if (itmp->x509) { //復制一個X509對象 x509_obj = X509_dup(itmp->x509); sk_X509_INFO_pop_free(inf, X509_INFO_free); return x509_obj; } } sk_X509_INFO_pop_free(inf, X509_INFO_free); return NULL; } static UINT32 _x509_get_entry_value(const X509_NAME* x509_name, X509_NAME_ENTRY_TYPE nEntry, char* value) { ASN1_STRING* strNameValue = NULL; const X509_NAME_ENTRY *x_name_entry = NULL; //獲取到相應的NameEntry x_name_entry = X509_NAME_get_entry(x509_name, nEntry); if (NULL == x_name_entry) { return 1; } strNameValue = X509_NAME_ENTRY_get_data(x_name_entry); if (NULL == strNameValue) { return 1; } //拷貝內容 lstrcpyA(value, (const char*)ASN1_STRING_data(strNameValue)); return 0; } CRYPT_SMDLL_C UINT32 X509_GetIssuerName(const void* pX509_object, X509_NAME_ENTRY_TYPE nEntry, char* value) { const X509_NAME* x_name = NULL; ASN1_STRING* strNameValue = NULL; const X509_NAME_ENTRY *x_name_entry = NULL; if (NULL == pX509_object) { return 1; } //校驗nEntry if (nEntry < NID_commonName || nEntry > NID_organizationalUnitName) { return 1; } //獲取到值 x_name = X509_get_issuer_name((const X509*)pX509_object); if (NULL == x_name) { return 1; } //調用一樣的接口返回 return _x509_get_entry_value(x_name, nEntry, value); } CRYPT_SMDLL_C UINT32 X509_GetSubjectName(const void* pX509_object, X509_NAME_ENTRY_TYPE nEntry, char* value) { const X509_NAME* x_name = NULL; ASN1_STRING* strNameValue = NULL; const X509_NAME_ENTRY *x_name_entry = NULL; if (NULL == pX509_object) { return 1; } //校驗nEntry if (nEntry < NID_commonName || nEntry > NID_organizationalUnitName) { return 1; } //獲取到值 x_name = X509_get_subject_name((const X509*)pX509_object); if (NULL == x_name) { return 1; } //調用一樣的接口返回 return _x509_get_entry_value(x_name, nEntry, value); } class Mini_X509_Verify_Class { private: X509* x509_obj; X509_STORE * x509_store; STACK_OF(X509) * x509_chain; X509_STORE_CTX * x509_store_ctx; public: Mini_X509_Verify_Class(): x509_obj(NULL), x509_store(NULL), x509_chain(NULL), x509_store_ctx(NULL){} ~Mini_X509_Verify_Class() { if (NULL != x509_obj){ X509_free(x509_obj); } if (NULL != x509_store){ X509_STORE_free(x509_store); } if (NULL != x509_chain){ sk_X509_pop_free(x509_chain, X509_free); } if (NULL != x509_store_ctx){ X509_STORE_CTX_cleanup(x509_store_ctx); } } UINT32 set_verify_cert(const char* cert) { x509_obj = (X509*)X509_Str2Object(cert); if (NULL == x509_obj){ return 1; } return 0; } UINT32 add_root_cert(const char* root_cert) { //沒有存儲對象,則先創建一個 if (NULL == x509_store){ x509_store = X509_STORE_new(); if (NULL == x509_store) { return 1; } } //得到X509對象 X509* x509_root_obj = (X509*)_X509_RootCertStr2Object(root_cert); if (NULL == x509_root_obj){ return 1; } //加入存儲區 if (0 == X509_STORE_add_cert(x509_store, x509_root_obj)){ X509_free(x509_root_obj); return 1; } return 0; } UINT32 add_cert_chain(const char* cert_node) { //沒有X509鏈,則先創建一個 if (NULL == x509_chain){ x509_chain = sk_X509_new_null(); if (NULL == x509_chain) { return 1; } } //得到X509對象 X509* x509_node_obj = (X509*)X509_Str2Object(cert_node); if (NULL == x509_node_obj){ return 1; } //加入X509鏈 if (0 == sk_X509_push(x509_chain, x509_node_obj)){ X509_free(x509_node_obj); return 1; } return 0; } BOOL verify(X509* cert) { X509* pTemp = NULL; EVP_PKEY *pkey=NULL; // 每次都采取遍歷證書的辦法吧 if (NULL != x509_chain){ //獲取鏈條總長度 int nSize = sk_X509_num(x509_chain); for (int index = 0; index < nSize; index ++) { //取出相應的數值 pTemp = sk_X509_value(x509_chain, index); //取出公鑰 pkey = X509_get_pubkey(pTemp); //測試能否驗證過,能夠驗證過,遞歸下去驗證 if (X509_verify(cert, pkey)){ return verify(pTemp); } } } //如果鏈條中都驗證不過,試一下根證書 STACK_OF(X509_OBJECT) * root_obj = X509_STORE_get0_objects(x509_store); if (NULL == root_obj) { return FALSE; } int root_obj_num = sk_X509_OBJECT_num(root_obj); if (root_obj_num <= 0) { return FALSE; } X509 *root_cert = NULL; X509_OBJECT* x509_obj = NULL; for(int j = 0; j < root_obj_num; j ++){ //取出OBJ x509_obj = sk_X509_OBJECT_value(root_obj, j); //取出證書 root_cert = X509_OBJECT_get0_X509(x509_obj); //取出公鑰 pkey = X509_get_pubkey(root_cert); //測試能否驗證過,能夠驗證過,返回成功 if (X509_verify(cert, pkey)){ return TRUE; } } return FALSE; } UINT32 verify_cert() { if (!x509_obj){ return 1; } if (verify(x509_obj)) { return 0; } return 1; } }; // 驗證一個證書,輸入根證書及相應的二級、三級...證書 UINT32 X509_Verify(const char *cert, const char* root_cert, UINT32 ulLevelNum, ...) { UINT32 index = 0; va_list arg_ptr; const char* cert_node = NULL; Mini_X509_Verify_Class x509_verify; if (NULL == cert) { return 1; } if (NULL == root_cert) { return 1; } //最多支持6級 if (ulLevelNum > 6) { return 1; } //存放待驗證證書 if (x509_verify.set_verify_cert(cert)) { return 1; } //存放根證書 if (x509_verify.add_root_cert(root_cert)) { return 1; } va_start(arg_ptr, ulLevelNum); for(index = 0; index < ulLevelNum; index ++){ cert_node = va_arg(arg_ptr, const char*); //存放證書鏈接 if (x509_verify.add_cert_chain(cert_node)) { return 1; } } va_end(arg_ptr); //開始校驗 if (x509_verify.verify_cert()) { return 1; } return 0; } };
DEMO驗證代碼
BOOL LoadCertFileToStr(const char* strFile, std::string& strBuff) { // 打開文件,讀取內容 FILE * hSrcfile = NULL; fopen_s(&hSrcfile, strFile,"rb"); if (hSrcfile == NULL) { return FALSE; } //讀取文件 fseek (hSrcfile, 0, SEEK_END); ///將文件指針移動文件結尾 long size = ftell (hSrcfile); ///求出當前文件指針距離文件開始的字節數 //分配內存 strBuff.resize(size + 1, '\0'); //重新開始讀取文件 fseek (hSrcfile, 0, SEEK_SET); //讀取文件 fread(&strBuff[0], size,1, hSrcfile); fclose(hSrcfile); return TRUE; } int main(int argc, char* argv[]) { //cwSL3D_test_sum();//測試能否成功調用所有接口 //至少兩個證書文件 if (argc < 3) { std::cout << "缺少證書文件" << std::endl; return -1; } if (argc > 9) { std::cout << "證書文件過多,目前不支持" << std::endl; return -1; } //第一個證書文件,根證書文件 std::string root_cert; if (!LoadCertFileToStr(argv[1], root_cert)) { std::cout << "讀取根證書文件失敗" << std::endl; return -1; } //第二個證書文件,待驗證的證書 std::string cert_file; if (!LoadCertFileToStr(argv[2], cert_file)) { std::cout << "讀取待驗證證書文件失敗" << std::endl; return -1; } //后續的證書文件,證書鏈條上的文件 std::string cert_chain[6]; for(int index = 0; index < 6 && index < argc - 3; index ++) { if (!LoadCertFileToStr(argv[index + 3], cert_chain[index])) { std::cout << "讀取證書文件失敗:" << argv[index + 2] << std::endl; return -1; } } //直接調用接口驗證吧 if (X509_Verify(cert_file.c_str(), root_cert.c_str(), argc - 3, cert_chain[0].c_str(), cert_chain[1].c_str(), cert_chain[2].c_str(), cert_chain[3].c_str(), cert_chain[4].c_str(), cert_chain[5].c_str())) { std::cout << "驗證證書文件失敗" << std::endl; return -1; } return 0; }