1. 程式人生 > >《ASCE1885的信息安全》のCryptoAPI---密鑰的產生和交換函數

《ASCE1885的信息安全》のCryptoAPI---密鑰的產生和交換函數

加密 加密算法 代碼 md5 troy 格式 bsp 公私鑰 origin

在公開密碼算法的前提下,數據的安全取決於密鑰。因此,密鑰的產生、銷毀、交換(分發)是數據保密工作中的重要部分。

CryptoAPI密鑰產生和交換函數主要有生成密鑰函數CryptGenKey、派生密鑰函數CryptDeriveKey、銷毀密鑰函數CryptDestroyKey、復制密鑰函數CryptDuplicateKey、導出密鑰函數CryptExportKey、導入密鑰函數CryptImportKey、獲得密鑰參數函數CryptGetKeyParam、設置密鑰參數函數CryptSetKeyParam、產生隨機函數CryptGenRandom、

一、生成函數CryptGenKey:

功能:產生一個隨機的對稱或非對稱算法的密鑰;

原型:

BOOL WINAPI CryptGenKey(

__in HCRYPTPROV hProv, //CSP句柄指針

__in ALG_ID Algid, //密碼算法標識

__in DWORD dwFlags, //標誌位,指定生成密鑰的參數,

//如對稱密鑰的長度,RSA密鑰的長度

__out HCRYPTKEY *phKey //新產生的密鑰的句柄

);

其中,Algid參數取值如下:

CALG_HMAC---HMAC帶密鑰的摘要算法;

CALG_MD2---MD2摘要算法;

CALG_MD4---MD4摘要算法;

CALG_MD5---MD5摘要算法;

CALG_SHA---SHA摘要算法;

CALG_SHA1---SHA1摘要算法;

CALG_MAC---MAC算法;

CALG_SSL3_SHAMD5---SSLV3客戶端算法;

CALG_RSA_SIGN---RSA簽名算法;

CALG_DSS_SIGN---DSS簽名算法;

CALG_RSA_KEYX---RSA加密算法;

CALG_DES---DES---對稱加密算法;

CALG_3DES_112---112位的3DES加密算法;

CALG_3DES---3DES加密算法;

CALG_RC2---RC2分組算法;

CALG_RC4---RC4流加密算法。

返回值:操作成功返回TRUE,否則返回FALSE,使用GetLastError獲取更多信息。

二、派生密鑰函數CryptDeriveKey:

功能:根據基礎數據派生以對稱密鑰(會話密鑰);

原型:

BOOL WINAPI CryptDeriveKey(

__in HCRYPTPROV hProv, //CSP句柄指針

__in ALG_ID Algid, //密碼算法標識

__in HCRYPTHASH hBaseData, //基礎數據的摘要句柄

__in DWORD dwFlags, //標誌位

__inout HCRYPTKEY *phKey //新產生的會話密鑰句柄

);

此函數和CryptGenKey函數相似,不同的是CryptGenKey是通過隨機數產生的,而CryptDeriveKey是通過指定的數據產生的。CryptDeriveKey只能產生對稱算法的會話密鑰,不能產生非對稱算法的公/私鑰。

返回值:操作成功返回TRUE,否則返回FALSE,使用GetLastError獲取更多信息。

三、銷毀密鑰函數CryptDestroyKey:

功能:銷毀密鑰;

原型:

BOOL WINAPI CryptDestroyKey(

__in HCRYPTKEY hKey //密鑰句柄

);

返回值:操作成功返回TRUE,否則返回FALSE,使用GetLastError()獲取更多信息。

下面的示例由一個密碼產生會話密鑰:

#include <windows.h>

#include <wincrypt.h>

#include <iostream>

#include <string.h>

#include <conio.h>

#include <stdio.h>

#pragma comment(lib, "crypt32.lib")

#define PASSWORD_LENGTH 512

void HandleError(TCHAR* s);

void GetConsoleInput(CHAR*, UINT);

int _tmain(int argc, _TCHAR* argv[])

