程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> c++中如何使用CryptoAPI創建一個自簽名證書

c++中如何使用CryptoAPI創建一個自簽名證書

編輯:關於C++

CryptoAPI編程

(1) 微軟加密服務體系

微軟加密服務體系CryptoAPI的結構如下圖所示,微軟加密服務體系包含三層結構和兩個接口,分別為應用程序層、操作系統層(OS)、加密服務提供者層(Cryptographic Service Provider,CSP),CryptoAPI接口和加密服務提供者接口(Cryptographic Service Provider Interface,CSPF)。

(2)CryptoAPI體系結構

CryptoAPI體系架構共由五大主要部分組成:基本加密函數、證書編/解碼函數、證書庫管理函數、簡單消息函數、底層消息函數。體系結構如下圖所系:

基本加密函數:用於選擇CSP、建立CSP連接、產生密鑰、交換及傳輸密鑰等操作。

證書編/解碼函數:用於數據加密、解密、哈希等操作。這類函數支持數據的加密/解密操作;計算哈希、創建和校驗數字簽名操作;實現證書、證書撤銷列表、證書請求和證書擴展等編碼和解碼操作。

證書庫管理函數:用於數字證書及證書庫管理等操作。這組函數用於管理證書、證書撤銷列表和證書信任列表的使用、存儲、獲取等。

簡單消息函數:用於消息處理,比如消息編碼/解碼、消息加/解密、數字簽名及簽名驗證等操作。它是把多個底層消息函數包裝在一起以完成某個特定任務,方便用戶的使用。

底層消息函數:底層消息函數對傳輸的PKCS#7數據進行編碼,對接受到的PKCS#7數據進行解碼,並且對接收到的消息進行解碼和驗證。它可以實現簡單消息函數可以實現的所有功能,且提供更大的靈活性,但一般會需要更多的函數調用。

(3)CryptoAPI基本功能

利用CryptoAPI,開發者可以給基於Windows的應用程序添加安全服務,包括:ASN.1編碼/解碼、數據加密/解密、身份認證、數字證書管理,同時支持PKI、對稱密碼技術等。

密鑰管理

在CryptoAPI中,支持兩種類型的密鑰:會話密鑰、公/私密鑰對。會話密鑰也成為對稱密鑰,用於對稱加密算法。為了保證密鑰的安全性,在CryptoAPI中,這些密鑰都保存在CSP內部,用戶可以通過CryptExpoetKey以加密密鑰快形式導出。公/私鑰對用於非對稱加密算法。非對稱加密算法主要用於加解密會話密鑰和數字簽名。在CryptoAPI中,一般來說,大多數CSP產生的密鑰容器包含兩對密鑰對,一對用於加密會話密鑰,稱為交換密鑰對,一對用於產生數字簽名,稱為簽名密鑰對。在CryptoAPI中,所有的密鑰都存儲在CSP中,CSP負責密鑰的創建,銷毀,導入導出等操作。

數據編碼/解碼

CryptoAPI采用的編碼方式為ASN.1,編碼規則為DER,表示發送方發送數據時先把數據抽象為ASN.1對象,然後使用DER編碼規則把ASN.1對象轉化為可傳輸的0、1串;接受方接受到數據後,利用DER解碼規則把0、1串轉化為ASN.1對象,然後把ASN.1對象轉化為具體應用支持的數據對象。

數據加/解密

在CryptoAPI中約定加密較大的數據塊時,采用對稱加密算法。通過其封裝好的加解密函數來實現數據解加密操作。

哈希與數字簽名

哈希與數字簽名一般用於數據的完整性驗證和身份鑒別。CryptoAPI中,通過其封裝好的哈希與數字簽名函數來實現相關操作。微軟公司提供的CSP產生的數字簽名遵循RSA標准(PKCS#6).

數字證書管理

數字證書主要用於安全通信中的身份鑒別。CryptoAPI中,對數字證書的使用管理函數分為證書與證書庫函數、證書驗證函數兩大部分。

在VC++中開發CryptoAPI應用程序,需要預先設置一些編譯環境。

1.需要包含以下頭文件:

#include <windows.h>

#include <wincrypt.h>

2.包含的靜態鏈接庫:

鏈接CryptoAPI函數必須有靜態庫Crypto32.lib的支持,部分CryptoAPI函數可能還需要靜態庫advapi32.lib及CryptUI.lib的支持。

3.假如在VC++6.0上編譯程序,則還需加上以下語句:

#ifndef _WIN32_WINNT

#define _WIN32_WINNT 0x0400

#endif

在不同的版本的windows操作系統下,可能需要定義不同的常量,具體查看wincrypt.h頭文件,根據wincrypt.h上不同的預編譯語句在自己的應用程序中進行不同定義。(我在VS 2008環境中編譯程序,不在需要自定義這部分)。在vs2008的wincrypt.h頭文件已經沒有這些相關的定義。)

