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

目录

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

API:应用程序接口(API:Application Program Interface)是一组定义、程序及协议的集合,通过 API 接口实现计算机软件之间的相互通信。API 的一个主要功能是提供通用功能集。程序员通过使用 API 函数开发应用程序,从而可以避免编写无用程序,以减轻编程任务。

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

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

(1)Crypto API:

Crypto API作为一部分 Microsoft Windows 提供的应用程序编程接口(API) ,提供了一组允许应用程序在对用户的敏感私钥数据提供保护时,以灵活的方式对数据进行加密或数字签名的函数,而实际的加密操作是由称为加密服务提供程序 (CSP) 的独立模块执行。

imgimage-20210424224038340
CryptoAPI是一组函数,为了完成数学计算,必须具有密码服务提供者模块(CSP)。Microsoft通过捆绑RSA Base Provider在操作系统级提供一个CSP,使用RSA公司的公钥加密算法,更多的CSP可以根据需要增加到应用中。事实上,CSP有可能与特殊硬件设备(如智能卡)一起来进行数据加密。CryptoAPI接口允许简单的函数调用来加密数据,交换公钥,散列一个消息来建立摘要以及生成数字签名。它还提供高级的管理操作,如从一组可能的CSP中使用一个CSP。此外,CryptoAPI还为许多高级安全性服务提供了基础,包括用于电子商务的SET,用于加密客户机/服务器消息的PCT,用于在各个平台之间来回传递机密数据和密钥的PFX,代码签名等等。

(2)PKCS#11:

在密码系统中,PKCS#11是公钥加密标准(PKCS, Public-Key Cryptography Standards)中的一份子 ,由RSA实验室(RSA Laboratories)发布[1],它为加密令牌定义了一组平台无关的API ,如硬件安全模块和智能卡。

PKCS#11标准定义了与密码令牌(如硬件安全模块(HSM)和智能卡)的独立于平台的API,并将API本身命名为“Cryptoki”(来自“加密令牌接口”,发音为“crypto-key” - 但是“PKCS#11”通常用于指代API以及定义它的标准)。 API定义了最常用的加密对像类型(RSA密钥,X.509证书,DES / 三重DES密钥等)以及使用,创建/生成,修改和删除这些对象所需的所有功能。

PKCS# 11标准定义了与密码令牌(如硬件安全模块(HSM)和智能卡)的独立于平台的API,API定义了最常用的加密对像类型(RSA密钥,X.509证书,DES / 三重DES密钥等)以及使用,创建/生成,修改和删除这些对象所需的所有功能。

PKCS#11创建和支持下列对象:

对象 说明
CKO_DATA 应用程序定义的对象。对象的数据结构可由应用程序任意定义,但是数据意义的解释由应用程序负责。
CKO_SECRET_KEY 对称加密算法使用的密钥。
CKO_CERTIFICATE X.509
CKO_PUBLIC_KEY RSA
CKO_PRIVATE_KEY RSA
CKO_MECHANISM 算法对象

接口指令:

接口类型 函数名称 描述
通用接口函数 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 初始化一个签名操作
C_Sign 签名单部分数据
C_SignUpdate 继续一个多部分签名操作
C_SignFinal 完成一个多部分签名操作
C_SignRecoverInit 初始化一个签名操作,在操作中数据能从签名中恢复
C_SignRecover 签名单部分数据,在操作中数据能从签名中恢复
签名鉴定消息鉴别函数 C_VerifyInit 初始化一个鉴定操作
C_Verify 在单部分数据上鉴定一个签名
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

CSP接口标准:

接口类型 函数名称 描述
CSP连接函数 CPAcquireContext 为应用程序创建一个上下文
CPGetProvParam 返回CSP相关的信息
CPReleaseContext 释放CPAcquireContext创建的上下文
CPSetProvParam 设置CSP的参数操作
CSP密钥生成和交换函数 CPDeriveKey 从一个数据散列中生成一个会话密钥,它保证生成的密钥互不相同
CPDestroyKey 释放一个密钥句柄,释放后,句柄将无效,密钥将无法再被访问
CPExportKey 从CSP容器中导出密钥
CPGenKey 用来生成密钥或密钥对
CPGenRandom 使用随机数填充一个缓冲
CPGetKeyParam 用来得到加密操作密钥的属性
CPGetUserKey 用来获取CSP容器中的持久密钥对
CPImportKey 从一个blob中导入密钥到CSP容器中
CPSetKeyParam 设置密钥的属性
CSP加解密函数 CPDecrypt 用来解密先前被加密的数据
CPEncrypt 用来加密明文
CSP散列和数字签名函数 CPCreateHash 初始化并散列输入数据
CPDestroyHash 删除一个散列对象句柄
CPDuplicateHash 创建一个散列对象的拷贝
CPGetHashParam 获取散列对象的计算结果
CPHashData 散列输入的数据
CPSetHashParam 定制一个散列对象的属性
CPSignHash 签名一个散列对象
CPVerifySignature 校验一个数字签名

(3)GMT 0016-2012:

本标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。
本标准适用于智能密码钥匙产品的研制、使用和检测。

(4)GMT 0018-2012:

本标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口标准,
本标准适用于服务类密码设备的研制使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测

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

(1)Crypto API

可以按照如下顺序调用接口实现摘要:

摘要运算的第一步——调用CryptAcquireContext方法。

实际上,下面介绍的每一个密码运算基本都要先通过调用此方法,设置相应的密码运算参数,并返回相应的CSP句柄,用于后面的运算。此方法调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。

    BOOL CryptAcquireContext (
    HCRYPTPROV * phProv, //返回的CSP句柄
    LPCTSTR pszContainer, //要使用的密钥是在容器,摘要运算不涉及密钥,所以这里设置为NULL
    LPCTSTR pszProvider, //使用到的CSP的名称,如果设置为NULL,则CryptAcquireContext会调用系统缺省CSP
    DWORD dwProvType, //所使用的CSP的类型,一般这里设置为PROV_RSA_FULL(0x1)
    DWORD dwFlags //标志值,如果是涉及到私钥的运算,如签名或解密,应设置为0,否则应设置成CRYPT_VERIFYCONTEXT。
    )

调用CryptCreateHash方法生成一个摘要运算的对象。

此方法调用成功返回true(-1),否则返回false(0)。

    BOOL CryptCreateHash(
    HCRYPTPROV hProv, //上一步返回的CSP句柄
    ALG_ID Algid, //摘要算法
    HCRYPTKEY hKey, //设置成0
    DWORD dwFlags, //设置成0
    HCRYPTHASH * phHash //返回的摘要运算对象
    )

调用CryptHashData方法进行摘要运算。

此方法调用成功返回true(-1),否则返回false(0)。

    BOOL CryptHashData(
    HCRYPTHASH hHash, //上一步返回的摘要运算对象
    BYTE * pbData, //原文
    DWORD dwDataLen, //原文长度
    DWORD dwFlags //设置成0
    )

调用CryptGetHashParam返回摘要的各种相关数据信息。

    BOOL CryptGetHashParam(
    HCRYPTHASH hHash, //上一步返回的摘要运算对象
    DWORD dwParam, //返回摘要的数据长度时:HP_HASHSIZE(0x0004),返回摘要值时:HP_HASHVAL(0x0002)
    BYTE *pbData, //返回摘要的数据长度时:长度值,返回摘要值时:摘要值
    DWORD *pdwDataLen, //长度值所占字节数
    DWORD dwFlags //设置为0
    )

对称加密

生成摘要(同上)

    CryptAcquireContext //返回CSP句柄,参数设置与摘要运算时一致。
    CryptCreateHash //生成摘要运算对象。
    CryptHashData //生成摘要。pbData为调用加密功能的上位程序输入的加密口令。

生成一个会话密钥,用来加密数据。

    BOOL CryptDeriveKey(
	HCRYPTPROV hProv, //上一步返回的CSP句柄
	ALG_ID Algid, //加密算法
	HCRYPTHASH hBaseData, //上一步返回的摘要对象
	DWORD dwFlags, //密钥类型,如果调用的CSP没有特别要求,设置为0
	HCRYPTKEY * phKey //返回的密钥对象
	)

设置密钥参数。

PS:如果采用的是RC2\RC4等流加密算法,这一步可以省略。如果采用的是分组加密算法,那应该在这一步设置加密模式等参数,如:

  • CryptSetKeyParam(hKey, KP_MODE, CRYPT_MODE_CBC, 0);//设置成CBC模式
  • CryptSetKeyParam(hKey, KP_IV, pbIV, 0);//设置初始向量
	BOOL CryptSetKeyParam(
	HCRYPTKEY hKey,
	DWORD dwParam,
	BYTE * pbData,
	DWORD dwFlags
	)

调用CryptEncrypt进行加密。

    BOOL CryptEncrypt(
    HCRYPTKEY hKey,
    HCRYPTHASH hHash, //如果不需要对原数据进行摘要运算可以设为NULL
    BOOL Final, //true时表示没有分块加密或当前是最后一块加密,否则为false
    DWORD dwFlags, //设置为0
    BYTE *pbData, //原文,调用后输出密文
    DWORD *pdwDataLen, //要加密原文长度,调用后返回密文长度
    DWORD dwBufLen //为pbData分配的缓冲区长度
    )
  • 要注意的是,这里的分块和分组加密里的分组是不同的概念,分组是加密算法本身的处理过程,而这里的分块是调用加密功能的业务逻辑,它们处于不同的层面,但分块长度必须是分组长度的整数倍。
  • 在采用分组加密的情况下,密文长度会比明文长度长一些,所以dwBufLen的值应该设置的足够大,以满足返回加密结果的要求。一般的做法是调用两次CryptEncrypt,第一次调用时pbData传NULL,dwBufLen传0,调用后pdwDataLen输出密文所需长度;第二次调用时 dwBufLen设置的值不小于第一次调用后pdwDataLen即可。
  • 调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。

对称解密

    CryptAcquireContext
    CryptCreateHash
    CryptHashData
    CryptDeriveKey
    CryptSetKeyParam

上面的方法与加密的调用顺序和参数设置基本一致,不再赘述。

    BOOL CryptDecrypt(
    HCRYPTKEY hKey,
    HCRYPTHASH hHash,
    BOOL Final,
    DWORD dwFlags,
    BYTE *pbData, //输入密文,调用后输出明文
    DWORD *pdwDataLen //输入为密文长度,调用后输出明文长度
    )

CryptDecrypt方法调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。

对同一数据的加密和解密可以采用不同的分块方式。比如,加密时不分块,解密时分块,不影响最后的解密结果。

使用CryptoAPI编写一个文件保护程序,具有如下功能:

(1)给定明文文件,生成加密文件,同时产生文件的数字签名文件;

(2)给定密文文件,解密出明文文件,并验证签名的正确性。

在不安全的网络上进行安全的数据传输涉及三个方面的要求:信息隐藏,身份鉴别和完整性检验。CryptoAPI除了提供上述三个功能外还提供标准的ASN.1编码、解码,信息解密,数字证书和证书存储区的管理,证书信任列表、吊销列表和证书有效性检查等功能。

信息隐藏

信息隐藏的意义是保障信息内容只能被特定的人获取。信息隐藏通常是使用某种形式的密码学方式。数据加密算法能保障信息的安区隐藏和传输。数据加密算法是将明文数据经过一定的变换使其看上去是一组毫无意义的数据。在没有加密密钥的情况下,对于好的加密算法想从密文获取明文信息是不可能的。被加密的数据可以是任意的ASCII编码文本文件,数据库文件,和任意需要进行安全传输的数据。这里,“信息”是指任意的一段数据,“明文”是指任意一段没有被加密的数据,“密文”是指任意一段加密的数据。被加密的数据可以在不安全的通道上进行传输而不伤害其安全性。之后,密文可以被还原成明文.

数据加密和解密的概念是:对数据加密的时候需要一个加密密钥,相当于门上的一把钥匙。解密的时候,需要使用一个解密密钥来解开数据。加密密钥、解密密钥可以相同也可以不相同。

加密密钥必须小心保存,给其它用户的时候也必须通过安全的通道传递。对解密密钥的访问权限必须小心控制,因为拥有解密密钥意味着可以解开所有相应加密密钥加密的信息。

身份鉴别

安全通讯的前提是通讯的双方知道对方的身份。身份鉴别的任务就是鉴别一个用户或者实体的真实身份。标识用户身份的文档通常被称为信任状或者凭证。

身份鉴别有时候也用来判定接受的数据就是被发送的数据。如果A向B发送了一段数据,B需要鉴别这段数据就是A发出去的,而不是其它冒充A发出去的。为了满足这类验证的需求,CryptoAPI提供数字签名和校验函数,用来对信息进行鉴别。

因为在计算机网网络上传输的数据与用户之间并没有物理连接,因此对数据进行鉴别的凭证也必须能够在网络上进行传输。这种凭证必须由受信任的凭证发行机构发行。

数字证书就是平常说的证书就是这种凭证,是计算机在网络上进行身份验证的有效凭证。

数字证书是由一个被称为证书机构的信任组织或实体颁发的凭证。它包含与证书对应的用户公钥以及其它一些记录证书主题和用户信息的数据。证书机构只有在验证了证书主题和证书对应的用户公钥的有效性之后才会签发证书。

证书申请者和证书机构之间交换签发证书信息可以使用物理介质,比如软盘,进行传输。通常,这种信息都是在计算机网络上进行完成的。证书机构使用被信任的服务程序处理用户的请求和证书的签发工作。

完整性检测

任何通过不安全介质传输的信息都可以被意外或蓄意的修改。在现实世界中,盖章、签名就是用来提供和证明信息完整性的工具。

信息的接收者不但需要确定信息是由谁发送的,还要确定自己收到的信息是发送者发送的信息,而没有任何的变化。要建立数据的完整性检测机制,不仅要发送信息本身,还要发送用来校验数据的信息,这一信息通常被称作哈希值。数据和验证信息都可以与数字签名一起发送来证明其完整性。

(2)PKCS#11

架构:

img

会话状态:

img

对象:

img

(3)GMT 0016-2012

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

术语和定义

下列术语和定义适用于本文件

应用 application

包括容器、设备认证密钥和文件的一种结构,具备独立的权限管理。

容器container

密码设备中用于保存密钥所划分的唯一性存储空间。

设备device

本标准中将智能密码钥匙统称为设备

设备认证 device authentication智能密码钥匙对应用程序的认证
设备认证密钥 device authentication key用于设备认证的密钥。
设备标签 label

设备的别名,可以由用户进行设定并存储于设备内部。

消息鉴别码 message authentication code; MAC消息鉴别算法的输出。
管理员PIN administrator PIN管理员的口令,为ASCII字符串
用户PIN user PIN

用户的口令,为ASCII字符串。

缩略语

下列缩略语适用于本规范: API 应用编程接口(Application Programming Interface)
PKI 公钥基础设施(Public Key Infrastructure)
PKCS#1
公钥密码使用标准系列规范中的第1部分,定义RSA公开密钥算法加密和签名机制(the Public-Key Cryptography Standard Part 1)
PKCS#5 公钥密码使用标准系列规范中的第5部分,描述一种利用从口令派生出来的安全密
钥加密字符串的方法(the Public-Key Cryptography Standard Part 5)
PIN 个人身份识别码(Personal Identification Number)
MAC 消息鉴别码(Message Authentication Code)

(4)GMT 0018-2012

规范性引用文件

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

术语和定义

以下术语和定义活用干本文件

算法标识algorithm ideutifier

用于对密码算法进行唯一标识的符号。

非对称密码算法/公钥密码算法 asymmetric cryptographic algorithm/public key cryptographic algorithm加解密使用不同密钥的密码算法。
解密 decipherment/decryption加密过程对应的逆过程。
设备密钥device key pair

存储在设备内部的用于设备管理的非对称密钥对包含签名密钥对和加密密钥对。

加密encipherment/encryption

对数据进行密码变换以产生密文的过程。

密钥加密密钥key encrypt key;KEK对密钥进行加密保护的密钥。
公钥基础设施public key infrastructure;PKI

用公钥密码技术建立的普遍适用的基础设施,为用户提供证书管理和密钥管理等安全服务。

私钥访问控制码private key access password

用于验证私钥使用权限的口令字。

对称密码技术/对称密码体制 symmetric cryptographic technique

原发者和接收者均采用同秘密密钥进行变换的密码技术(体制)。其中,加密密钥与解密密钥相同或者一个密钥可以从另一个密钥导出的密码体制。

会话密钥session key

处于层次化密钥结构中的最低层,仅在一次会话中使用的密钥。

用户密钥 user key

存储在设备内部的用于应用密码运算的非对称密钥,包含签名密钥对和加密密钥对。

符号和缩略语

下列缩略语适用于本部分:
ECC 椭圆曲线算法(Elliptic Curve Cryptography)
IPK 内部加密公钥(Internal Public Key)
ISK
内部加密私钥(Interal Private Key) EPK
外部加密公钥(Extenal Public Key) KEK 密钥加密密钥(Key Encrypt Key)

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:密钥派生
img

(3)GMT 0016-2012

img
img
img
img
img
img
img
img
img
img
img

(4)GMT 0018-2012

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

img

img
img
img
img
img
img
img
img
img
img
img
img

实际数据结构定义:
image

typedef struct DeviceInfo_st{
    unsigned char IssuerName[40];
    unsigned char DeviceName[16];
    unsigned char DeviceSerial[16];
    unsigned int DeviceVersion;
    unsigned int StandardVersion;
    unsigned int AsymAlgAbility[2];
    unsigned int SymAlgAbility;
    unsigned int HashAlgAbility;
    unsigned int BufferSize;
}DEVICEINFO;

RSA密钥实际数据结构定义:

#define RSAref_MAX_BIT S2048
#define RSAref_MAX_LEN ((RSAref_MAX_BITS+7)/8)
#define RSAref_MAX_PBITS ((RSAref_MAX_BITS+1)/2)
#define RSAref_MAX_PLEN ((RSAref_MAX_PBITS+7)/8)

typedef struct RSArefPublicKey_st
{
    unsigned int bits;
    unsigned char m[RSAref_MAX_LEN];
    unsigned char e[RSAref_MAX_LEN];
}RSArefPublicKey;

typedef struct RSArefPrivateKey_st
{
    unsigned int bits;
    unsigned char m[RSAref_MAX_LEN];
    unsigned char e[RSAref_MAX_LEN];
    unsigned char d[RSAref_MAX_LEN];
    unsigned char prime[2][RSAref_MAX_PLEN]; 
    unsigned char pexp[2][RSAref_MAX_PLEN]; 
    unsigned char coefRSArefMAX_PLEN];
}RSArefPrivateKey;

ECC密钥实际数据结构定义:

#define ECCrcf_MAX_BITS	512
#define ECCref_MAX_LEN	((ECCref_MAXBITS+7)/8)

typedef struct ECCrefPublicKey_st
{
	unsigned int bits;
    unsigned char x[ECCref_MAX_LEN];
    unsigned char y[ECCref_MAX_LEN];
}ECCrefPublicKey;

typedef struct ECCrefPrivateKey_st{
    unsigned int bits;
    unsigned char K[ECCref_MAX_LEN];
}ECCrefPrivateKey,

设备接口描述:

//************************************************************************************
//设备管理
//************************************************************************************

/*
功能:打开密码设备,返回设备句柄。
参数:
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_OpenDevice(void** phDeviceHandle);

/*
功能:关闭密码设备,并释放相关资源。
参数:hDeviceHandle[in] 已打开的设备句柄
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_CloseDevice(void* hDeviceHandle);

/*
功能: 创建与密码设备的会话。
已打开的设备句柄
hDeviceHandlein]参数:h
phessionHiandle[out]
返回与密码设备建立的新会话句柄成功
返回值:0(SDR OK) 成功
非0 失败,返回错误代码

*/

int SDF_OpenSession(void* hDeviceHandle, void** phSessionHandle);

/*
功能:关闭与密码设备已建立的会话,并释放相关资源。
参数:hSessionHandle[in] 与密码设备已建立的会话句柄
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_CloseSesson(void* hSessionHandle);

/*
功能:获取密码设备能力描述。
参数:hSessionHandle[in] 与设备建立的会话句柄
pstDevicelnfo[our] 设备能力描述信息,内容及格式见设备信息定义成功
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_GetDeviceInfo(
	void* hSessionHandle,
          DEVICEINFO* pstDeviceInfo
);

/*
功能:获取指定长度的随机数。
参数:
hSessonHandle[in] 与设备建立的会话句柄
uiLegth[in] 欲获取的随机数长度
pucRandom[out] 缓冲区指针,用于存放获取的随机数
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_GenerateRandom(
	void* hSessionHandle,
	unsigned int uiLength,
	unsigned char* pucRandom
);

/*
功能:获取密码设备内部存储的指定索引私钥的使用权。
参数:
hSessionHandle[in] 与设备建立的会话句柄
uiKeyIndex[in] 密码设备存储私钥的索引值
pucPassword[in] 使用私钥权限的标识码
uiPwdLength[in] 私钥访问控制码长度,不少于8 字节
返回值:0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_GetPrivateKeyAccessRight(
	void* hSessionHandle,
	unsigned int uiKeyIndex,
	unsigned char* pucPassword,
	unsigned int uiPwdLength
);

/*
功能:释放密码设备存储的指定索引私钥的使用授权。
参数:
hSessonHandle[in] 与设备建立的会话句柄
uiKeyIndex[in] 密码设备存储私钥索引值成功
返回值∶0(SDR OK) 成功
非0 失败,返回错误代码
*/

int SDF_ReleasePrivateKeyAccessRight(
	void* hSessionHandle,
	unsigned int uiKeyIndex
);

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

(1)Crypto API

image
image

(2)PKCS#11

image
image
image
image
image

(3)GMT 0016-2012

(4)GMT 0018-2012

参考资料:

https://blog.csdn.net/arlaichin/article/details/3696528?ops_request_misc={"request_id"%3A"161819624516780271560488"%2C"scm"%3A"20140713.130102334.pc_all."}&request_id=161819624516780271560488&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2

posted @ 2021-04-25 18:36  STong66  阅读(436)  评论(0编辑  收藏  举报