1. 程式人生 > >CSP程式設計基礎--CryptAPI基本函式含參介紹

CSP程式設計基礎--CryptAPI基本函式含參介紹

一: Creating a Key Container and Generating Keys

  建立一個金鑰容器,在進行加密,解密檔案,並且簽名的時候,必須需要一個公/私鑰對,下面我們就來建立預設的金鑰容器,要注意的是建立金鑰容器並不會自動產生公/私鑰對.

  下面是我們程式的任務:

  1,假如金鑰容器不存在則建立一個。

  2,假如簽名金鑰不存在則在金鑰容器裡建立一個。

  3,假如交換金鑰不存在則在金鑰容器裡建立一個。

  4,獲取CSP中的一些引數

  下面是具體的步驟:

  1,連線預設的CSP

BOOL WINAPI CryptAcquireContext(

  HCRYPTPROV* phProv,   //out

  LPCTSTR pszContainer, //in

  LPCTSTR pszProvider,  //in

  DWORD dwProvType,     //in

  DWORD dwFlags         //in

);

第一個引數是返回的CSP控制代碼,第二個是金鑰容器的名字,第三個是A null-terminated string that specifies the name of the CSP to be used.第四個是指定提供的型別。目前已經有9中CSP型別。如下表所示

CSP型別

交換演算法

簽名演算法

對稱加密演算法

Hash演算法

PROV_RSA_FULL

RSA

RSA

RC2
RC4

MD5
SHA

PROV_RSA_SIG

none

RSA

none

MD5
SHA

PROV_RSA_SCHANNEL

RSA

RSA

RC4
DES
Triple DES

MD5
SHA

PROV_DSS

DSS

none

DSS

MD5
SHA

PROV_DSS_DH

DH

DSS

CYLINK_MEK

MD5
SHA

PROV_DH_SCHANNEL

DH

DSS

DES
Triple DES

MD5
SHA

PROV_FORTEZZA

KEA

DSS

Skipjack

SHA

PROV_MS_EXCHANGE

RSA

RSA

CAST

MD5

PROV_SSL

RSA

RSA

Varies

Varies

例如:CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, 0));

如果當前機器的未曾設定過預設的金鑰容器,因此必須為機器建立預設的金鑰容器。

CryptAcquireContext( &hCryptProv, UserName, NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET)大家有沒有看到,只是最後一個引數不同而已,多了一個CRYPT_NEWKEYSET而已。

  2,取得CSP的引數

BOOL WINAPI CryptGetProvParam(

  HCRYPTPROV hProv,

  DWORD dwParam,

  BYTE* pbData,

  DWORD* pdwDataLen,

  DWORD dwFlags

);

第一個引數是CSP的控制代碼,第二個引數是需要取得的具體引數物件(型別比較多,具體請看MSDN)。

例子:CryptGetProvParam(hCryptProv, PP_CONTAINER, (BYTE *)szUserName, &dwUserNameLen, 0)

3,函式返回所獲取金鑰型別的控制代碼(0表失敗,非0表成功)

BOOL WINAPI CryptGetUserKey(

  HCRYPTPROV hProv,

  DWORD dwKeySpec,

  HCRYPTKEY* phUserKey

);

  引數比較簡單,只談談第二次引數,它可以是AT_KEYEXCHANGE(交換金鑰) or AT_SIGNATURE(簽名金鑰),例如:

CryptGetUserKey(hCryptProv,AT_KEYEXCHANGE,&hKey)

4,產生一個隨機的交換金鑰或者公/私鑰對

BOOL WINAPI CryptGenKey(

  HCRYPTPROV hProv,

  ALG_ID Algid,

  DWORD dwFlags,

  HCRYPTKEY* phKey

);

ALG_ID 表明產生私鑰所使用的演算法。有如下引數:

微軟提供的基本演算法

CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_MAC,CALG_HMAC,CALG_SSL3_SHAMD5,CALG_MD2,CALG_MD2

CALG_RSA_SIGN,CALG_RSA_KEYX,CALG_RC2,CALG_RC4,CALG_DES

微軟提供的增強型演算法:

CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1