注:部分的CryptoAPI函數在VC++6.0上並沒有定義,如CertGetNameString函數為CryptoAPI的證書管理函數,但是在VC++6.0下編譯時會報錯,查看相應的wincryp.h文件時會發現裡面沒有聲明該函數。但在VC++7.0以上的版本中則定義了這個函數。解決方法是可以將VC++7.0上的wincrypt.h、crypt32.lib、advapi32.lib三個文件覆蓋vc+6.0的相應文件。

以下介紹幾個編寫CryptoAPI應用程序常用到得函數。

1.BOOLEAN CRYPTFUNC CryptAcquireContext(

HCRYPTPROV* phProv,   CSP句柄

LPCTSTR pszContainer,   密鑰容器名稱,指向密鑰容器的字符串指針

LPCTSTR pszProvider,    指向CSP名稱的字符串指針,如果為NULL,則使用默認的CSP

DWORD dwProvType, CSP類型

DWORD dwFlags 標志

);

這個函數是為了獲得CSP句柄,函數通過phProv參數返回獲得的CSP句柄。在CryptoAPI加密服務相關的所有操作都在CSP實現,CSP真正實行加密相關服務的獨立模塊,當應用程序需要加密相關服務時,比如:加解密操作、密鑰產生於管理等,必須先獲取某個CSP句柄。這時一般CryptoAPI編程的第一步。

2.BOOL CRYPTFUNC CryptGenKey(

HCRYPTPROV hProv,   //CSP句柄

ALG_ID Algid, //算法標志ID值。創建會話密鑰時,它指定具體的加解密算法。指定算法時應注意具體的

// CSP是否支持此算法。創建公/私密鑰對時,參數應為AT_KEYEXCHANGE(交換密鑰對)

//或AT_SIGNATURE(簽名密鑰對)。

DWORD dwFlags,    //說明創建密鑰的長度及其它屬性。

HCRYPTKEY* phKey   //新創建密鑰句柄,函數通過這個參數返回創建密鑰句柄。

);

在CryptoAPI中,構造密鑰一般有兩種方法,一通過哈希值,而通過隨機數構造。上面這種就是通過隨機數創建的。下面介紹利用哈希值創建的函數。

BOOL CRYPTFUNC CryptDeriveKey(

HCRYPTPROV hProv,

ALG_ID Algid,     //要產生密鑰的對稱加密算法

HCRYPTHASH hBaseData,    //哈希句柄,函數根據這個哈希句柄創建密鑰。

DWORD dwFlags,    //指定密鑰的類型。

HCRYPTKEY* phKey   //密鑰句柄,函數通過這個參數返回創建的密鑰句柄。

);

這個函數通過輸入的哈希值hBaseData來創建一個密鑰,通過密鑰句柄phKey參數返回。注意:這個函數只能創建會話密鑰,不能用於創建公/私密鑰對。

3.BOOL CRYPTFUNC CryptCreateHash(

HCRYPTPROV hProv, //CSP句柄

ALG_ID Algid,   //哈希算法標識符

HCRYPTKEY hKey, // 如果哈希算法是密鑰哈希,如HMACH或者MAC算法,就用此密鑰句柄傳遞密鑰。

//對於非密鑰算法,此參數為NULL。

DWORD dwFlags,   //保留,必須為0

HCRYPTHASH* phHash //哈希句柄,函數通過這個參數返回創建的哈希對象句柄。

);

這個函數初始化一個哈希句柄,它創建並返回一個CSP哈希句柄。