{

HCRYPTPROV hCryptProv;

HCRYPTKEY hKey;

HCRYPTHASH hHash;

CHAR szPassword[PASSWORD_LENGTH] = "";

DWORD dwLength;

fprintf(stderr, "請輸入用於產生會話密鑰的密碼:");

//獲得用戶輸入密碼,控制臺上顯示的是*號

GetConsoleInput(szPassword, PASSWORD_LENGTH);

printf("密碼已保存/n");

dwLength = (DWORD)strlen(szPassword);

//獲得CSP句柄

if(!CryptAcquireContext(&hCryptProv, NULL, NULL,

PROV_RSA_FULL, 0))

{

HandleError(L"獲取CSP句柄時出錯");

}

//創建一個空的哈希對象

if(!CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &hHash))

{

HandleError(L"創建空的哈希對象時出錯");

}

//對密碼進行哈希

if(!CryptHashData(hHash, (BYTE*)szPassword, dwLength, 0))

{

HandleError(L"對密碼進行哈希時出錯");

}

//由密碼的哈希值創建會話密鑰

if(!CryptDeriveKey(hCryptProv, CALG_RC2, hHash,

CRYPT_EXPORTABLE, &hKey))

{

HandleError(L"由密碼的哈希值創建會話密鑰時出錯");

}

//這裏使用上面得到的會話密鑰進行加解密等操作,略...

//釋放資源

if(hHash)

{

if(!CryptDestroyHash(hHash))

HandleError(L"銷毀哈希對象時出錯");

}

if(hKey)

{

if(!CryptDestroyKey(hKey))

HandleError(L"銷毀會話密鑰時出錯");

}

if(hCryptProv)

{

if(!CryptReleaseContext(hCryptProv, 0))

HandleError(L"釋放CSP句柄時出錯");

}

printf("程序正常結束運行/n");

system("pause");

return 0;

}

void HandleError(TCHAR* s)

{

_tprintf(TEXT("程序運行出現錯誤./n"));

_tprintf(TEXT("%s/n"),s);

_tprintf(TEXT("錯誤代碼%x/n."), GetLastError());

_tprintf(TEXT("程序終止./n"));

exit(1);

}

//從鍵盤獲取字符串輸入,並控制臺上*號顯示

void GetConsoleInput(char* strInput, UINT 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‘);

}

四、復制密鑰函數CryptDuplicateKey:

功能:復制一個密鑰,產生一個密鑰的拷貝,包括其狀態;

原型:

BOOL WINAPI CryptDuplicateKey(

__in HCRYPTKEY hKey, //密鑰句柄

__in DWORD *pdwReserved, //保留參數,必須為NULL

__in DWORD dwFlags, //保留參數,必須為0

__out HCRYPTKEY *phKey //復制後的密鑰句柄

);

返回值:操作成功返回TRUE,否則返回FALSE,使用GetLastError()獲取更多信息。

五、獲得密鑰參數函數CryptGetKeyParam:

功能:獲得key句柄的各項參數;

原型:

BOOL WINAPI CryptGetKeyParam(

__in HCRYPTKEY hKey, //密鑰句柄

__in DWORD dwParam, //參數類型

__out BYTE *pbData, //接收key參數數據緩沖區指針

__inout DWORD *pdwDataLen, //輸出的數據長度

__in DWORD dwFlags //保留參數,必須為0

);

其中,對於所有的key,dwParam可取值如下:

KP_ALGID---算法ID;

KP_BLOCKLEN---如果hKey為會話密鑰,則此參數獲得對稱算法分組長度(單位為比特);

如果hKey為非對稱密鑰,則此參數獲得密鑰對加密強度(單位為比特,如1024bits RSA)

KP_KEYLEN---密鑰長度;

KP_SALT---salt值,不使用於非對稱密鑰對;

對於對稱密鑰或者會話密鑰,dwParam還可取值如下:

KP_IV---初始化向量;

KP_PADDING---補位的方式;

KP_MODE---加密模式(CBC、ECB等)。

返回值:操作成功返回TRUE,否則返回FALSE,調用GetLastError()獲得更多信息。

六、獲得密鑰參數函數CryptSetKeyParam:

功能:設置key句柄的各項參數;

原型:

BOOL WINAPI CryptSetKeyParam(

__in HCRYPTKEY hKey, //密鑰句柄

__in DWORD dwParam, //參數類型

__in const BYTE *pbData, //參數數據緩沖區指針

__in DWORD dwFlags //只用於dwParam為KP_ALGID時

);

其中dwParam可取值如下:

KP_ALGID---算法ID;

KP_SALT---salt值,不適用於非對稱密鑰對;

KP_IV---初始化向量;

KP_PADDING---補位的方式;

KP_MODE---加密模式(CBC,ECB等);

KP_CERTIFICATE---設置數字證書,對於RSA密鑰,設置與其對應的數字證書;pbData即為DER編碼的X509證書指針;

KP_KEYEXCHANGE_PIN---設置操作該交換密鑰的口令;

KP_SIGNATRUE_PIN---設置操作該簽名密鑰的口令。

返回值:操作成功返回TRUE,否則返回FALSE,調用GetLastError()獲得更多信息。

七、獲得隨機數函數CryptGenRandom:

功能:生成隨機數;

原型:

BOOL WINAPI CryptGenRandom(

__in HCRYPTPROV hProv, //CSP句柄

__in DWORD dwLen, //生成隨機數的長度

__inout BYTE *pbBuffer //接收隨機數緩沖區的指針

);

返回值:操作成功返回TRUE,否則返回FALSE,調用GetLastError()獲得更多信息。

下面代碼示例演示了復制一個會話密鑰的過程:

#include <windows.h>

#include <wincrypt.h>

#include <iostream>

#include <string.h>

#include <conio.h>

#include <stdio.h>

#pragma comment(lib, "crypt32.lib")

//#define ASCE_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

#define PASSWORD_LENGTH 512

void HandleError(TCHAR* s);

void GetConsoleInput(CHAR*, UINT);

int _tmain(int argc, _TCHAR* argv[])

{

HCRYPTPROV hCryptProv;

HCRYPTKEY hOriginalKey;

HCRYPTKEY hDuplicateKey;

DWORD dwMode;

BYTE pbData[16];

printf("本程序創建一個會話密鑰,並復制它,同時添加參數到原會話密鑰中/n");

if(!CryptAcquireContext(&hCryptProv, NULL, NULL,

PROV_RSA_FULL, 0))

{

HandleError("創建CSP句柄時出錯");

}

//生成一個會話密鑰

if(!CryptGenKey(hCryptProv, CALG_RC4, 0, &hOriginalKey))

{

HandleError("生成會話密鑰時出錯");

}

//復制會話密鑰

if(!CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey))

{

HandleError("復制會話密鑰時出錯");

}

//給原會話密鑰設置參數

dwMode = CRYPT_MODE_ECB;

if(!CryptSetKeyParam(hOriginalKey, KP_MODE,

(BYTE*)&dwMode, 0))

{

HandleError("給原會話密鑰設置參數時出錯");

}

//生成一個隨機初始化向量

if(!CryptGenRandom(hCryptProv, 8, pbData))

{

HandleError("生成一個隨機初始化向量時出錯");

}

//給原會話密鑰設置初始化向量

if(!CryptSetKeyParam(hOriginalKey, KP_IV, pbData, 0))

{

HandleError("給原會話密鑰設置初始化向量時出錯");

}

//釋放資源

if(hOriginalKey)

{

if(!CryptDestroyKey(hOriginalKey))

HandleError(L"銷毀原會話密鑰時出錯");

}

if(hDuplicateKey)

{

if(!CryptDestroyKey(hDuplicateKey))

HandleError(L"銷毀復制會話密鑰時出錯");

}

if(hCryptProv)

{

if(!CryptReleaseContext(hCryptProv, 0))

HandleError(L"釋放CSP句柄時出錯");

}

printf("程序正常結束運行/n");

system("pause");

return 0;

}

void HandleError(TCHAR* s)

{

_tprintf(TEXT("程序運行出現錯誤./n"));

_tprintf(TEXT("%s/n"),s);

_tprintf(TEXT("錯誤代碼%x/n."), GetLastError());

_tprintf(TEXT("程序終止./n"));

exit(1);

}

八、導出密鑰函數CryptExportKey:

功能:從CSP導出密鑰或密鑰對;

原型:

BOOL WINAPI CryptExportKey(

__in HCRYPTKEY hKey, //要導出的密鑰句柄

__in HCRYPTKEY hExpKey, //目標用戶的加密密鑰,用於加密要導出的數

__in DWORD dwBlobType, //pbData中的數據類型

__in DWORD dwFlags, //標誌位

__out BYTE *pbData, //接收導出密鑰數據塊的緩沖區指針

__inout DWORD *pdwDataLen //pbData字節大小

);

返回值:操作成功返回TRUE,否則返回FALSE,調用GetLastError()獲得更多信息。

九、導入密鑰函數CryptImportKey:

功能:把BLOB數據導入CSP,該函數可導入會話密鑰、公鑰、或者公私鑰對。

原型:

BOOL WINAPI CryptImportKey(

__in HCRYPTPROV hProv, //CSP句柄

__in BYTE *pbData, //BLOB數據

__in DWORD dwDataLen, //BLOB數據長度

__in HCRYPTKEY hPubKey, //此參數的意義根據CSP類型以及導入的BLOB數據的類型不同而不同:如果BLOB數據是由交換密鑰加密的,該參數就是交換密鑰的句柄

如果BLOB數據是由會話密鑰加密的,該參數就是會話密鑰的句柄

如果BLOB數據沒有被加密,則該參數為NULL

__in DWORD dwFlags, //標誌位

__out HCRYPTKEY *phKey //導入密鑰的句柄

);

返回值:操作成功返回TRUE,否則返回FALSE,調用GetLastError()獲得更多信息。

下面代碼演示了密鑰的導入和導出操作:

#include <windows.h>

#include <wincrypt.h>

#include <iostream>

#include <stdio.h>

#pragma comment(lib, "crypt32.lib")

//存在數組中的明文密鑰BLOB,數組格式必須如下所示:

// BLOBHEADER hdr;

// DWORD dwKeySize;

// BYTE rgbKeyData[];

BYTE DesKeyBlob[] =

{

0x08, 0x02, 0x00, 0x00, 0x01, 0x66, 0x00, 0x00, //BLOB header

0x08, 0x00, 0x00, 0x00, //key length, in bytes

0xf1, 0x0e, 0x25, 0x7c, 0x6b, 0xce, 0x0d, 0x34 //DES key with parity

};

//#define ASCE_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)

#define PASSWORD_LENGTH 512

void HandleError(TCHAR* s);

void GetConsoleInput(CHAR*, UINT);

int _tmain(int argc, _TCHAR* argv[])

{

HCRYPTPROV hCryptProv = NULL;

HCRYPTKEY hKey = NULL;

DWORD dwBlobLen;

BYTE* pbKeyBlob;

//獲得CSP句柄

if(!CryptAcquireContext(&hCryptProv, NULL, MS_ENHANCED_PROV,

PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))

{

//如果密鑰容器打不開,則創建一個新的

if(NTE_BAD_KEYSET == GetLastError())

{

if(!CryptAcquireContext(&hCryptProv, L"ASCEContainer",

MS_ENHANCED_PROV, PROV_RSA_FULL,

CRYPT_NEWKEYSET |CRYPT_VERIFYCONTEXT))

{

printf("創建新的密鑰容器出錯/n");

return -1;

}

else

{

printf("打開密鑰容器失敗/n");

return -1;

}

}

}

//導入PLAINTEXTKEYBLOB數組到密鑰容器中

if(!CryptImportKey(hCryptProv, DesKeyBlob,

sizeof(DesKeyBlob), 0, CRYPT_EXPORTABLE, &hKey))

{

printf("導入密鑰時出錯/n");

return -1;

}

//將剛導入的密鑰導出來,驗證是否正確

if(!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB,

0, NULL, &dwBlobLen))

{

printf("獲取BLOB長度時出錯/n");

return -1;

}

if(!(pbKeyBlob = (BYTE*)malloc(dwBlobLen)))

{

printf("分配內存時出錯/n");

return -1;

}

if(!CryptExportKey(hKey, NULL, PLAINTEXTKEYBLOB,

0, pbKeyBlob, &dwBlobLen))

{

printf("導出密鑰時出錯/n");

return -1;

}

DWORD count;

//打印明文BLOB以驗證密鑰導入是否正確

for(count=0; count<dwBlobLen;)

{

printf("%02x", pbKeyBlob[count]);

count++;

}

//釋放資源

if(pbKeyBlob)

{

free(pbKeyBlob);

}

if(hCryptProv)

{

if(!CryptReleaseContext(hCryptProv, 0))

HandleError(L"釋放CSP句柄時出錯");

}

printf("程序正常結束運行/n");

system("pause");

return 0;

}

《ASCE1885的信息安全》のCryptoAPI---密鑰的產生和交換函數