CALG_MAC,CALG_HMAC ,CALG_SSL3_SHAMD5,CALG_RSA_SIGN,CALG_RSA_KEYX,CALG_RC2,CALG_RC4,CALG_DES,CALG_3DES_112,CALG_3DES

使用DH的CSP有如下兩個引數,CALG_DH_EPHEM,CALG_DH_SF

使用公開金鑰演算法:AT_KEYEXCHANGE,AT_SIGNATURE

dwFlags,,表示金鑰使用的長度,引數可以為0,採用預設的金鑰長度。或者是進行如下幾個引數的或:

CRYPT_ARCHIVABLE:表示在控制代碼在關閉之前都能夠被匯出

CRYPT_CREATE_SALT:表示金鑰按照一個salt value來隨機產生。

CRYPT_EXPORTABLE:表示金鑰可以從CSP中匯出到BLOB,因為會話金鑰產生是必須可匯出的,所以必須設定

CRYPT_NO_SALT:表示沒有SALT VALUE獲取allocated for a forty-bit symmetric key

CRYPT_PREGEN:表示在DH或者DSS金鑰產生必須有個初始化。

例如:CryptGenKey(hCryptProv,AT_KEYEXCHANGE,0,&hKey)

5,釋放CSP控制代碼

BOOL WINAPI CryptReleaseContext(

  HCRYPTPROV hProv,

  DWORD dwFlags //保留字,現在必須為0

);

6,為CSP增加一個reference count(用來跟蹤COM物件的整數值,當物件建立,值為1。每次對物件的操作都將增加,而對物件的關閉將減少,當值為0是,物件釋放,所以與物件相關操作將無效)

BOOL WINAPI CryptContextAddRef(

  HCRYPTPROV hProv,

  DWORD* pdwReserved,  //保留字,必須為NULL

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

);

二:Deriving a Session Key from a Password

  1,連線CSP

  2,使用CryptCreateHash產生一個空的HASH物件

  3,對密碼進行HASH處理

  4,釋放HASH以及密碼物件

  5,釋放CSP

  下面是具體的步驟:

1,CryptCreateHash初始化一個HASH物件

BOOL WINAPI CryptCreateHash(

  HCRYPTPROV hProv,  //in

  ALG_ID Algid,      //in

  HCRYPTKEY hKey,    //in

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

  HCRYPTHASH* phHash //out

);

第二個引數是指定HASH演算法,有CALG_HMAC,CALG_MAC,CALG_MD2,CALG_MD5,CALG_SHA,CALG_SHA1,CALG_SSL3_SHAMD5。第三個引數對於那些keyed hash,例如HMAC,MAC演算法。但是nonkeyed演算法,必須設定為0。

2,CryptHashData對資料使用HASH

BOOL WINAPI CryptHashData(

  HCRYPTHASH hHash,  //in,HASH物件控制代碼

  BYTE* pbData,      //in,待HASH的資料

  DWORD dwDataLen,   //in,待HASH資料的長度,當dwFlags為CRYPT_USERDATA為0時,必須為0

  DWORD dwFlags      //in,一般為0,或者為CRYPT_USERDATA(用在使用者進入系統時需要輸入PIN)

);

3,CryptDeriveKey從某一資料產生會話金鑰。有點類似CryptGenKey,但是產生的會話金鑰來自固定資料,而CryptGenKey是隨機產生的。並且不能產生公/私鑰對

BOOL WINAPI CryptDeriveKey(

  HCRYPTPROV hProv,      //in,CSP控制代碼

  ALG_ID Algid,          //in,指定的演算法,類似CryptGenKey

  HCRYPTHASH hBaseData,  //in,HASH物件的控制代碼

  DWORD dwFlags,         //in,指定產生金鑰的型別

  HCRYPTKEY* phKey       //in,out產生的金鑰控制代碼地址

);

例如:CryptDeriveKey(hCryptProv, CALG_RC2, hHash, CRYPT_EXPORTABLE, &hKey)

4,CryptDestroyHash(hHash);

5, CryptDestroyKey(hKey);

