实验一-密码引擎-加密API研究

任务详情

密码引擎API的主要标准和规范包括:
1 微软的Crypto API
2 RAS公司的PKCS#11标准
3 中国商用密码标准:GMT 0016-2012 智能密码钥匙密码应用接口规范,GMT 0018-2012密码设备应用接口规范等

研究以上API接口,总结他们的异同,并以龙脉GM3000Key为例,写出调用不同接口的代码,提交博客链接和代码链接。
内容:
0 查找各种标准的原始文档,研究学习(至少包含Crypto API,PKCS#11,GMT 0016-2012,GMT 0018-2012)(5分)
1 总结这些API在编程中的使用方式(5分)
2 列出这些API包含的函数,进行分类,并总结它们的异同(10分)
3 以龙脉GM3000Key为例,写出调用不同接口的代码(Crypto API,PKCS#11,SKF接口),把运行截图加入博客,并提供代码链接(10分)

查找各种标准的原始文档,研究学习(至少包含Crypto API,PKCS#11,GMT 0016-2012,GMT 0018-2012)

Ⅰ.微软的Crypto API

1.CryptoAPI系统架构由五个主要功能区域组成

基本密码功能
证书编码/解码功能
证书存储功能
简化的消息功能
低级消息功能

1.1 基本密码功能

①服务提供者函数
应用程序使用服务提供者函数来连接和断开一个 CSP。下图为主要的 API:

②密钥的产生和交换函数
密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。下图为主要的一些函数:


③编码/解码函数
有一些编码/解码函数,他们可以用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。

④数据加密/解密函数
这些函数支持数据的加密/解密操作。CryptEncrypt 和 CryptDecrypt 要求在被调用前指定一个密钥。
这个密钥可以由 CryptGenKey、CryptDeriveKey 或 CryptImportKey 产生。创建密钥时要指定加密算法。CryptSetKeyParam 函数可以指定额外的加密参数。

⑤哈希和数字签名函数
完成计算哈希、创建和校验数字签名

函数详解
获得 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 );

此函数释放密钥句柄

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 加密过的数据进行解密。

签名/验证

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");
}
…

证书和证书库函数

一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了
这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了
对库获得、枚举、验证和使用证书库里的信息。
以下就是这些函数:

维护函数

证书函数

证书撤销列表函数

证书信任列表函数

扩展属性函数

证书验证函数

使用 CTL 的函数

证书链验证函数

消息函数

低级消息函数

简化消息函数

辅助函数

数据管理函数

数据转换函数

增强密钥用法函数

密钥标示函数

证书库回调函数

OID 支持函数

PFX 函数

Ⅱ.PKCS#11

PKCS#11(简称P11)就是针对密码设备的接口指令标准。P11模型中重要的概念之一是slot,也称为槽。一个slot为一个密码设备对象。某个打开的slot会话称之为session。Session之间存在不同的验证权限。而同一个slot的不同的session之间存在操作的互相影响性,同时在某些状况下,权限会发生同步。另外一个重要的概念是对象。P11中支持几种重要的对象,如公钥、私钥、对称密钥,数据对象等。

CSP接口标准:CSP接口标准为微软所颁发,在windows操作系统上通行。CSP中重要的概念是容器(container)。一个容器中具有一对公私钥。而证书却是这一对密钥的附加属性了。

通用模型

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

设备管理:
打开设备:SDEOpenDerice
关闭设备:SDF_ Cias eDevice
创建会话:SDF_ OpenSesion
关闭会话: SDF _CloseSession
获取设备信息:SDF _GetDeviceInfo
产生随机数:SDF_enerateRandom
获取私钥使用权限:SDF _GetPrivateKeyAccessRight
释放私钥使用权限:SDF_ReleasePrivateKeyAccessRight

密钥管理:
导出 RSA签名公钥:SDF_ ExportSignPublicKey_ RSA
导出 RSA加密公钥:SDF_ ExportEncPublicKey_ RSA
产生 RSA非对称密钥对并输出:SDF_ GenerateKeyPair_RSA
生成 会话密钥并用内部RSA公钥加密输出:SDF_GenerateKeyWithIPK ,RSA
生成会 话密钥并用外部RSA公钥加密输出:SDF_GenerateKeyWithEPK_ RSA
导人会 话密钥并用内部RSA私钥解密:SDF_ ImportKeyWithISK_ RSA
基于 RSA算法的数字信封转换:SDF_ ExchangeDigitEnvelopeBaseOnRSA
导出 ECC签名公钥:SDF_ ExportSignPublicKey_ ECC
导出 ECC加密公钥:SDF_ ExportEncPublicKey_ ECC
产生ECC非对称密钥对并输出:SDF_GenerateKeyPair_ ECC
生成会 话密钥并用内部:ECC公钥加密输出:SDF_ GenerateKeyWithIPK_ ECC
生成会话密 钥并用外部:ECC公钥加密输出: SDF_ GenerateKeyWithEPK_ ECC
导人会话密钥并用内部:ECC私钥解密:SDF_ ImportKeyWithISK_ ECC
生成密钥协商参数并输出:SDF_GenerateAgreementDataWithECC
计算会话密钥:SDF_GenerateKeyWithECC
产生协商数据并计算会话密钥:SDF_ GenerateAgreementDataAndKeyWithECC
基于ECC算法的数字信封转换:SDF_ ExchangeDigitEnvelopeBaseOnECC
生成会话密钥并用密钥加密密钥加密输出:SDF_GenerateKeyWithKEK
导入会话密钥并用密钥加密密钥解密:SDF_ ImportKeyWithKEK
销毁会话密钥:SDF_DestroyKey

非对称算法运算类函数:
外部公钥RSA运算:SDF_ ExternalPublicKeyOperation_ RSA
内部公钥RSA运算:SDF_ InternalPublicKeyOperation_ RSA
内部私钥RSA运算:SDF_InternalPrivateKeyOperation_ RSA
外部密钥ECC验证:SDF_ ExternalVerify_ECC
内部密钥ECC签名:SDF_ nternalSign_ ECC
内部密钥ECC验证:SDF_ InternalVerify_ ECC
外部密钥ECC加密:SDF_ ExternalEncrypt_ECC

对称算法运算类函数
对称加密:SDF_Encrypt
对称解密:SDF_Decrypt
计算MAC:SDF_CalculateMAC

杂凑运算类函数
杂凑运算初始化:SDF_HashInit
多包杂凑运算:SDF_HashUpdate
杂凑运算结束:SDF_HashFinal

用户文件操作类函数:
创建文件:SDF_CreatFile
读取文件:SDF_ReadFile
写文件:SDF_WriteFile
删除文件:SDF_DeleteFile

总结异同:

这三类标准中都包含加解密的函数,并且自成体系。
具体函数名称有些许不同,但大同小异。
还有GM涉及到了设备的管理,所以有设备管理接口还有访问控制函数。

以龙脉GM3000Key为例,写出调用不同接口的代码(Crypto API,PKCS#11,SKF接口)

posted @ 2022-05-12 17:48  1219雷天荣  阅读(178)  评论(0编辑  收藏  举报