微軟公司在NT4.0以上版本中提供了一套完整的Crypto API的函數,支援通行金鑰交換,資料加密解密,數位簽章,給程式員帶來了很大方便,使用者在對軟體進行保護的時候可以直接利用Crypto API來完成這些工作,比如計算註冊碼,檢查程式的完整性等。
我們在用這些的API進行加密解密的時候,只需要知道如何去應用它們,而不必知道它們的底層實現。如果想知道它們更為詳盡的資料,可以尋找相關的資料。
對Crypto API只是業餘型的感興趣,想通過寫學習筆記,一是讓自己記的更牢固些,二是想把自己的學的跟大家探討一下。寫的不好,大家多多原諒。我主要通過MSDN來學習,例子也是完全取自MSDN。
首先,是Crypto API啟動並執行環境。
首先需要Crypt32.lib,將它加到project->setting->link下面,當然你也可以在程式中用#pragma comment (lib, "crypt32.lib")加入。
在程式開頭,你要加入兩個標頭檔 windows.h 和 Wincrypt.h,和一個#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
在正式應用API進行一系列的加密解密的時候,需要有一些準備工作,下面是介紹一些在正式開始時所需要瞭解和做的工作。
產生密鑰和密鑰容器
我們知道,在進行加密解密的時候,我們需要一個密鑰進行加密,和一個密鑰進行解密,加密金鑰和解密密鑰可能相同,也可能不同。於是在我們進行加密解密的開始時,我們首先需要有密鑰。下面這個程式,完成了三個任務,並且介紹了一些函數的用法。
任務一:擷取一個指定的密鑰容器,如果這個容器不存在,建立一個。
任務二:如果容器中不存在一個簽名金鑰組,建立一個
任務三:如果容器中不存在一個交換金鑰組,建立一個
//-------------------------------------------------------------------
下面這段程式使用到了這幾個函數
CryptAcquireContext
CryptDestroyKey
CryptGenKey
CryptGetUserKey
// Copyright (c) Microsoft Corporation. All rights reserved.
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Wincrypt.h>
//-------------------------------------------------------------------
void MyHandleError(LPTSTR psz)
{
_ftprintf(stderr, TEXT("An error occurred in the program. /n"));
_ftprintf(stderr, TEXT("%s/n"), psz);
_ftprintf(stderr, TEXT("Error number %x./n"), GetLastError());
_ftprintf(stderr, TEXT("Program terminating. /n"));
exit(1);
} // End of MyHandleError.
上面這個函數是一個異常處理函數,當出現錯誤的時候,出現提示,並推出程式。以後的程式中都有這個函數,以後就會將這個函數的實現省去。現在這個函數的實現在後面。
void main(void)
{
HCRYPTPROV hCryptProv; //定義一個CSP模組的控制代碼。“CSP模組,請查看《加密解密二》222頁,那裡有簡單的說明,這裡就不說了。
LPCTSTR pszContainerName = TEXT("My Sample Key Container");//用一個TEXT宏定義一個容器的名字,
if(CryptAcquireContext( //這個函數是擷取有某個容器的CSP模組的指標,成功返回TRUE。
&hCryptProv, //指向一個CSP模組控制代碼指標,裡面用指定的容器
pszContainerName, //指定容器的名稱
NULL, //這個參數這裡用的是預設值,指得是預設得CSP模組,你也可以傳入一個LPCTSTR類型的字串,指定CSP模組
PROV_RSA_FULL, //確定密鑰的類型
0)) //常設為0,還有些其他的類型,請看MSDN
{
_tprintf(
TEXT("A crypto context with the %s key container ")
TEXT("has been acquired./n"),
pszContainerName);
}
else
{
//不成功的處理段
if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味著密鑰
//容器不存在,下面就去建立一個
//新的密鑰容器
{
if(CryptAcquireContext(
&hCryptProv,
pszContainerName,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味著當指定容器不存在的時候,去建立一個容器。
{
_tprintf(TEXT("A new key container has been ")
TEXT("created./n"));
}
else
{
MyHandleError(TEXT("Could not create a new key ")
TEXT("container./n"));
}
}
else
{
MyHandleError(TEXT("CryptAcquireContext failed./n"));
}
}
HCRYPTKEY hKey; //建立一個密鑰控制代碼
if(CryptGetUserKey( // CryptGetUserKey是擷取一個密鑰//控制代碼的函數,成功返回TRUE
hCryptProv, //指定容器的CSP模組控制代碼
AT_SIGNATURE, //指定私密金鑰的類型
&hKey)) //原來接收穫取的密鑰控制代碼
{
_tprintf(TEXT("A signature key is available./n"));
}
else
{
_tprintf(TEXT("No signature key is available./n"));
if(GetLastError() == NTE_NO_KEY) // NTE_NO_KEY意味著密鑰不存在,下面就產生一個密鑰
{
_tprintf(TEXT("The signature key does not exist./n"));
_tprintf(TEXT("Create a signature key pair./n"));
if(CryptGenKey( // CryptGenKey產生一個密鑰
hCryptProv, //指定CSP模組的控制代碼
AT_SIGNATURE, //對於公開金鑰密碼系統,產生一個私密金鑰和一個公開金鑰,這個參數指定了這個密鑰是公開金鑰,於是產生了一個密碼對。如果不是公開金鑰系統,則指定了密碼演算法,具體看MSDN。
0, //指定了產生密鑰的類型,這個參數的說明挺多的,想擷取更為詳盡的資料請看MSDN。
&hKey))
{
_tprintf(TEXT("Created a signature key pair./n"));
}
else
{
MyHandleError(TEXT("Error occurred creating a ")
TEXT("signature key./n"));
}
}
else
{
MyHandleError(TEXT("An error other than NTE_NO_KEY ")
TEXT("getting a signature key./n"));
}
} // End if.
_tprintf(TEXT("A signature key pair existed, or one was ")
TEXT("created./n/n"));
if(hKey) //將密鑰控制代碼銷毀
{
if(!(CryptDestroyKey(hKey)))
{
MyHandleError(TEXT("Error during CryptDestroyKey."));
}
hKey = NULL;
}
下面這部分和上面是類似的,只不過密鑰類型不相同而已。
if(CryptGetUserKey(
hCryptProv,
AT_KEYEXCHANGE,
&hKey))
{
_tprintf(TEXT("An exchange key exists./n"));
}
else
{
_tprintf(TEXT("No exchange key is available./n"));
// Check to determine whether an exchange key
// needs to be created.
if(GetLastError() == NTE_NO_KEY)
{
// Create a key exchange key pair.
_tprintf(TEXT("The exchange key does not exist./n"));
_tprintf(TEXT("Attempting to create an exchange key ")
TEXT("pair./n"));
if(CryptGenKey(
hCryptProv,
AT_KEYEXCHANGE,
0,
&hKey))
{
_tprintf(TEXT("Exchange key pair created./n"));
}
else
{
MyHandleError(TEXT("Error occurred attempting to ")
TEXT("create an exchange key./n"));
}
}
else
{
MyHandleError(TEXT("An error other than NTE_NO_KEY ")
TEXT("occurred./n"));
}
}
// Destroy the exchange key.
if(hKey)
{
if(!(CryptDestroyKey(hKey)))
{
MyHandleError(TEXT("Error during CryptDestroyKey."));
}
hKey = NULL;
}
// Release the CSP.
if(hCryptProv)
{
if(!(CryptReleaseContext(hCryptProv, 0)))
{
MyHandleError(TEXT("Error during CryptReleaseContext."));
}
}
_tprintf(TEXT("Everything is okay. A signature key "));
_tprintf(TEXT("pair and an exchange key exist in "));
_tprintf(TEXT("the %s key container./n"), pszContainerName);
} // End main.
下面我們再通過一個程式,用幾種不同的方法,將CryptAcquireContext和其他的API函數聯絡起來,看看它們是如何與一個CSP和容器工作的。這個程式示範了以下內容和幾個函數。
一. 用CryptAcquireContext擷取一個預設容器的預設CSP的控制代碼。如果預設容器不存在,用CryptAcquireContext建立一個。
二. 用CryptGetProvParam擷取CSP和容器的資訊。
三. 用CryptContextAddRef增加CSP的引用計數器的數值。
四. 用CryptAcquireContext建立一個指定的容器
五. 用CryptAcquireContext刪除一個容器
六. 用一個新建立的容器擷取一個CSP的控制代碼。
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <Wincrypt.h>
異常處理函數省略
void main(void)
{
HCRYPTPROV hCryptProv; //定義CSP控制代碼
if(CryptAcquireContext(
&hCryptProv,
NULL, //預設容器
NULL, //預設CSP
PROV_RSA_FULL,
0))
{
_tprintf(TEXT("CryptAcquireContext succeeded./n"));
}
else
{
if (GetLastError() == NTE_BAD_KEYSET) //同樣,如果當不存在這樣的容器的時候,建立一個
{
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
_tprintf(TEXT("CryptAcquireContext succeeded./n"));
}
else
{
MyHandleError(TEXT("Could not create the default ")
TEXT("key container./n"));
}
}
else
{
MyHandleError(TEXT("A general error running ")
TEXT("CryptAcquireContext."));
}
}
CHAR pszName[1000];
DWORD cbName;
cbName = 1000;
if(CryptGetProvParam(
hCryptProv, //CSP模組控制代碼
PP_NAME, //指定擷取哪些資訊,這裡是指定擷取CSP名字的資訊
(BYTE*)pszName, //緩衝區接受資訊傳回值
&cbName,
0))
{
_tprintf(TEXT("CryptGetProvParam succeeded./n"));
printf("Provider name: %s/n", pszName);
}
else
{
MyHandleError(TEXT("Error reading CSP name./n"));
}
//---------------------------------------------------------------
// Read the name of the key container.
cbName = 1000;
if(CryptGetProvParam(
hCryptProv,
PP_CONTAINER, //擷取容器名字
(BYTE*)pszName,
&cbName,
0))
{
_tprintf(TEXT("CryptGetProvParam succeeded./n"));
printf("Key Container name: %s/n", pszName);
}
else
{
MyHandleError(TEXT("Error reading key container name./n"));
}
if(CryptContextAddRef( // CryptContextAddRef是向一個CSP的引用計數器增加一個值的函數
hCryptProv,
NULL, //保留值,必須為NULL
0)) //保留值,必須為0
{
_tprintf(TEXT("CryptcontextAddRef succeeded./n"));
}
else
{
MyHandleError(TEXT("Error during CryptContextAddRef!/n"));
}
//---------------------------------------------------------------
// The reference count on hCryptProv is now greater than one.
// The first call to CryptReleaseContext will not release the
// provider handle.
//---------------------------------------------------------------
// Release the context once.
if (CryptReleaseContext(hCryptProv, 0)) // CryptReleaseContext是用來釋放CSP控制代碼的,當這個函數調用一次的時候,CSP裡面的引用計數就減少一,當引用計數減少的0的時候。CSP將不能再被這個程式中的任何函數調用了。
{
_tprintf(TEXT("The first call to CryptReleaseContext ")
TEXT("succeeded./n"));
}
else
{
MyHandleError(TEXT("Error during ")
TEXT("CryptReleaseContext #1!/n"));
}
if (CryptReleaseContext(hCryptProv, 0)) //再次釋放CSP模組
{
_tprintf(TEXT("The second call to CryptReleaseContext ")
TEXT("succeeded./n"));
}
else
{
MyHandleError(TEXT("Error during ")
TEXT("CryptReleaseContext #2!/n"));
}
下面是從PROV_RSA_FULL的CSP模組中建立一個自己的容器
LPCTSTR pszContainerName = TEXT("My Sample Key Container");
hCryptProv = NULL;
if(CryptAcquireContext(
&hCryptProv,
pszContainerName,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))
{
_tprintf(TEXT("CryptAcquireContext succeeded. /n"));
_tprintf(TEXT("New key set created. /n"));
//-----------------------------------------------------------
// Release the provider handle and the key container.
if(hCryptProv)
{
if(CryptReleaseContext(hCryptProv, 0))
{
hCryptProv = NULL;
_tprintf(TEXT("CryptReleaseContext succeeded. /n"));
}
else
{
MyHandleError(TEXT("Error during ")
TEXT("CryptReleaseContext!/n"));
}
}
}
else
{
if(GetLastError() == NTE_EXISTS)
{
_tprintf(TEXT("The named key container could not be ")
TEXT("created because it already exists./n"));
}
else
{
MyHandleError(TEXT("Error during CryptAcquireContext ")
TEXT("for a new key container."));
}
}
if(CryptAcquireContext(
&hCryptProv,
pszContainerName,
NULL,
PROV_RSA_FULL,
0))
{
_tprintf(TEXT("Acquired the key set just created. /n"));
}
else
{
MyHandleError(TEXT("Error during CryptAcquireContext!/n"));
}
//---------------------------------------------------------------
// Perform cryptographic operations.
//---------------------------------------------------------------
if(CryptReleaseContext(
hCryptProv,
0))
{
_tprintf(TEXT("CryptReleaseContext succeeded. /n"));
}
else
{
MyHandleError(TEXT("Error during CryptReleaseContext!/n"));
}
if(CryptAcquireContext(
&hCryptProv,
pszContainerName,
NULL,
PROV_RSA_FULL,
CRYPT_DELETEKEYSET)) //CRYPT_DELETEKEYSET意味著CryptAcquireContex刪除一個指定的容器
{
_tprintf(TEXT("Deleted the key container just created. /n"));
}
else
{
MyHandleError(TEXT("Error during CryptAcquireContext!/n"));
}
}
就先寫到這,大家多提寶貴意見。附件中有這兩個程式編譯好的程式,來源程式就是文章中的。