6, 在這裡發現一個不錯的函式,就是那種提示輸入密碼的命令列(螢幕只會出現***)

  void GetConsoleInput(char* strInput, int intMaxChars)

{

 char ch;

 char minChar = ' ';

 minChar++;

 ch = getch();

 while (ch != '\r')

 {

  if (ch == '\b' && strlen(strInput) > 0)

  {

   strInput[strlen(strInput)-1]   = '\0';

   printf("\b \b");

  }

  else if (ch >= minChar && strlen(strInput) < intMaxChars)

  {

   strInput[strlen(strInput)+1] = '\0';

   strInput[strlen(strInput)]   = ch;

   putch('*');

  }

  ch = getch();

 }

 putch('\n');

}

 

 三:Duplicating,setting and getting Session key

  1,連線CSP

  2,使用CryptGenKey產生一個會話金鑰

  3,CryptDuplicateKey複製會話金鑰

  4,CryptSetKeyParam改變金鑰產生的過程

  5,CryptGenRandom產生隨機數

具體過程。

1,CryptDuplicateKe複製會話金鑰

BOOL WINAPI CryptDuplicateKey(

  HCRYPTKEY hKey,      //in 會話金鑰控制代碼

  DWORD* pdwReserved,  //in 保留字,必須為NULL

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

  HCRYPTKEY* phKey     //out 新的會話金鑰

);

2,CryptSetKeyParam定製會話金鑰的引數

BOOL WINAPI CryptSetKeyParam(

  HCRYPTKEY hKey,

  DWORD dwParam,  //in 很多,具體請看MSDN

  BYTE* pbData,

  DWORD dwFlags  //in  只有在dwParam=KP_ALGID才被使用

);

例如CryptSetKeyParam(hOriginalKey, KP_MODE, (BYTE*)&dwMode, 0)(dwMode = CRYPT_MODE_ECB)

3,CryptGenRandom為空間產生隨機位元組

BOOL WINAPI CryptGenRandom(

  HCRYPTPROV hProv,

  DWORD dwLen,    //需要產生的隨機位元數

  BYTE* pbBuffer  //需要返回資料的空間,這個pbBuffer可以等於CryptSetKeyParam的pbData

);

例如:CryptGenRandom(hCryptProv, 8, pbData)

4,CryptGetKeyParam獲取金鑰的一些引數

BOOL WINAPI CryptGetKeyParam(

  HCRYPTKEY hKey,

  DWORD dwParam,     //in,引數眾多

  BYTE* pbData,      //out,獲取BYTE資料的指標

  DWORD* pdwDataLen, //out,獲取BYTE資料的長度

  DWORD dwFlags      //關鍵字,必須為0

);

例如:CryptGetKeyParam(hKey, KP_IV, pbData, &dwCount, 0)

四:Exporting a Session Key

  1,連線CSP

  2,CryptGetUserKey獲取公/私鑰對和交換金鑰,公私鑰用來簽名,而交換金鑰用來匯出會話金鑰

  3,CryptGenKey產生會話金鑰

  4,CryptExportKey建立簡單包含有會話金鑰的key BLOB

  5,釋放處理:

  具體過程:

1,CryptExportKey函式匯出金鑰

BOOL WINAPI CryptExportKey(

  HCRYPTKEY hKey,     //需要匯出的金鑰控制代碼

  HCRYPTKEY hExpKey,  //將待匯出金鑰用交換金鑰進行加密,假如是公開的BLOG當然就設定為0

  DWORD dwBlobType,   //指定匯出的金鑰BLOB型別。六個引數見MSDN

  DWORD dwFlags,      //CRYPT_DESTROYKEY,CRYPT_SSL2_FALLBACK,CRYPT_OAEP

  BYTE* pbData,       //匯出的資料指標,以後就可以將這個資料寫如磁碟或者別的任務。

  DWORD* pdwDataLen   //匯出的資料長度

);

例如:CryptExportKey(hKey, hXchgKey, SIMPLEBLOB, 0, pbKeyBlob, &dwBlobLen)

2,CryptImportKey將金鑰從BLOB轉換到CSP中