4.BOOL WINAPI CryptHashData(

HCRYPTHASH hHash,    //哈希句柄,創建的哈希值通過這個句柄返回

BYTE* pbData,    //指向要加入到哈希句柄的數據指針

DWORD dwDataLen,   // 數據長度

DWORD dwFlags   //標志

);

這個函數是計算一段數據的哈希值並加入到指定的哈希句柄中。在使用這個函數前必須通過CrpytHashData函數創建了一個哈希句柄。

5.BOOL WINAPI CryptEncodeObject(

__in          DWORD dwCertEncodingType,    //使用的編碼類型。通常為 X509_ASN_ENCODING |

//PKCS_7_ASN_ENCODING

__in          LPCSTR lpszStructType,            //要編碼的結構體類型

__in          const void* pvStructInfo,   //欲編碼的結構體指針,要和lpszStructType類型一致

__out         BYTE* pbEncoded,    //編碼後結構體指針,當設置為NULL時用於獲取其長度

__in_out      DWORD* pcbEncoded   //編碼後的結構體長度

);

這個函數用於將pvStructInfo所指向的數據按照lpszStructType結構體類型編碼。

6.BOOL WINAPI CryptDecodeObject(

__in          DWORD dwCertEncodingType,

__in          LPCSTR lpszStructType,

__in          const BYTE* pbEncoded,

__in          DWORD cbEncoded,

__in          DWORD dwFlags,

__out         void* pvStructInfo,

__in_out      DWORD* pcbStructInfo

);

這個函數是對上面編碼後的數據進行解碼,參數和上面編碼函數的參數差不多,具體可以查看MSDN幫助文檔。

1.CERT_RDN_ATTR 結構體

typedef struct _CERT_RDN_ATTR {

LPSTR pszObjId;

DWORD dwValueType;

CERT_RDN_VALUE_BLOB Value;

} CERT_RDN_ATTR,

*PCERT_RDN_ATTR;

pszObjId:對象標識符,用於標識證書屬性,具體可以查看MSDN中的解析,也可以查看wincrypt.h文件查看相應的定義。譬如szOID_STATE_OR_PROVINCE_NAME,表示省名。

dwValueType:對成員Value的解析,取值查看MSDN,當主要是初始化證書屬性時,Value的值主要是一些字符串時,該值可以為CERT_RDN_PRINTABLE_STRING,表示可以打印的字符串。

Value:一個結構體,在這裡初始化證書屬性。

typedef struct _CRYPTOAPI_BLOB {

DWORD cbData;

BYTE* pbData;

} ,其中cbData表示大小,pbData指向一個內存空間。

2.CERT_RDN 結構體:The CERT_RDN structure contains a relative distinguished name (RDN) consisting of an array of CERT_RDN_ATTR structures.

typedef struct _CERT_RDN {

DWORD cRDNAttr;

PCERT_RDN_ATTR rgRDNAttr;

} CERT_RDN,

*PCERT_RDN;

參數:cRDNAttr:rgRDNAttr數組元素的個數;rgRDNAttr:指向CERT_RDN_ATTR結構元素的數組地址。

3.CERT_NAME_INFO 結構體:The CERT_NAME_INFO structure contains subject or issuer names.The information is represented as an array of CERT_RDN structures.

typedef struct _CERT_NAME_INFO {

DWORD cRDN;

PCERT_RDN rgRDN;

} CERT_NAME_INFO,

*PCERT_NAME_INFO;

參數:同上差不多。

4.CERT_REQUEST_INFO 證書請求結構體:這個結構體包含證書請求的主體,主體公鑰,屬性塊等信息,這些信息都是經過編碼的。

typedef struct _CERT_REQUEST_INFO {

DWORD dwVersion;

CERT_NAME_BLOB Subject;

CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo;

DWORD cAttribute;

PCRYPT_ATTRIBUTE rgAttribute;

} CERT_REQUEST_INFO,

*PCERT_REQUEST_INFO;

參數:dwVersion:證書版本號,可以為CERT_V1等,根據屬性擴展情況,符合不同版本證書;Subject:證書主題;SubjectPublicKeyInfo:證書主題中的公鑰信息;cAttribute:rgAttribute數組元素個數,可以為0;rgAttribute:屬性參數數組,可以為NULL;

以上信息都是要經過編碼後的信息來填充的。

5.CryptSignAndEncodeCertificate函數,用來創建自簽名證書

