实验一-密码引擎-加密API研究
一、查找各种标准的原始文档,研究学习
Ⅰ.Crypto API
1.基本加密函数
①服务提供者函数
应用程序使用服务提供者函数来连接和断开一个 CSP。下图为主要的 API:
②密钥的产生和交换函数
密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。下图为主要的一些函数:
③编码/解码函数
有一些编码/解码函数,他们可以用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。
④数据加密/解密函数
这些函数支持数据的加密/解密操作。CryptEncrypt 和 CryptDecrypt 要求在被调用前指定
一个密钥。这个密钥可以由 CryptGenKey、CryptDeriveKey 或 CryptImportKey 产生。创建密钥时要指定加密算法。CryptSetKeyParam 函数可以指定额外的加密参数。
⑤哈希和数字签名函数
完成计算哈希、创建和校验数字签名
2.函数详解
获得 CSP 密钥容器句柄
BOOL WINAPI CryptAcquireContext( HCRYPTPROV *phProv, LPCTSTR pszContainer, LPCTSTR pszProvider, DWORD dwProvType, DWORD dwFlags );
这个函数是用来取得指定 CSP 密钥容器句柄,以后的任何加密操作就是针对此 CSP 句柄而言。函数首先查找由 dwProvType 和 pszProvider 指定的 CSP,如果找到了 CSP,函数就
查找由此 CSP 指定的密钥容器。由适当的 dwFlags 标志,这个函数就可以创建和销毁密钥容器,如果不要求访问私钥的话,也可以提供对 CSP 临时密钥容器的访问。
BOOL WINAPI CryptReleaseContext( HCRYPTPROV hProv, DWORD dwFlags );
hProv:[in]由CryptAcquireContext 获得的 CSP 句柄。
dwFlags:[in]保留。必须为 0。
HCRYPTPROV hCryptProv; if(CryptAcquireContext( hCryptProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) CryptReleaseContext(hCryptProv, NULL);
此函数释放 CSP 的句柄。对于每一次调用,CSP 的引用计数都减 1。当引用计数为 0时,CSP 上下文就会被系统释放变成无效句柄,以后针对此 CSP 句柄的函数不再可用。此函数并不销毁密钥容器或密钥对。
枚举 CSP
BOOL WINAPI CryptEnumProviders( DWORD dwIndex, DWORD *pdwReserved, DWORD dwFlags, DWORD *pdwProvType, LPTSTR pszProvName, DWORD *pcbProvName );
此函数得到第一个或下一个可用的 CSP。如果使用循环,就可以得到计算机上所有可用
的 CSP。
/--------------------------------------------------------------------
//声明初始化数据
DWORD cbName;
DWORD dwType;
DWORD dwIndex=0;
CHAR *pszName;
//循环枚举 CSP.
dwIndex = 0;
while(CryptEnumProviders(
dwIndex,
NULL,
0,
&dwType,
NULL, // 如果为 NULL,则 cbName 返回 CSP 名称所需字节数
&cbName //
))
{
//--------------------------------------------------------------------
//
//动态分配内存.
if (!(pszName = (LPTSTR)LocalAlloc(LMEM_ZEROINIT, cbName)))
{
printf("ERROR - LocalAlloc failed!");
exit(1);
}
//--------------------------------------------------------------------
// 获得 CSP 名称
if (CryptEnumProviders(
dwIndex++,
NULL,
0,
&dwType,
pszName,
&cbName
))
{
printf ("%4.0d %s\n",dwType, pszName);
}
else
{
printf("ERROR - CryptEnumProviders");
exit(1);
}
LocalFree(pszName);
} // 循环结束
获得 CSP 参数
BOOL WINAPI CryptGetProvParam( HCRYPTPROV hProv, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags );
此函数获得 CSP 的各种参数。
//-----------------------------------------------------------------
//
HCRYPTPROV hCryptProv;
BYTE pbData[1000];
DWORD cbData;
//-------------------------------------------------------------------
//缺省 CSP 名称
cbData = 1000;
if(CryptGetProvParam(
hCryptProv,
PP_NAME,
pbData,
&cbData,
0))
{
printf("CryptGetProvParam succeeded.\n");
printf("Provider name: %s\n", pbData);
}
else
{
printf("Error reading CSP name. \n");
exit(1);
}
//--------------------------------------------------------------------
cbData = 1000;
if(CryptGetProvParam(
hCryptProv,
PP_CONTAINER,
pbData,
&cbData,
0))
{
printf("CryptGetProvParam succeeded. \n");
printf("Key Container name: %s\n", pbData);
}
else
{
printf("Error reading key container name. \n");
exit(1);
}
创建哈希
BOOL WINAPI CryptCreateHash( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTKEY hKey, DWORD dwFlags, HCRYPTHASH *phHash );
此函数初始化哈希数据流。它创建并返回了一个 CSP 哈希对象的句柄。此句柄由CryptHashData 和 CryptHashSessionKey 来调用
BOOL WINAPI CryptHashData( HCRYPTHASH hHash, BYTE *pbData, DWORD dwDataLen, DWORD dwFlags );
此函数把一段数据加入到指定的哈希对象中去
BOOL WINAPI CryptGetHashParam( HCRYPTHASH hHash, DWORD dwParam, BYTE *pbData, DWORD *pdwDataLen, DWORD dwFlags );
此函数得到指定哈希对象的数据
BOOL WINAPI CryptDestroyHash( HCRYPTHASH hHash );
此函数销毁由 hHash 指定的哈希对象。当一个哈希对象被销毁后,它对程序来说不可用
BOOL WINAPI CryptDeriveKey( HCRYPTPROV hProv, ALG_ID Algid, HCRYPTHASH hBaseData, DWORD dwFlags, HCRYPTKEY *phKey );
此函数从一基本数据值中派生会话密钥。函数保证当 CSP 和算法相同时,从相同基本数据值中产生的密钥是唯一的。
BOOL WINAPI CryptDestroyKey( HCRYPTKEY hKey );
此函数释放密钥句柄
HCRYPTKEY hCryptKey; if (CryptDeriveKey(hCryptProv, m_algid, m_hHash, 0, &hCryptKey)) CryptDestroyKey(hCryptKey);
BOOL WINAPI CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwBufLen
);
此函数用于加密数据。加密数据所需要的算法由 hKey 的密钥指定。
BOOL WINAPI CryptDecrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen
);
此函数对由 CryptEncrypt 加密过的数据进行解密。
//---------------------------------------------------
HCRYPTPROV hCryptProv;
HCRYPTHASH hCryptHash;
HCRYPTKEY hCryptKey;
CryptAcquireContext(
hCryptProv, NULL,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT)
CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hCryptHash
)
static char szHash[]=”PISAHASHDATA”; //原始字符串
DWORD dwLen=strlen(szHash);
CryptHashData(hCryptHash, (BYTE*)szHash, dwLen, 0);
CryptDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0, &hCryptKey);
static char szEntry[]= “PISA2002”;
DWORD dwLenIn = strlen(szEntry);
DWORD dwLenOut=dwLenIn;
CryptEncrypt(hCryptKey, 0, TRUE, 0, (BYTE*)szEntry, &dwLenOut, dwLenIn);
CryptDecrypt(hCryptKey, 0, TRUE, 0,(BYTE*)szEntry,&dwLenOut);
CryptDestroyKey(hCryptKey);
CryptDestroyHash(hCryptHash);
CryptReleaseContext(hCryptProv, NULL);
签名/验证
BOOL WINAPI CryptSignMessage(
PCRYPT_SIGN_MESSAGE_PARA pSignPara,
BOOL fDetachedSignature,
DWORD cToBeSigned,
const BYTE *rgpbToBeSigned[ ],
DWORD rgcbToBeSigned[ ],
BYTE *pbSignedBlob,
DWORD *pcbSignedBlob
);
此函数对指定数据进行哈希,然后对哈希值进行签名,然后对原始消息和签名哈希进行编码。
//--------------------------------------------------------------------
…
#define MY_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define SIGNER_NAME L"Insert_signer_name_here"
#define CERT_STORE_NAME L"MY"
void HandleError(char *s);
void main(void)
{
// 系统证书库句柄
HCERTSTORE hStoreHandle;
//--------------------------------------------------------------------
// 待签名的消息
BYTE* pbMessage =
(BYTE*)"CryptoAPI is a good way to handle security";
//--------------------------------------------------------------------
DWORD cbMessage = strlen((char*) pbMessage)+1;
//--------------------------------------------------------------------
// 证书的上下文
PCCERT_CONTEXT pSignerCert;
CRYPT_SIGN_MESSAGE_PARA SigParams;
DWORD cbSignedMessageBlob;
BYTE *pbSignedMessageBlob;
DWORD cbDecodedMessageBlob;
BYTE *pbDecodedMessageBlob;
CRYPT_VERIFY_MESSAGE_PARA VerifyParams;
//--------------------------------------------------------------------
const BYTE* MessageArray[] = {pbMessage};
DWORD MessageSizeArray[1];
MessageSizeArray[0] = cbMessage;
//--------------------------------------------------------------------
//
printf("Begin processing. \n");
printf(" The message to be signed is\n-> %s.\n",pbMessage);
//--------------------------------------------------------------------
// Open a certificate store.
if ( !( hStoreHandle = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER,
CERT_STORE_NAME)))
{
HandleError("The MY store could not be opened.");
}
//--------------------------------------------------------------------
//
//得到证书的上下文,此证书必须能访问签名者的私钥
if(pSignerCert = CertFindCertificateInStore(
hStoreHandle,
MY_TYPE,
0,
CERT_FIND_SUBJECT_STR,
SIGNER_NAME,
NULL))
{
printf("The signer's certificate was found.\n");
}
else
{
HandleError( "Signer certificate not found.");
}
//--------------------------------------------------------------------
//初始化签名结构
SigParams.cbSize = sizeof(CRYPT_SIGN_MESSAGE_PARA);
SigParams.dwMsgEncodingType = MY_TYPE;
SigParams.pSigningCert = pSignerCert;
SigParams.HashAlgorithm.pszObjId = szOID_RSA_MD5;
SigParams.HashAlgorithm.Parameters.cbData = NULL;
SigParams.cMsgCert = 1;
SigParams.rgpMsgCert = &pSignerCert;
SigParams.cAuthAttr = 0;
SigParams.dwInnerContentType = 0;
SigParams.cMsgCrl = 0;
SigParams.cUnauthAttr = 0;
SigParams.dwFlags = 0;
SigParams.pvHashAuxInfo = NULL;
SigParams.rgAuthAttr = NULL;
//--------------------------------------------------------------------
//
// 首先得到 BLOB 的大小
if(CryptSignMessage(
&SigParams, // Signature parameters
FALSE, // Not detached
1, // Number of messages
MessageArray, // Messages to be signed
MessageSizeArray, // Size of messages
NULL, // Buffer for signed message
&cbSignedMessageBlob)) // Size of buffer
{
printf("The size of the BLOB is %d.\n",cbSignedMessageBlob);
}
else
{
HandleError("Getting signed BLOB size failed");
}
//--------------------------------------------------------------------
// 分配 BLOB 的内存.
if(!(pbSignedMessageBlob =
(BYTE*)malloc(cbSignedMessageBlob)))
{
HandleError("Memory allocation error while signing.");
}
//--------------------------------------------------------------------
//
if(CryptSignMessage(
&SigParams, //
FALSE, //
1, // 消息数量
MessageArray, // 待签名的消息
MessageSizeArray, // 消息大小
pbSignedMessageBlob, // 缓冲区
&cbSignedMessageBlob)) // 缓冲区大小
{
printf("The message was signed successfully. \n");
}
else
{
HandleError("Error getting signed BLOB");
}
//--------------------------------------------------------------------
// 验证签名信息
//--------------------------------------------------------------------
// 初始化 VerifyParams 结构.
VerifyParams.cbSize = sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType = MY_TYPE;
VerifyParams.hCryptProv = 0;
VerifyParams.pfnGetSignerCertificate = NULL;
VerifyParams.pvGetArg = NULL;
//--------------------------------------------------------------------
//
if(CryptVerifyMessageSignature(
&VerifyParams, //.
0, //
pbSignedMessageBlob, //.
cbSignedMessageBlob, //
NULL, //
&cbDecodedMessageBlob, //.
NULL)) // Pointer to signer certificate.
{
printf("%d bytes need for the buffer.\n",cbDecodedMessageBlob);
}
else
{
printf("Verification message failed. \n");
}
//--------------------------------------------------------------------
// 为缓冲区分配内存.
if(!(pbDecodedMessageBlob =
(BYTE*)malloc(cbDecodedMessageBlob)))
{
HandleError("Memory allocation error allocating decode BLOB.");
}
//--------------------------------------------------------------------
//
// 得到缓冲区的大小
if(CryptVerifyMessageSignature(
&VerifyParams, // Verify parameters.
0, // Signer index.
pbSignedMessageBlob, // Pointer to signed BLOB.
cbSignedMessageBlob, // Size of signed BLOB.
pbDecodedMessageBlob, // Buffer for decoded message.
&cbDecodedMessageBlob, // Size of buffer.
NULL)) // Pointer to signer certificate.
{
printf("The verified message is \n-> %s \n",pbDecodedMessageBlob);
}
else
{
printf("Verification message failed. \n");
}
//--------------------------------------------------------------------
//
if(pbSignedMessageBlob)
free(pbSignedMessageBlob);
if(pbDecodedMessageBlob)
free(pbDecodedMessageBlob);
if(pSignerCert)
CertFreeCertificateContext(pSignerCert);
if(CertCloseStore(
hStoreHandle;
CERT_CLOSE_STORE_CHECK_FLAG))
{
printf("The store closed and all certificates are freed. \n");
}
else
{
printf("Store closed after signing -- \n"
"not all certificates, CRLs or CTLs were freed");
}
…
3.证书和证书库函数
证书库函数
一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了
这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了
对库获得、枚举、验证和使用证书库里的信息。
以下就是这些函数:
维护函数
证书函数
证书撤销列表函数
证书信任列表函数
扩展属性函数
4.证书验证函数
使用 CTL 的函数
证书链验证函数
5.消息函数
低级消息函数
简化消息函数
6.辅助函数
数据管理函数
数据转换函数
增强密钥用法函数
密钥标示函数
证书库回调函数
OID 支持函数
PFX 函数
Ⅱ.PKCS#11
通用模型
Cryptoki的通用模型如下图所示。模型从一个或多个必须执行某些密码操作的应用程序开始,以一个或多个密码设备结束(在密码设备上执行某些或全部操作)。一个用户可涉及也可不涉及一个程序。
Cryptoki 为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行。每个对应于一个物理阅读器或另一个设备接口的槽可包含一个令牌。当一台密码设备存在于阅读器中,一个令牌就存在于该槽中。当然,由于Cryptoki提供槽和令牌的逻辑视图,所以可能有其它的物理译码。多个槽可能共享一个阅读器。问题在于一个系统有相当多的槽,应用程序能连接到这些槽的其中任何一个或全部槽的令牌上。
密码设备可以按照某一命令集执行某些密码操作,这些命令通常要经过标准设备驱动程序,例如PCMCIA卡服务程序或槽服务程序。Cryptoki 使每个密码设备看起来逻辑上很象其它设备,而不管什么技术实现的。因此,应用程序不必直接与设备驱动器接口(或甚至不必知道包括那些设备);Cryptoki 隐藏了这些细节。的确,基础设备完全能用软件来实现,(例如,在一个服务器上运行的处理程序),不须专用硬件。
Cryptoki 或许可以作为支持接口功能的库来实现,而应用程序则与该库连接。应用程序可以直接与Cryptoki 连接,或者,Cryptoki 是一个所谓的“共享”库(或动态连接库),在这种情况下,应用程序动态地连接库。用Microsoft Windows和OS/2操作系统可以比较容易地生成数据库,并且在UNIX和DOS中也可相对容易地生成“共享”库。
由于新库可以使用,所以动态方法有许多优点;但从安全的角度来说,也有一些缺点。要特别指出的是,如果库能较容易地被替换,攻击者有可能用恶意制造的假库取而代之,以截取用户的PIN。即使编码签名技术能防止许多动态连接的安全危险,从安全角度来说,一般采用直接连接。总之,应用程序和Cryptoki 库之间的程序设计接口是相同的。
设备的种类和所支持的能力的种类将取决于专用Cryptoki 库。本标准只定义库的接口,不定义库的特征。要特别指出的是,并不是所有的库支持这个接口(因为不是所有的令牌支持所有的机制)中定义的机制(算法)。并且库也许只支持可使用的所有密码设备的一个子集。(当然,可以预料更多更好的设备种类将被开发,以支持多种令牌,而不是单个供应商提供的令牌。)只要开发出应用程序,就会形成Cryptoki 的接口、标准数据库和令牌“轮廓”
Ⅲ.GMT 0016-2012
结构模型
智能密码钥匙应用接口位于智能密码钥匙应用程序与设备之间
一个设备中存在设备认证密钥和多个应用,应用之间相互独立。
应用由管理员PIN、用户PIN、文件和容器组成,可以存在多个文件和多个容器。
每个应用维护各自的与管理员PIN和用户PIN相关的权限状态。
一个应用的逻辑结构如图所示
密码服务
错误代码
Ⅳ.GMT 0018-2012#
二、总结这些API在编程中的使用方式
上文有过总结
三、列出这些API包含的函数,进行分类,并总结它们的异同
分类见上文
Crypto API感觉更加易读,但是分类较为复杂;PKCS#11为拥有密码信息(如加密密钥和证书)和执行密码学函数的单用户设备定义了一个应用程序接口(API)。国密标准较为复杂,既具体地讲解了设备又分析了如何调用其接口进行加密解密,其API更侧重于设备,基本上都是基于设备进行开发。
四、调用不同接口的代码
Ⅰ.Crypto API
HCRYPTPROV hCryptProv;
HCRYPTHASH hCryptHash;
HCRYPTKEY hCryptKey;
CPAcquireContext(
hCryptProv, NULL,
MS_DEF_PROV,
PROV_RSA_FULL,
CRYPT_VERIFYCONTEXT
)//为应用程序创建一个上下文
CPCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hCryptHash
) //为应用程序创建一个上下文
static char szHash[]=”PISAHASHDATA”; //原始字符串
DWORD dwLen=strlen(szHash);
CPHashData(hCryptHash, (BYTE*)szHash, dwLen, 0);//散列输入的数据
CPDeriveKey(hCryptProv, CALG_RC2, hCryptHash, 0, &hCryptKey);//从一个数据散列中生成一个会话密钥,它保证生成的密钥互不相同
static char szEntry[]= “PISA2002”;
DWORD dwLenIn = strlen(szEntry);
DWORD dwLenOut=dwLenIn;
CPEncrypt(hCryptKey, 0, TRUE, 0, (BYTE*)szEntry, &dwLenOut, dwLenIn);//用来加密明文
CPDecrypt(hCryptKey, 0, TRUE, 0,(BYTE*)szEntry,&dwLenOut);//用来解密先前被加密的数据
CPDestroyKey(hCryptKey);//释放一个密钥句柄,释放后,句柄将无效,密钥将无法再被访问
CPDestroyHash(hCryptHash);//删除一个散列对象句柄
CPReleaseContext(hCryptProv, NULL);//释放CPAcquireContext.创建的上下文
Ⅱ.PKCS#11
根据
Token GM3000 Firefox及Thunderbird证书应用.pdf
进行加密
CK_SESSION_HANDLE hSession;
CK_MECHANISM digestMechanism;
CK_ULONG ulStateLen;
CK_BYTE data1[] = {0x01, 0x03, 0x05, 0x07};
CK_BYTE data2[] = {0x02, 0x04, 0x08};
CK_BYTE data3[] = {0x10, 0x0F, 0x0E, 0x0D, 0x0C};
CK_BYTE pDigest[20];
CK_ULONG ulDigestLen;
CK_RV rv;
/* Initialize hash operation */
rv = C_DigestInit(hSession, &digestMechanism);//初始化消息杂凑计算操作,指定计算消息杂凑的算法
assert(rv == CKR_OK);
/* Start hashing */
rv = C_DigestUpdate(hSession, data1, sizeof(data1));//对多个分组的消息进行杂凑计算
assert(rv == CKR_OK);//返回成功或者失败
/* Find out how big the state might be */
rv = C_GetOperationState(hSession, NULL_PTR, &ulStateLen);//获取设备是否存在的状态
assert(rv == CKR_OK);//返回成功或者失败
/* Allocate some memory and then get the state */
pState = (CK_BYTE_PTR) malloc(ulStateLen);
rv = C_GetOperationState(hSession, pState, &ulStateLen);//获取设备是否存在的状态
/* Continue hashing */
rv = C_DigestUpdate(hSession, data2, sizeof(data2));//对多个分组的消息进行杂凑计算
assert(rv == CKR_OK);//返回成功或者失败
/* Restore state. No key handles needed */
rv = C_SetOperationState(hSession, pState, ulStateLen, 0, 0);
assert(rv == CKR_OK);//返回成功或者失败
/* Continue hashing from where we saved state */
rv = C_DigestUpdate(hSession, data3, sizeof(data3));//对多个分组的消息进行杂凑计算
assert(rv == CKR_OK);//返回成功或者失败
/* Conclude hashing operation */
ulDigestLen = sizeof(pDigest);
rv = C_DigestFinal(hSession, pDigest, &ulDigestLen);
if (rv == CKR_OK) {
/* pDigest[] now contains the hash of 0x01030507100F0E0D0C */
}//返回成功或者失败
Ⅲ.SKF接口
CK_FLAGS flags = 0;
CK_Dev_ID DevID;
CK_Dev_INFO DevInfo;
/* Block and wait for a slot event */
rv = SKF_WaitForSlotEvent(flags, &slotID, NULL_PTR);//等待设备的插拔事件
assert(rv == CKR_OK);//返回成功或者失败
/* See what’s up with that slot */
rv = SKF_GetDevInfo(slotID, &slotInfo);//获取设备的一些特征信息
assert(rv == CKR_OK);//返回成功或者失败