Crypto API 学习笔记一
微软公司在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"));
}
}
Cryptography API 学习笔记二
前面学习了密钥容器的建立的一些知识,现在我们接下来自然是学习如何获取一个密钥,首先是获取一个session key,即对话密钥,是对称密钥。
这些学习笔记主要是从MSDN中的例子中,学习基本的Cryptography API的用法,了解一些用法的过程。欢迎大家多提宝贵意见。
下面依旧是从一个小程序开始。这个程序的学习任务有以下几个:
任务一:调用CryptAcquireContext,获取一个缺省CSP和缺省密钥容器的句柄
任务二:用CryptCreateHash去创建一个空的哈希对象
任务三:用CryptHashData.哈希密码
任务四:调用CryptDeriveKey获取一个对话密钥
任务五:销毁密码和哈希后的数据
任务六:释放CSP
#include <stdio.h>
#include <string.h>
#include <conio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void GetConsoleInput(char*, int);
void main()
{
HCRYPTPROV hCryptProv; 定义CSP句柄
HCRYPTKEY hKey; 定义密钥句柄
HCRYPTHASH hHash; 定义一个HASH对象的句柄
CHAR szPassword[512] = ""; 定义512大小的字符数组,用来保存密码
DWORD dwLength; 保存密码长度
fprintf(stderr,"Enter a password to be used to create a key:");
GetConsoleInput(szPassword, 512); 获取密码,这个是自己写的函数,目的是在屏幕上显示的是*。
printf("The password has been stored./n");
dwLength = strlen(szPassword);
if(CryptAcquireContext( 以下是获取一个缺省的PROV_RSA_FULL CSP 句柄
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("A context has been acquired. /n");
}
else
{
MyHandleError("Error during CryptAcquireContext!");
}
//--------------------------------------------------------------------
if(CryptCreateHash( 调用CryptCreateHash创建一个HASH对象
hCryptProv, 一个CSP句柄
CALG_MD5, 确定哈希算法
0, 对于非密钥算法,这个参数一定是0,如果是密钥算法,那么这个参数就是密钥
0, 保留参数,为0
&hHash)) 一个哈希对象的指针
{
printf("An empty hash object has been created. /n");
}
else
{
MyHandleError("Error during CryptCreateHash!");
}
//--------------------------------------------------------------------
if(CryptHashData( 调用CryptHashData哈希密码
hHash, 哈希对象
(BYTE *)szPassword, 指向缓冲区的地址
dwLength, 密码长度
0))
{
printf("The password has been hashed. /n");
}
else
{
MyHandleError("Error during CryptHashData!");
}
//--------------------------------------------------------------------
if(CryptDeriveKey( 调用CryptDeriveKey获取对话密码
hCryptProv, CSP句柄
CALG_RC2, 一个ALG_ID结构,用来指定对称密钥生成的算法
hHash, 哈希对象
CRYPT_EXPORTABLE, 指定生成密钥的类型,CRYPT_EXPORTABLE意味着这个程序生成的密钥可以被其它程序调用,而不是仅仅限于这个程序当中。但是它不能用于非对称密码中。
&hKey))
{
printf("The key has been derived. /n");
}
else
{
MyHandleError("Error during CryptDeriveKey!");
}
if(hHash) 销毁哈希对象
{
if(!(CryptDestroyHash(hHash)))
MyHandleError("Error during CryptDestroyHash");
}
if(hKey) 销毁密钥句柄
{
if(!(CryptDestroyKey(hKey)))
MyHandleError("Error during CryptDestroyKey");
}
if(hCryptProv) 销毁CSP句柄
{
if(!(CryptReleaseContext(hCryptProv, 0)))
MyHandleError("Error during CryptReleaseContext");
}
printf("The program to derive a key completed without error. /n");
} // end main
void MyHandleError函数的实现省略,在上一篇中有
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');
}
下面讲讲如何复制一个对话密钥,照例是从一个程序讲起,完成以下任务。获取一个CSP句柄,创建一个对话密钥,复制密钥。改变密钥生成过程,随机填充一个缓冲区,销毁密钥句柄,释放CSP句柄。
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void main()
{
//-------------------------------------------------------------------
// Declare and initialize variables.
HCRYPTPROV hCryptProv; CSP句柄
HCRYPTKEY hOriginalKey; 源密钥句柄
HCRYPTKEY hDuplicateKey; 复制后的密钥句柄
DWORD dwMode;
BYTE pbData[16];
printf("This program creates a session key and duplicates /n");
printf("that key. Next, parameters are added to the original /n");
printf("key. Finally, both keys are destroyed. /n/n");
if(CryptAcquireContext( 获取CSP句柄,前面叙述过了,这里就不叙述了
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("CryptAcquireContext succeeded. /n");
}
else
{
MyHandleError("Error during CryptAcquireContext!/n");
}
//-------------------------------------------------------------------
if (CryptGenKey( 生成一个CALG_RC4算法生成的密钥,保存在hOriginalKey中
hCryptProv,
CALG_RC4, ALG_ID结构,指定生成这个密钥使用的算法
0,
&hOriginalKey))
{
printf("Original session key is created. /n");
}
else
{
MyHandleError("ERROR - CryptGenKey.");
}
if (CryptDuplicateKey( 复制密钥
hOriginalKey, 源密钥
NULL, 保留参数,必须为NULL
0, 保留参数,必须为0
&hDuplicateKey)) 副本密钥
{
printf("The session key has been duplicated. /n");
}
else
{
MyHandleError("ERROR - CryptDuplicateKey");
}
给源密钥设置附加参数
dwMode = CRYPT_MODE_ECB; CRYPT_MODE_ECB是一个没有反馈的块加密模式
if(CryptSetKeyParam(
hOriginalKey,
KP_MODE, 指定密钥的某种属性被改变,
KP_MODE意味着改变的是加密模式
(BYTE*)&dwMode, 指向一个已经被初始化的缓冲区
0))
{
printf("Key Parameters set. /n");
}
else
{
MyHandleError("Error during CryptSetKeyParam.");
}
if(CryptGenRandom( 随机填充一块缓冲区
hCryptProv, CSP句柄
8, 缓冲区大小
pbData)) 缓冲区地址
{
printf("Random sequence generated. /n");
}
else
{
MyHandleError("Error during CryptGenRandom.");
}
if(CryptSetKeyParam( 再次给密钥设置属性
hOriginalKey,
KP_IV, KP_IV意味着,这个函数的第三个参数指向一个BYTE数组,数组大小为块大小/8。
pbData,
0))
{
printf("Parameter set with random sequence as "
"initialization vector. /n");
}
else
{
MyHandleError("Error during CryptSetKeyParam.");
}
//-------------------------------------------------------------------
if (hOriginalKey) 以下依次销毁,释放源密钥句柄,副本句柄,CSP句柄,
if (!CryptDestroyKey(hOriginalKey))
MyHandleError("Failed CryptDestroyKey/n");
if (hDuplicateKey)
if (!CryptDestroyKey(hDuplicateKey))
MyHandleError("Failed CryptDestroyKey/n");
if(hCryptProv)
if (!CryptReleaseContext(hCryptProv, 0))
MyHandleError("Failed CryptReleaseContext/n");
printf("/nThe program ran to completion without error. /n");
} // End of main.
当我们已经生成了一个密钥,我们下面要做的是如何保存,导出,导入一个对话密钥。将一个密钥保存起来,导出,保存到硬盘上。这样,这个密钥就可以在其它应用程序上使用,而不会随着生成密钥程序的关闭而丢失了。
下面这段程序就是用来演示如何保存,导出一个对话密钥的。
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void main(void)
{
//--------------------------------------------------------------------
// Declare and initialize variables.
HCRYPTPROV hProv; // CSP handle
HCRYPTKEY hSignKey; // Signature key pair handle
HCRYPTKEY hXchgKey; // Exchange key pair handle
HCRYPTKEY hKey; // Session key handle
BYTE *pbKeyBlob; // Pointer to a simple key BLOB
DWORD dwBlobLen; // The length of the key BLOB
//--------------------------------------------------------------------
// Acquire a cryptographic provider context handle.
if(CryptAcquireContext( 获取一个缺省容器的CSP句柄
&hProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("The CSP has been acquired. /n");
}
else
{
MyHandleError("Error during CryptAcquireContext.");
}
if(CryptGetUserKey( 获取一个AT_SIGNATURE类型的密钥句柄
hProv,
AT_SIGNATURE,
&hSignKey))
{
printf("The signature key has been acquired. /n");
}
else
{
MyHandleError("Error during CryptGetUserKey for signkey.");
}
//--------------------------------------------------------------------
if(CryptGetUserKey( 获取一个AT_KEYEXCHANGE,类型的密钥句柄,保存在hXchgKey中
hProv,
AT_KEYEXCHANGE,
&hXchgKey))
{
printf("The key exchange key has been acquired. /n");
}
else
{
printf("Error during CryptGetUserKey exchange key.");
}
// Generate a session key.
if (CryptGenKey( 生成一个CRYPT_EXPORTABLE(可导出的),CALG_RC4(指定算法)的密钥,保存在hKey
hProv,
CALG_RC4,
CRYPT_EXPORTABLE,
&hKey))
{
printf("Original session key is created. /n");
}
else
{
MyHandleError("ERROR -- CryptGenKey.");
}
if(CryptExportKey( CryptExportKey导出一个密钥
hKey, 将要导出的密钥的句柄
hXchgKey, 用户最终使用到的密钥的句柄
SIMPLEBLOB, 指定BLOB的类型,SIMPLEBLOB说明是用来导出对话密钥的
0, 指定密钥的附加属性
NULL,
&dwBlobLen)) 当时这个函数在这里的主要
目的是得到这个BLOB的长度
{
printf("Size of the BLOB for the session key determined. /n");
}
else
{
MyHandleError("Error computing BLOB length.");
}
if(pbKeyBlob = (BYTE*)malloc(dwBlobLen))
{
printf("Memory has been allocated for the BLOB. /n");
}
else
{
MyHandleError("Out of memory. /n");
}
if(CryptExportKey( 这是这个函数才是真正的导出密钥
hKey,
hXchgKey,
SIMPLEBLOB,
0,
pbKeyBlob,
&dwBlobLen))
{
printf("Contents have been written to the BLOB. /n");
}
else
{
MyHandleError("Error during CryptExportKey.");
}
free(pbKeyBlob); 释放内存
// Destroy the session key.
if(hKey)
CryptDestroyKey(hKey);
// Destroy the signature key handle.
if(hSignKey)
CryptDestroyKey(hSignKey);
// Destroy the key exchange key handle.
if(hXchgKey)
CryptDestroyKey(hXchgKey);
// Release the provider handle.
if(hProv)
CryptReleaseContext(hProv, 0);
printf("The program ran to completion without error. /n");
}
Crypto API 学习笔记三
Encoding and Decoding Data
下面逐渐进入主题了,现在来讲讲是如何对数据进行Encoding and Decoding的。依旧是从一段程序中开始。
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void main(void)
{
HCRYPTMSG hMsg; 指向一个消息句柄
BYTE* pbContent; 一个BYTE指针指向消息
DWORD cbContent; 消息长度
DWORD cbEncodedBlob; ECODE的BLOB的大小
BYTE *pbEncodedBlob; 一个BYTE指针指向ENCODE BLOB
DWORD cbData = sizeof(DWORD); 数据大小
DWORD cbDecoded; Decode内容大小
BYTE *pbDecoded; 指向Decode的指针
pbContent = (BYTE*) "Security is our only business";
cbContent = strlen((char *) pbContent)+1;
printf("The original message => %s/n",pbContent);
if(cbEncodedBlob = CryptMsgCalculateEncodedLength(
MY_ENCODING_TYPE, 指定Encode类型,在程序的开头已经预定义了,MY_ENCODING_TYPE 就是 PKCS_7_ASN_ENCODING | X509_ASN_ENCODING
0, // Flags
CMSG_DATA, 定义了数据的类型,这里指定为BYTE型的字符串
NULL,
NULL,
cbContent)) 内容的大小
这里的的函数的作用是计算指定消息Encode所需要的最大的长度,通过计算,为一个BLOB分配内存空间。
{
printf("The length of the data has been calculated. /n");
}
else
{
MyHandleError("Getting cbEncodedBlob length failed");
}
为encode blob分配内存空间
if(pbEncodedBlob = (BYTE *) malloc(cbEncodedBlob))
{
printf("Memory has been allocated for the signed message. /n");
}
else
{
MyHandleError("Memory allocation failed");
}
if(hMsg = CryptMsgOpenToEncode( CryptMsgOpenToEncode为Encode,开一个消息
MY_ENCODING_TYPE, Encode类型,文件开始有说明
0, // Flags
CMSG_DATA, 指定Message的类型,CMSG_DATA说明类型没用到
NULL, 现在没有到,为NULL
NULL, 同上
NULL)) 不是流加密,这个参数为NULL
{
printf("The message to be encoded has been opened. /n");
}
else
{
MyHandleError("OpenToEncode failed");
}
if(CryptMsgUpdate( CryptMsgUpdate将数据加到消息中,可以通过循环,将数据一段段的加得到消息中
hMsg, 一个小心句柄
pbContent, 指向数据的指针
cbContent, 数据的大小
TRUE)) TRUE表明这个是最后一段数据,在开个消息的时候,如果CMSG_DETACHED_FLAG有使用到,这设为FALSE,否则为TRUE。
{
printf("Content has been added to the encoded message. /n");
}
else
{
MyHandleError("MsgUpdate failed");
}
if(CryptMsgGetParam( CryptMsgGetParam是获取一个消息中的参数
hMsg, 一个消息句柄
CMSG_BARE_CONTENT_PARAM, 指定要获取的参数的类型
0,
pbEncodedBlob, 一个接受数据的内存地址
&cbEncodedBlob)) BLOB的大小,即是上面接受的数据的大小
{
printf("Message encoded successfully. /n");
}
else
{
MyHandleError("MsgGetParam failed");
}
释放消息句柄
if(hMsg)
CryptMsgClose(hMsg);
if(hMsg = CryptMsgOpenToDecode( 开个Decode的小心句柄,参数和上面的Encode一样
MY_ENCODING_TYPE,
0,
CMSG_DATA,
NULL,
NULL,
NULL))
{
printf("The message to decode is open. /n");
}
else
{
MyHandleError("OpenToDecode failed");
}
下面的过程和Encode类似,调用的函数和上面相同,只不过是过程逆向
printf("/nThe length of the encoded message is %d./n/n",
cbEncodedBlob);
if(CryptMsgUpdate(
hMsg, // Handle to the message
pbEncodedBlob, // Pointer to the encoded BLOB
cbEncodedBlob, // Size of the encoded BLOB
TRUE)) // Last call
{
printf("The encoded BLOB has been added to the message. /n");
}
else
{
MyHandleError("Decode MsgUpdate failed");
}
if(CryptMsgGetParam( CryptMsgGetParam的调用和上面有所不同,这里一共是调用两次,第一次的作用主要是得到消息的大小,第二次是得到消息所在的内存地址
hMsg, 消息句柄
CMSG_CONTENT_PARAM, // Parameter type
0,
NULL, // Address for returned
// information
&cbDecoded)) // Size of the returned
// information
{
printf("The decoded message size is %d. /n", cbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM failed");
}
if(pbDecoded = (BYTE *) malloc(cbDecoded))
{
printf("Memory has been allocated for the decoded message./n");
}
else
{
MyHandleError("Decoding memory allocation failed.");
}
if(CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbDecoded, // Address for returned
// information
&cbDecoded)) // Size of the returned
// information
{
printf("The message is %s./n",(LPSTR)pbDecoded);
}
else
{
MyHandleError("Decode CMSG_CONTENT_PARAM #2 failed");
}
if(pbEncodedBlob)
free(pbEncodedBlob);
if(pbDecoded)
free(pbDecoded);
if(hMsg)
CryptMsgClose(hMsg);
printf("This program ran to completion without error. /n");
} // End of main
下面我们来看看如何哈希一个对话密钥,这个密钥可以用来对一个消息,文件进行加密。我们依旧从一个程序开始。
#include <stdio.h>
#include <windows.h>
#include <Wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
void MyHandleError(char *s);
void main()
{
//--------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Declare variables.
HCRYPTPROV hCryptProv;
HCRYPTHASH hHash;
HCRYPTKEY hKey;
//--------------------------------------------------------------------
// Begin processing.
printf("Process beginning. Creating a session key. /n");
if(CryptAcquireContext( 首先依旧是获取一个缺省的CSP句柄
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("CryptAcquireContext complete. /n");
}
else
{
MyHandleError("Acquisition of context failed.");
}
if(CryptCreateHash( 创建一个CALG_MD5算法的HASH对象,这个hash对象用的是MD5算法
hCryptProv, 指定一个CSP句柄
CALG_MD5, 指定算法
0,
0,
&hHash))
{
printf("An empty hash object has been created. /n");
}
else
{
MyHandleError("Error during CryptBeginHash!/n");
}
if(CryptGenKey( 创建密钥
hCryptProv, 传入一个CSP句柄
CALG_RC2, 指明密钥身成所用算法
CRYPT_EXPORTABLE, 说明密钥是可以导出到CSP,用于这个应用程序外的
&hKey))
{
printf("A random session key has been created. /n");
}
else
{
MyHandleError("Error during CryptGenKey!/n");
}
if(CryptHashSessionKey( 对生成的密钥进行hash
hHash,
hKey,
0))
{
printf("The session key has been hashed. /n");
}
else
{
MyHandleError("Error during CryptHashSessionKey!/n");
}
在这里就可以添加代码,用生成的密钥进行加密
if(hHash)
{
if(!(CryptDestroyHash(hHash)))
MyHandleError("Error during CryptDestroyHash");
}
if(hKey)
{
if(!(CryptDestroyKey(hKey)))
MyHandleError("Error during CryptDestroyKey");
}
// Release the CSP.
if(hCryptProv)
{
if(!(CryptReleaseContext(hCryptProv,0)))
MyHandleError("Error during CryptReleaseContext");
}
printf("Create random session key completed without error. /n");
} // end main