BOOL WINAPI CryptSignAndEncodeCertificate(

__in          HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey,

__in          DWORD dwKeySpec,

__in          DWORD dwCertEncodingType,

__in          LPCSTR lpszStructType,

__in          const void* pvStructInfo,

__in          PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,

__in          const void* pvHashAuxInfo,

__out         PBYTE pbEncoded,

__in_out      DWORD* pcbEncoded

);

參數:1,CSP句柄;2,指明公鑰是來自簽名公鑰還是交換公鑰,可以為AT_KEYEXCHANGE或者AT_SIGNATURE之一;3,指明編碼類型,可以為X509_ASN_ENCODING;4,結構體類型,和第5個參數配合起來使用,可以為X509_CERT_CRL_TO_BE_SIGNED或者X509_CERT_REQUEST_TO_BE_SIGNED或者X509_CERT_TO_BE_SIGNED或者X509_KEYGEN_REQUEST_TO_BE_SIGNED,意思可以查看MSDN。

6,簽名算法結構體,指明簽名算法,算法標識可以為szOID_RSA_MD5RSA 或者szOID_RSA_SHA1RSA 或者szOID_X957_SHA1DSA ;7,可以不用,設為NULL;8,簽名後數據的長度,當設為NULL時,可以用來求數據的長度;9,用於存放數據的內存空間。

(4)使用CryptoAPI創建一個自簽名證書

下面的 c + + 示例演示如何使用 CertCreateSelfSignCertificate API 來創建一個自簽名的證書。將計算機配置文件中創建私鑰/公鑰和證書將存儲該同一配置文件的受信任根 CA 存儲中:

#include "stdio.h"  
#include "conio.h"  
#include "windows.h"  
#include "wincrypt.h"  
#include "tchar.h"  
      
int SelfSignedCertificateTest()  
{  
  // CREATE KEY PAIR FOR SELF-SIGNED CERTIFICATE IN MACHINE PROFILE  
      
  HCRYPTPROV hCryptProv = NULL;  
  HCRYPTKEY hKey = NULL;  
      
  __try 
  {  
    // Acquire key container  
    _tprintf(_T("CryptAcquireContext... "));  
    if (!CryptAcquireContext(&hCryptProv, _T("alejacma"), NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET))   
    {  
      // Error  
      _tprintf(_T("Error 0x%x\n"), GetLastError());  
      
      // Try to create a new key container  
      _tprintf(_T("CryptAcquireContext... "));  
      if (!CryptAcquireContext(&hCryptProv, _T("alejacma"), NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET))  
      {  
        // Error  
        _tprintf(_T("Error 0x%x\n"), GetLastError());  
        return 0;  
      }  
      else 
      {  
        _tprintf(_T("Success\n"));  
      }  
    }  
    else
    {  
      _tprintf(_T("Success\n"));  
    }  
      
    // Generate new key pair  
    _tprintf(_T("CryptGenKey... "));  
    if (!CryptGenKey(hCryptProv, AT_SIGNATURE, 0x08000000 /*RSA-2048-BIT_KEY*/, &hKey))  
    {  
      // Error  
      _tprintf(_T("Error 0x%x\n"), GetLastError());  
      return 0;  
    }  
    else
    {  
      _tprintf(_T("Success\n"));  
    }  
  }  
  __finally
  {  
    // Clean up    
           
    if (hKey)   
    {  
      _tprintf(_T("CryptDestroyKey... "));  
      CryptDestroyKey(hKey);  
      _tprintf(_T("Success\n"));  
    }   
    if (hCryptProv)   
    {  
      _tprintf(_T("CryptReleaseContext... "));  
      CryptReleaseContext(hCryptProv, 0);  
      _tprintf(_T("Success\n"));  
    }  
  }  
      
  // CREATE SELF-SIGNED CERTIFICATE AND ADD IT TO ROOT STORE IN MACHINE PROFILE  
      
  PCCERT_CONTEXT pCertContext = NULL;  
  BYTE *pbEncoded = NULL;  
  HCERTSTORE hStore = NULL;  
  HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProvOrNCryptKey = NULL;  
  BOOL fCallerFreeProvOrNCryptKey = FALSE;  
      
  __try 
  {               
    // Encode certificate Subject
//
		
							
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved