CryptoAPI第一天
[轉]http://www.chinaitpower.com/A200507/2005-07-27/175804.html
一:準備工作
一般必須包含如下頭
文件
以及定義
#include
#include
#include
#define MY_ENCODING_TYPE? (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
?當包含wincrypt.h頭
文件
時,一般都需要定義#define _WIN32_WINNT 0x(具體的值),否則將得到如下錯誤:error C2065: undeclared identifier,不同的操作
系統
不同定義如下:
Windows Server 2003 family _WIN32_WINNT>=0x0502
WINVER>=0x0502
Windows XP _WIN32_WINNT>=0x0501
WINVER>=0x0501
Windows 2000 _WIN32_WINNT>=0x0500
WINVER>=0x0500
Windows NT 4.0 _WIN32_WINNT>=0x0400
WINVER>=0x0400
Windows Me _WIN32_WINDOWS=0x0500
WINVER>=0x0500
Windows 98 _WIN32_WINDOWS>=0x0410
WINVER>=0x0410
Windows 95 _WIN32_WINDOWS>=0x0400
WINVER>=0x0400
Internet Explorer 6.0 _WIN32_IE>=0x0600
Internet Explorer 5.6 _WIN32_IE>=0x0560
Internet Explorer 5.01, 5.5 _WIN32_IE>=0x0501
Internet Explorer 5.0, 5.0a, 5.0b _WIN32_IE>=0x0500
Internet Explorer 4.01 _WIN32_IE>=0x0401
Internet Explorer 4.0 _WIN32_IE>=0x0400
Internet Explorer 3.0, 3.01, 3.02 _WIN32_IE>=0x0300
二:了解基本知識
CryptoAPI的配置信息存儲在注冊表中,包括如下密鑰:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft \ Cryptography \Defaults
HKEY_CURRENT_USER\ Software \ Microsoft\ Cryptography \Providers
---- CryptoAPI使用兩種密鑰:會話密鑰與公共/私人密鑰對。會話密鑰使用相同的加密和解密密鑰,這種算法較快,但必須保證密鑰的安全傳遞。公共/私人密鑰對使用一個公共密鑰和一個私人密鑰,私人密鑰只有專人才能使用,公共密鑰可以廣泛傳播。如果密鑰對中的一個用于加密,另一個一定用于解密。公共/私人密鑰對算法很慢,一般只用于加密小批數據,例如用于加密會話密鑰。CryptoAPI支持兩種基本的編碼方法:流式編碼和塊編碼。流式編碼在明碼文本的每一位上創建編碼位,速度較快,但安全性較低。塊編碼在一個完整的塊上(一般為64位)上工作,需要使用填充的方法對要編碼的數據進行舍入,以組成多個完整的塊。這種算法速度較慢,但更安全。
三:下面進入具體的編程?
一: 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.第四個是指定提供的類型。例如: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.
posted on 2006-07-05 23:18 david.turing 閱讀(3457) 評論(1) 編輯 收藏 所屬分類: CryptoAPI