BOOL WINAPI CryptImportKey(

  HCRYPTPROV hProv,  //CSP控制代碼

  BYTE* pbData,      //待轉換的BLOB資料

  DWORD dwDataLen,   //待轉換的資料長度

  HCRYPTKEY hPubKey, //對BLOB解密的公鑰,譬如上面是用交換金鑰金鑰加密的,就用交換金鑰解密

  DWORD dwFlags,     //目前還只應用在當一對公/私鑰從PRIVATEKEYBLOB中加入CSP中這種情況。

  HCRYPTKEY* phKey   //out匯入的金鑰

);

例如:CryptImportKey(hProv,pbKeyBlob,dwBlobLen,0,0,&hPubKey)

  五:Encoding and Decoding Messages

  編碼的處理過程

  1,將待編碼的資料轉化為合適的格式,使用

  2,呼叫CryptMsgOpenToEncode,passing the necessary argument;

  3, 呼叫CryptMsgUpdate函式多次,最後一次呼叫時,將final引數設定為true

  4, 呼叫CryptMsgGetParam來獲取一個需要得到的引數。

  5, 呼叫CryptMsgClose來關閉訊息

  解碼的處理過程

  1,檢查申請的放編碼後資料的空間,利用函式CryptMsgCalculateEncodedLength.

  2,呼叫函式CryptMsgOpenToDecode,passing the necessary argument;

  3,呼叫CryptMsgUpdate一次,這將導致合適的動作去處理資訊,以來於資訊的格式

  4,一些額外的處理,例如額外的解密或者是驗證,呼叫CryptMsgControl,

  5,呼叫CryptMsgGetParam來獲取需要得到的引數

  6,呼叫CryptMsgClose來關閉訊息

  具體的函式介紹:

  1,CryptMsgCalculateEncodedLength計算所需要的儲存編碼的最大空間值

DWORD WINAPI CryptMsgCalculateEncodedLength(

  DWORD dwMsgEncodingType,//指定編碼型別。一般為X509_ASN_ENCODING|PKCS_7_ASN_ENCODING

  DWORD dwFlags,

  DWORD dwMsgType,

  const void* pvMsgEncodeInfo, //in 指向待編碼的資料,資料型別依賴於dwMsgType

  LPSTR pszInnerContentObjID,

  DWORD cbData                 //in 位元數的容量

);

第二個引數:CMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG

第三個引數:CMSG_DATA,CMSG_SIGNED,CMSG_ENVELOPED,CMSG_SIGNED_AND_ENVELOPED,CMSG_HASHED,CMSG_ENCRYPTED

第五個引數:szOID_RSA_data,szOID_RSA_signedData,szOID_RSA_envelopedData,szOID_RSA_signEnvData,szOID_RSA,digestedData ,

szOID_RSA_encryptedData,SPC_INDIRECT_DATA_OBJID,NULL

返回值:返回需要的一個加密資訊所需要的長度

  2,CryptMsgOpenToEncode開啟一個訊息以便進行編碼,返回開啟訊息的控制代碼

  HCRYPTMSG WINAPI CryptMsgOpenToEncode(

  DWORD dwMsgEncodingType,      //指定編碼型別。一般為X509_ASN_ENCODING|PKCS_7_ASN_ENCODING

  DWORD dwFlags,               

  DWORD dwMsgType,             

  const void* pvMsgEncodeInfo,

  LPSTR pszInnerContentObjID,    //和CryptMsgCalculateEncodedLength一樣

  PCMSG_STREAM_INFO pStreamInfo //當流沒被使用時,該引數為NULL

);

第二個引數:CMSG_BARE_CONTENT_FLAG,CMSG_DETACHED_FLAG,CMSG_CONTENTS_OCTETS_FLAG,CMSG_CMS_ENCAPSULATED_CONTENT_FLAG,

CMSG_CRYPT_RELEASE_CONTEXT_FLAG

第三個引數:

CMSG_DATA(Not used),CMSG_SIGNED,CMSG_SIGNED_ENCODE_INFO,CMSG_ENVELOPED,CMSG_ENVELOPED_ENCODE_INFO

CMSG_SIGNED_AND_ENVELOPED(Not currently implemented),CMSG_HASHED

