是TC

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

一、任务详情

密码引擎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分)

 

二、学习过程

0 .查找各种标准的原始文档,研究学习

(1)CryptoAPI

Crypto 接口提供了当前上下文中可用的基本的加密功能。它允许访问一个密码学安全的随机数生成器和密码学原语(cryptographic primitive)。Web Crypto API 可以通过 crypto 属性(一个 Crypto 对象)来访问。有些浏览器实现了叫作 Crypto 的接口,但是它缺乏良好的定义,或在密码学上是不健全的。为了避免混乱,这个接口的方法和属性已经被实现 Web Crypto API 的浏览器所移除,并且所有的 Web Crypto API 方法都可以在新的接口中使用:SubtleCryptoCrypto.subtle 属性可以获取到一个实现了新接口的对象。

(2)PKCS#11

PKCS#11是公钥加密标准Public-Key Cryptography Standards中的一份子,由RSA实验室发布。PKCS#11标准定义了与密码令牌的独立于平台的API,API本身命名为Cryptoki,这个API已经发展成为一个通用的加密令牌的抽象层,主要是应用于智能卡和HSM。PKCS#11为使用加密Token的应用程序提供统一的编程接口,独立于设备,屏蔽加密设备的复杂性,应用程序可以方便地更换设备。

(3)GM/T 0016-2012 智能密码钥匙密码应用接口规范

 

(4)GM/T 0018-2012 密码设备应用接口规范

 

1.总结这些API在编程中的使用方式

(1)CryptoAPI

int main()
{
    HCRYPTPROV hCryptProv = NULL;
    HCRYPTKEY hKey = NULL;
    BYTE* pDataToEncrypt = (BYTE*)"Hello, World!";
    BYTE* pEncryptedData = NULL;
    DWORD encryptedDataLen = 0;
    BYTE* pDecryptedData = NULL;
    DWORD decryptedDataLen = 0;
 
    //Get the handler of Crypt Service Provider(CSP)
    if (!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_AES, 0)) {
        printf("CryptAcquireContext failed: %d\n", GetLastError());
        return 1;
    }
 
    //Generate AES key
    if (!CryptGenKey(hCryptProv, CALG_AES_256, 0, &hKey)) {
        printf("CryptGenKey failed: %d\n", GetLastError());
        CryptReleaseContext(hCryptProv, 0);
        return 1;
    }
 
    //Encrypt data
    if (EncryptData(hKey, pDataToEncrypt, strlen((char*)pDataToEncrypt) + 1, &pEncryptedData, &encryptedDataLen)) {
        printf("Data encrypted successfully.\n");
        printf("Encrypted data: ");
        for (DWORD i = 0; i < encryptedDataLen; i++)
        {
            printf("%02X", pEncryptedData[i]);
        }
        printf("\n");
 
        //Decrypt data
        if (DecryptData(hKey, pEncryptedData, encryptedDataLen, &pDecryptedData, &decryptedDataLen)) {
            printf("Decrypted data: %s\n", pDecryptedData);
            free(pDecryptedData);
        }
 
        free(pEncryptedData);
    }
 
    //Clean resources
    CryptDestroyKey(hKey);
    CryptReleaseContext(hCryptProv, 0);
 
    return 0;
}

阅读这段 Windows Console Application main 函数,可以按照几个注释分步学习:(1)获取加解密上下文、(2)产生目标密钥、(3)加密明文、(4)解码密文和最后(5)释放相关资源。

BOOL CryptAcquireContext(
  HCRYPTPROV *phProv,         // 用于存储 CSP 句柄的指针
  LPCTSTR    pszContainer,    // CSP 容器名称(可选)
  LPCTSTR    pszProvider,     // CSP 提供程序名称(可选)
  DWORD      dwProvType,      // CSP 提供程序类型
  DWORD      dwFlags          // 标志,用于控制 CSP 的行为
);

CryptAcquireContext 函数是 Windows Cryptographic API(CryptoAPI)中的一个函数,用于获取或创建一个加密服务提供程序(CSP)的上下文句柄。CSP是用于执行各种加密和安全操作的组件,例如生成密钥、加密和解密数据等。

BOOL CryptGenKey(
  HCRYPTPROV hProv,
  ALG_ID    Algid,
  DWORD     dwFlags,
  HCRYPTKEY *phKey
);

CryptGenKey 是 Windows Cryptographic API(CryptoAPI)中的一个函数,用于生成对称密钥非对称密钥。这个函数可以用于生成加密和解密数据的密钥,以及用于数字签名和验证的密钥对

BOOL CryptEncrypt(
  HCRYPTKEY hKey,
  HCRYPTHASH hHash,
  BOOL      Final,
  DWORD     dwFlags,
  BYTE      *pbData,
  DWORD     *pdwDataLen,
  DWORD     dwBufLen
);

 CryptEncrypt 是 Windows Cryptographic API(CryptoAPI)中用于对数据进行加密的函数。它允许使用已经生成的密钥来加密数据。

BOOL CryptDecrypt(
  HCRYPTKEY hKey,
  HCRYPTHASH hHash,
  BOOL      Final,
  DWORD     dwFlags,
  BYTE      *pbData,
  DWORD     *pdwDataLen
);

CryptDecrypt 函数是 Windows 操作系统提供的用于解密数据的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 临时**容器的访问。

phProv
[out] CSP 句柄指针
pszContainer
[in] *** 容 器 名 称 , 指 向 *** 容 器 的 字 符 串 指 针 。 如 果 dwFlags 为
CRYPT_VERIFYCONTEXT,pszContainer 必须为 NULL。
pszProvider
[in]指向 CSP 名称的字符串指针。如果为 NULL,就使用却省的 CSP。
dwProvType
[in]CSP 类型。
dwFlags
[in]标志。

BOOL WINAPI CryptReleaseContext(
HCRYPTPROV hProv,
DWORD dwFlags
);

hProv
[in]由 CryptAcquireContext 获得的 CSP 句柄。
dwFlags
[in]保留。必须为 0。

此函数释放 CSP 的句柄。对于每一次调用,CSP 的引用计数都减 1。当引用计数为 0时,CSP 上下文就会被系统释放变成无效句柄,以后针对此 CSP 句柄的函数不再可用。此函数并不销毁**容器或**对。

HCRYPTPROV hCryptProv;
if(CryptAcquireContext(
    hCryptProv, NULL,
    MS_DEF_PROV,
    PROV_RSA_FULL,
    CRYPT_VERIFYCONTEXT))
    CryptReleaseContext(hCryptProv, NULL);
枚举 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
);

 

此函数得到指定哈希对象的数据

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

 

 

(2)PKCS#11

简介

一套称为公共**加密标准(Public-Key Cryptography Standards )的规范,既PKCS

适用范围

本标准为那些保存密码信息,执行密码函数的设备确定一种程序设计接口(API),该接口称做Cryptoki。Cryptoki发“Crypto-Key”音,是cryptographic token interface (密码令牌接口)的缩写,它遵循一种基于对象的简单方法,提出技术独立性(各种各样的设备)和资源共享(多个应用程序访问多个设备)的目标,把设备的一种通用的逻辑视图,即密码令牌,提供给应用程序。

通用模型

Cryptoki的通用模型如下图所示。模型从一个或多个必须执行某些密码操作的应用程序开始,以一个或多个密码设备结束(在密码设备上执行某些或全部操作)。一个用户可涉及也可不涉及一个程序。

image-20210424173115815

Cryptoki 为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行。每个对应于一个物理阅读器或另一个设备接口的槽可包含一个令牌。当一台密码设备存在于阅读器中,一个令牌就存在于该槽中。当然,由于Cryptoki提供槽和令牌的逻辑视图,所以可能有其它的物理译码。多个槽可能共享一个阅读器。问题在于一个系统有相当多的槽,应用程序能连接到这些槽的其中任何一个或全部槽的令牌上。

密码设备可以按照某一命令集执行某些密码操作,这些命令通常要经过标准设备驱动程序,例如PCMCIA卡服务程序或槽服务程序。Cryptoki 使每个密码设备看起来逻辑上很象其它设备,而不管什么技术实现的。因此,应用程序不必直接与设备驱动器接口(或甚至不必知道包括那些设备);Cryptoki 隐藏了这些细节。的确,基础设备完全能用软件来实现,(例如,在一个服务器上运行的处理程序),不须专用硬件。

Cryptoki 或许可以作为支持接口功能的库来实现,而应用程序则与该库连接。应用程序可以直接与Cryptoki 连接,或者,Cryptoki 是一个所谓的“共享”库(或动态连接库),在这种情况下,应用程序动态地连接库。用Microsoft Windows和OS/2操作系统可以比较容易地生成数据库,并且在UNIX和DOS中也可相对容易地生成“共享”库。

由于新库可以使用,所以动态方法有许多优点;但从安全的角度来说,也有一些缺点。要特别指出的是,如果库能较容易地被替换,攻击者有可能用恶意制造的假库取而代之,以截取用户的PIN。即使编码签名技术能防止许多动态连接的安全危险,从安全角度来说,一般采用直接连接。总之,应用程序和Cryptoki 库之间的程序设计接口是相同的。

设备的种类和所支持的能力的种类将取决于专用Cryptoki 库。本标准只定义库的接口,不定义库的特征。要特别指出的是,并不是所有的库支持这个接口(因为不是所有的令牌支持所有的机制)中定义的机制(算法)。并且库也许只支持可使用的所有密码设备的一个子集。(当然,可以预料更多更好的设备种类将被开发,以支持多种令牌,而不是单个供应商提供的令牌。)只要开发出应用程序,就会形成Cryptoki 的接口、标准数据库和令牌“轮廓”。

令牌的逻辑视图

Cryptoki的令牌逻辑视图是一个能存储对象和能执行密码函数的设备。Cryptoki定义如下三个对象:数据、证书和**。数据对象由应用程序定义。一个证书对象存储一个证书。一个**对象存储一个密码**。**可以是一个公共**、一个私钥或是一个保***,每个种类的**在专用机制中使用其的辅助型。令牌的这种逻辑视图如下图所示:

image-20210424173353784

函数概述

种类函数描述
通用l C_Initialize 初始化 Cryptoki
目的函数 C_Finalize 整理各种适合 Cryptoki的资源
  C_GetInfo 获得关于Cryptoki的通用信息
  C_GetFunctionList 获得Cryptoki 库函数的进入点
槽和令牌 C_GetSlotList 获得系统中槽的名单
管理 C_GetSlotInfo 获得关于特殊槽的信息
函数 C_GetTokenInfo 获得关于特殊令牌的信息
  C_WaitForSlotEvent 等待槽事件(令牌插入,转移等) 的发生
  C_GetMechanismList 获得由令牌支持的机制的名单
  C_GetMechanismInfo 获得关于特殊机制的信息
  C_InitToken 初始化一个令牌
  C_InitPIN 初始化普通用户的 PIN
  C_SetPIN 改变现在用户的PIN
会话管理函数 C_OpenSession 打开一个应用程序和特殊令牌之间的连接或安装一个应用程序呼叫返回令牌插入
  C_CloseSession 关闭一个会话
  C_CloseAllSessions 用令牌关闭所有的会话
  C_GetSessionInfo 获得关于会话的信息
  C_GetOperationState 获得会话的加密操作状态
  C_SetOperationState 设置会话的加密操作状态
  C_Login 注册一个令牌
  C_Logout 从一个令牌注销
对象管理函数 C_CreateObject 建立一个对象
  C_CopyObject 建立一个对象的拷贝
  C_DestroyObject 销毁一个对象
  C_GetObjectSize 获取字节中一个对象的大小
  C_GetAttributeValue 获取一个对象的属性值
  C_SetAttributeValue 改变一个对象的属性值
  C_FindObjectsInit 初始化一个对象的搜索操作
  C_FindObjects 继续一个对象搜索操作
  C_FindObjectsFinal 完成一个对象搜索操作
加密 C_EncryptInit 初始化一个加密操作
函数 C_Encrypt 加密单部分数据
  C_EncryptUpdate 继续一个多部分加密操作
  C_EncryptFinal 完成一个多部分加密操作
解密 C_DecryptInit 初始化一个解密操作
函数 C_Decrypt 解密单部分加密数据
  C_DecryptUpdate 继续一个多部分解密操作
  C_DecryptFinal 完成一个多部分解密操作
消息 C_DigestInit 初始化一个消息摘要操作
解密 C_Digest 摘要单部分数据
函数 C_DigestUpdate 继续一个多部分摘要操作
  C_DigestKey 摘要一个**
  C_DigestFinal 完成一个多部分摘要操作
签名 C_SignInit 初始化一个签名操作
和MACing C_Sign 签名单部分数据
函数 C_SignUpdate 继续一个多部分签名操作
  C_SignFinal 完成一个多部分签名操作
  C_SignRecoverInit 初始化一个签名操作,在操作中数据能从签名中恢复
  C_SignRecover 签名单部分数据,在操作中数据能从签名中恢复
鉴定函数 C_VerifyInit 初始化一个鉴定操作
签名 C_Verify 在单部分数据上鉴定一个签名
和 MACs C_VerifyUpdate 继续一个多部分鉴定操作
  C_VerifyFinal 完成一个多部分鉴定操作
  C_VerifyRecoverInit 初始化一个鉴定操作,在操作中数据能从签名中恢复
  C_VerifyRecover 在单部分数据上鉴定一个签名,在操作中数据能从签名中恢复
双效加密 C_DigestEncryptUpdate 继续类似的多部分摘要和加密操作
函数 C_DecryptDigestUpdate 继续类似的多部分解密和摘要操作
  C_SignEncryptUpdate 继续类似的多部分签名和加密操作
  C_DecryptVerifyUpdate 继续类似的多部分解密和鉴定操作
** C_GenerateKey 产生一个保***
管理 C_GenerateKeyPair 产生一个公共/私钥对
函数 C_WrapKey 加密一个**
  C_UnwrapKey 解密一个**
  C_DeriveKey 从基础**派生一个**
随机数字生成 C_SeedRandom 把一个附加种子材料加入随机数字生成器
函数 C_GenerateRandom 生成随机数据
并行功能管理函数 C_GetFunctionStatus 经常返回 CKR_FUNCTION_NOT_PARALLEL的遗产函数
  C_CancelFunction 经常返回 CKR_FUNCTION_NOT_PARALLEL的遗产函数
呼叫返回函数   Cryptoki中应用程序提供的处理通知的函数
 

令牌信息标志

位标志表征码含义
CKF_RNG 0x00000001 如果令牌有自己的随机数发生器,为真
CKF_WRITE_PROTECTED 0x00000002 如果令牌是写保护的,为真
CKF_LOGIN_REQUIRED 0x00000004 如果用户必须注册才能执行密码函数,为真
CKF_USER_PIN_INITIALIZED 0x00000008 如果普通用户的PIN已经被初始化,为真
CKF_RESTORE_KEY_NOT_NEEDED 0x00000020 如果成功保存一个会话的密码操作状态总是包含恢复该会话的状态所需要的所有**,则为真
CKF_CLOCK_ON_TOKEN 0x00000040 如果令牌有自己的硬件时钟,则为真
CKF_PROTECTED_AUTHENTICATION_PATH 0x00000100 如果令牌有一个受保护的鉴别路径,用户借此不用把PIN传过Cryptoki 库就能够注册到令牌中,为真
CKF_DUAL_CRYPTO_OPERATIONS 0x00000200 如果与令牌的单一会话能够执行对偶密码操作,为真(参见章节 11.13)
CKF_TOKEN_INITIALIZED 0x00000400 如果使用C_InitializeToken 或本标准范围外的等效机制已经初始化该令牌,则为真。当该标志被设置并使令牌被重新初始化,则调用C_InitializeToken 。
CKF_SECONDARY_AUTHENTICATION 0x00000800 如果令牌支持私钥对象二次鉴别,则为真。(反对,新的执行程序不要把这个机制设置为真)
CKF_USER_PIN_COUNT_LOW 0x00010000 如果由于上一次成功的鉴别,一个错误的用户注册的PIN进入至少一次,则为真
CKF_USER_PIN_FINAL_TRY 0x00020000 如果提供一个错误的用户的PIN被锁住,则为真。
CKF_USER_PIN_LOCKED 0x00040000 如果用户的PIN被锁住,则为真。用户注册该令牌是不可能的。
CKF_USER_PIN_TO_BE_CHANGED 0x00080000 如果用户的PIN值通过令牌初始化或加工被设置成错误值,或PIN由卡终止。
CKF_SO_PIN_COUNT_LOW 0x00100000 从最后一次成功的认证,SO登录的PIN至少进入一次,则为真
CKF_SO_PIN_FINAL_TRY 0x00200000 如果提供的错误的SO PIN将被锁住,则为真
CKF_SO_PIN_LOCKED 0x00400000 如果SO PIN被锁住,则为真。用户登录令牌是不可能的。
CKF_SO_PIN_TO_BE_CHANGED 0x00800000 如果SO PIN值通过令牌初始化或加工被设置成错误的值,或PIN由卡终止,则为真。
typedef struct CK_SESSION_INFO {
      CK_SLOT_ID slotID;
      CK_STATE state;
      CK_FLAGS flags;c
      CK_ULONG ulDeviceError;
      CK_SESSION_INFO;
}

 

关于会话的信息定义

​ slotID 与令牌接口的那个槽的ID

​ state 会话的状态

​ flags 定义会话的类型的位标志;其定义在后面

​ ulDeviceError 由密码设备定义的错误编码。用于Cryptoki不能覆盖的错误

位标志表征码含义
CKF_RW_SESSION 0x00000002 如果会话为读/写,则为真;如果会话是只读,则为假
CKF_SERIAL_SESSION 0x00000004 该标志提供向后兼容,并始终设置为真

 

机制

image-20210424190755776

image-20210424190812958

image-20210424190828067

(3)GMT 0016-2012

结构模型

智能密码钥匙应用接口位于智能密码钥匙应用程序与设备之间

image-20210424201254384

一个设备中存在设备认证**和多个应用,应用之间相互独立。

image-20210424201637053

应用由管理员PIN、用户PIN、文件和容器组成,可以存在多个文件和多个容器。

每个应用维护各自的与管理员PIN和用户PIN相关的权限状态。

一个应用的逻辑结构如图所示

image-20210424201920046

密码服务

image-20210424212946635

image-20210424212955622

 

(4)GMT 0018-2012

image-20210424213426456

image-20210424213438228

image-20210424213452940

image-20210424213504892

image-20210424213515056

image-20210424213531523

image-20210424213620484

image-20210424213632431

image-20210424213643387

image-20210424213730680

image-20210424213740443

image-20210424213751303

image-20210424213801880

2.列出这些API包含的函数,进行分类,并总结它们的异同

(1)Crypto API

主要函数:

a) 主函数
void main(void)
b) 加密文件
BOOL EncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);
c) 解密文件
BOOL DecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);
d) 签名文件
BOOL SignFile (PCHAR szSource, PCHAR szDestination);
e) 验证签名
BOOL VerifyFile (PCHAR szSource, PCHAR szDestination);
f) 错误处理
void HandleError(char *s);

加密文件:

a) 打开源文件
hSource = fopen(szSource,"rb")
b) 取得密钥容器(CSP)句柄
CryptAcquireContext(&hCryptProv,NULL,NULL,PROV_RSA_FULL,0)
c) 根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件加密)
//创建一个Hash对象
CryptCreateHash(hCryptProv,CALG_MD5, 0, 0, &hHash)
//用用户输入的密码产生一个散列
CryptHashData(hHash, (BYTE *)szPassword, strlen(szPassword), 0)
//通过散列生成一个会话密钥
CryptDeriveKey(hCryptProv, ENCRYPT_ALGORITHM,hHash, KEYLENGTH, &hKey))
//销毁Hash对象
CryptDestroyHash(hHash);
注: 会话密钥即对称密钥,用于对原文件进行加密;非对称密钥由于效率非常低,所以一般不用于对数据直接加密,而是对会话密钥进行加密,然后把它传送给对方。对 方通过非对称密钥解密获得这个会话密钥,然后再对数据文件进行解密。可以看出,一个会话密钥的生存期可以限制在这次通信中,即每次通信都用不同的会话密钥 加密,而非对称密钥则必须是长期使用的。在此例中,加解密过程中没有使用到非对称 RSA密钥对,而只在数字签名及验证使用它。
d) 加密数据文件
CryptEncrypt(
hKey, //密钥
0, //如果数据同时进行散列和加密,这里传入一个散列对象
feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输
//入FALSE这里通过判断是否到文件尾来决定是否为最后一块
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount, //输入被加密数据实际长度,输出加密后数据长度
dwBufferLen) //pbBuffer的大小
注:查看完整代码时可以发现这是一个循环加密的过程,pbBuffer循环读入待加密文件的固定长度的内存块;当然你也可以将pbBuffer设得很大,一次读入整个文件,但那样浪费内存空间,而且影响扩展性(存在缓冲区溢出的可能)。
e) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);

解密文件:

a) 打开加密文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件解密)(同上)
注: 这里要求用户输入的密码与加密时输入的密码相同。在实际应用中,这个所谓用户输入的“密码”其实只是一个产生密钥的种子,一旦产生完会话密钥,则用户完全 可以忘记当初输入的“密码”,接收方可以使用传过来的密钥直接对加密文件进行解密,而不用再重复一次“生成密钥”的过程。
d) 解密数据文件
CryptDecrypt(
hKey, //密钥
0, //如果数据同时进行散列和加密,这里传入一个散列对象
feof(hSource), //如果是最后一个被加密的块,输入TRUE.如果不是输.
//入FALSE这里通过判断是否到文件尾来决定是否为最后一块。
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount)) //输入被加密数据实际长度,输出加密后数据长度
e) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。

签名文件:

a) 打开源文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 取得签名用的密钥句柄(非对称RSA密钥)
CryptGetUserKey(
hCryptProv, // 我们已经得到的CSP句柄
AT_SIGNATURE, // 这里想得到signature key pair
&hKey)) // 返回密钥句柄
d) 导出签名用密钥对的公钥,保存在pbKeyBlob中
CryptExportKey(hKey, NULL,PUBLICKEYBLOB, 0, pbKeyBlob,&dwBlobLen)
e) 计算数据文件的Hash值,保存在Hash对象hHash中
//生成一个空的Hash对象
CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash)
//计算数据文件的Hash值,保存在Hash对象中
CryptHashData(hHash,pbBuffer,dwCount,0)
f) 对数据文件的Hash值进行签名,数字签名保存在pbSignature中
CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &dwSigLen)
g) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。

签名文件:

a) 打开源文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 取得签名用的密钥句柄(非对称RSA密钥)
CryptGetUserKey(
hCryptProv, // 我们已经得到的CSP句柄
AT_SIGNATURE, // 这里想得到signature key pair
&hKey)) // 返回密钥句柄
d) 导出签名用密钥对的公钥,保存在pbKeyBlob中
CryptExportKey(hKey, NULL,PUBLICKEYBLOB, 0, pbKeyBlob,&dwBlobLen)
e) 计算数据文件的Hash值,保存在Hash对象hHash中
//生成一个空的Hash对象
CryptCreateHash(hCryptProv,CALG_MD5,0,0,&hHash)
//计算数据文件的Hash值,保存在Hash对象中
CryptHashData(hHash,pbBuffer,dwCount,0)
f) 对数据文件的Hash值进行签名,数字签名保存在pbSignature中
CryptSignHash(hHash, AT_SIGNATURE, NULL, 0, pbSignature, &dwSigLen)
g) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)

验证签名:

a) 打开文件(同上)
b) 取得密钥容器(CSP)句柄(同上)
c) 导入 pbKeyBlob 公钥
CryptImportKey(hCryptProv, pbKeyBlob, dwBlobLen, 0, 0, &hPubKey)
注:必须是与签名时所用的私钥配对的公钥,在此例中,这个公钥在生成数字签名时已经导出到pbKeyBlob中。
d) 计算数据文件的Hash值,保存在Hash对象hHash中。(同上)
e) 验证数字签名
CryptVerifySignature(hHash, pbSignature, dwSigLen,hPubKey,NULL, 0)
f) 清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)

(2)PKCS#11

根据机制标记,可以分为几类:
CKF_ENCRYPT:加密类
CKF_DECRYPT:解密类
CKF_DIGEST:摘要类
CKF_SIGN:签名类
CKF_SIGN_RECOVER:可恢复签名类
CKF_VERIFY:验证类
CKF_VERIFY_RECOVER:可恢复验证类
CKF_GENERATE:密钥产生
CKF_GENERATE_KEY_PAIR:密钥对产生
CKF_WRAP:密钥封装
CKF_UNWRAP:密钥解封
CKF_DERIVE:密钥派生

(3)GMT 0016-2012











(4)GMT 0018-2012

下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅注日期的版本适用于本文件,凡是不注日期的引用文件,其最新版本(包括所有的修单)适用于本文件,
GM/T0006 密码应用标识规范 和GM/T0009 SM2密码算法使用规范













3.以龙脉GM3000Key为例,写出调用不同接口的代码(Crypto API,PKCS#11,SKF接口),把运行截图加入博客,并提供代码链接

 

 

 

 

 

 

 代码链接:TcTeenager/GM3000key (gitee.com)

 

posted on 2024-04-08 11:21  是TC  阅读(47)  评论(0编辑  收藏  举报