实验一-密码引擎-加密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);//返回成功或者失败
posted @ 2022-04-21 17:18  李业达  阅读(296)  评论(0编辑  收藏  举报