3,CryptMsgOpenToDecode開啟一個訊息以便進行解碼,返回開啟訊息的句

  CRYPTMSG WINAPI CryptMsgOpenToDecode(

  DWORD dwMsgEncodingType,     //指定編碼型別。一般為X509_ASN_ENCODING|PKCS_7_ASN_ENCODING

  DWORD dwFlags,   //CMSG_DETACHED_FLAG,CMSG_CRYPT_RELEASE_CONTEXT_FLAG

  DWORD dwMsgType, //CMSG_DATA,CMSG_ENVELOPED,CMSG_HASHED,CMSG_SIGNED,CMSG_SIGNED_AND_ENVELOPED

  HCRYPTPROV hCryptProv,    //指定使用HASHING的控制代碼,一般設定為0

  PCERT_INFO pRecipientInfo,//保留字,必須為NULL

  PCMSG_STREAM_INFO pStreamInfo//假如流沒被使用,必須為NULL

);

4,CryptMsgUpdate增加內容到加密資訊中

BOOL WINAPI CryptMsgUpdate(

  HCRYPTMSG hCryptMsg, //待更新的加密資訊控制代碼

  const BYTE* pbData,  //待編碼/解碼的資料

  DWORD cbData,        // pbData 的資料長度

  BOOL fFinal         

);

第四個引數:當CMSG_DETACHED_FLAG沒有設定,並且資訊由CryptMsgOpenToDecode或CryptMsgOpenToEncode開啟,那麼fFinal被設定為TRUE,並且CryptMsgUpdate只被呼叫一次。當CMSG_DETACHED_FLAG被設定,並且資訊由 CryptMsgOpenToEncode開啟,那麼僅在最後一次呼叫CryptMsgUpdate才被設定為TRUE。當CMSG_DETACHED_FLAG被設定,並且資訊由CryptMsgOpenToDecode開啟,那麼僅在資訊頭單獨被處理時CryptMsgUpdate才被設定為TRUE。

5,CryptMsgGetParam在資料編碼/解碼後獲取引數

  BOOL WINAPI CryptMsgGetParam(

  HCRYPTMSG hCryptMsg,  //in 資訊控制代碼

  DWORD dwParamType,   //in 引數眾多,參見MSDN

  DWORD dwIndex,       //in 可適用的返回引數控制代碼,假如引數沒有被獲取,則被忽略或則為0

  void* pvData,        //out 獲取的資料指標

  DWORD* pcbData      //in,out資料長度

);

6,CryptMsgClose關閉資訊控制代碼

BOOL WINAPI CryptMsgClose(

  HCRYPTMSG hCryptMsg

);

具體的例子:

cbEncodedBlob = CryptMsgCalculateEncodedLength(

             MY_ENCODING_TYPE,       // Message encoding type

             0,                      // Flags

             CMSG_DATA,              // Message type

             NULL,                   // Pointer to structure

             NULL,                   // Inner content object ID

             cbContent))             // Size of content

hMsg = CryptMsgOpenToEncode(

          MY_ENCODING_TYPE,        // Encoding type

          0,                       // Flags

          CMSG_DATA,               // Message type

          NULL,                    // Pointer to structure

          NULL,                    // Inner content object ID

          NULL))                   // Stream information (not used)

CryptMsgUpdate(

        hMsg,         // Handle to the message

        pbContent,    // Pointer to the content

        cbContent,    // Size of the content

        TRUE))        // Last call

{

CryptMsgGetParam(

               hMsg,                      // Handle to the message

               CMSG_BARE_CONTENT_PARAM,   // Parameter type

               0,                         // Index

               pbEncodedBlob,             // Pointer to the BLOB

               &cbEncodedBlob))           // Size of the BLOB

CryptMsgClose(hMsg);

hMsg = CryptMsgOpenToDecode(

               MY_ENCODING_TYPE,      // Encoding type.

               0,                     // Flags.

               CMSG_DATA,             // Look for a data message.

               NULL,                  // Cryptographic provider.

               NULL,                  // Recipient information.

               NULL))                 // Stream information.