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 |
MD5 |
PROV_RSA_SIG |
none |
RSA |
none |
MD5 |
PROV_RSA_SCHANNEL |
RSA |
RSA |
RC4 |
MD5 |
PROV_DSS |
DSS |
none |
DSS |
MD5 |
PROV_DSS_DH |
DH |
DSS |
CYLINK_MEK |
MD5 |
PROV_DH_SCHANNEL |
DH |
DSS |
DES |
MD5 |
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.