加密API研究
一、微软的CryptoAPI加密技术
(一)研究学习
Windows Crypto API是Microsoft 公司提出的安全加密应用服务框架,也是PKI推荐使用的加密 API。它提供了在Win32 环境下使用认证、编码、加密和签名等安全服务时的标准加密接口,用于增强应用程序的安全性与可控性。应用开发者可以在不了解复杂的加密机制和加密算法的情况下,简便、快速地开发出标准、通用和易于扩展的安全加密应用程序。Crypto API 提供的功能主要有:密钥管理、数据加密和解密、数字签名和验证、证书管理、可信根证书管理、数据编码和解码、数字证书编码和解码、PKCS#7标准格式编码和解码等。
微软加密服务体系包含3层结构和两个接口,分别为应用程序层、操作系统层(OS)、加密服务提供者层(Cryptographic Service Provider,CSP)、CryptoAPI接口和加密服务提供者接口(CSPI)。
CryptoAPI基本功能主要有:加密密钥、数据编码/解码、数据加/解密、哈希与数字签名、数字证书、数字消息
CryptoAPI共有五部分组成:简单消息函数、低层消息函数、基本加密函数、证书编解码函数和证书库管理函数。
(二)使用方式
创建密钥容器,得到CSP句柄
每一个CSP都有一个名字和一个类型,并且名字保证唯一。所以可以通过名字和类型得到一个CSP。然而,要想加密肯定需要密钥,密钥放在密钥容器。密钥容器并不是一开始就存在的,需要用户去创建。
代码链接:
https://gitee.com/jingle1912/experiment/blob/master/加密API/CSP.c
文件加解密和签名验签
主要函数:(1)主函数:void main(void);(2) 加密文件:BOOL EncryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);(3)解密文件:BOOL DecryptFile(PCHAR szSource, PCHAR szDestination, PCHAR szPassword);(4)签名文件:BOOL SignFile (PCHAR szSource, PCHAR szDestination);(5)验证签名:BOOL VerifyFile (PCHAR szSource, PCHAR szDestination);(6)错误处理:void HandleError(char *s);
加密文件
(1)打开源文件
hSource = fopen(szSource,"rb")
(2)取得密钥容器(CSP)句柄
CryptAcquireContext(&hCryptProv,NULL,NULL,PROV_RSA_FULL,0)
(3)根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件加密)
CryptCreateHash(hCryptProv,CALG_MD5, 0, 0, &hHash)//创建一个Hash对象
CryptHashData(hHash, (BYTE *)szPassword, strlen(szPassword), 0)//用用户输入的密码产生一个散列
CryptDeriveKey(hCryptProv, ENCRYPT_ALGORITHM,hHash, KEYLENGTH, &hKey))//通过散列生成一个会话密钥
CryptDestroyHash(hHash);//销毁Hash对象
(4)加密数据文件
CryptEncrypt(
hKey, // 密钥
0, // 如果数据同时进行散列和加密,这里传入一个散列对象
feof(hSource), // 如果是最后一个被加密的块,输入 TRUE. 如果不是输
// 入 FALSE 这里通过判断是否到文件尾来决定是否为最后一块
0, // 保留
pbBuffer, // 输入被加密数据,输出加密后的数据
&dwCount, // 输入被加密数据实际长度,输出加密后数据长度
dwBufferLen) //pbBuffer 的大小
(5)清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
解密文件
(1)打开加密文件(同上)
(2)取得密钥容器(CSP)句柄(同上)
(3)根据用户输入的密码创建一个会话密钥(即对称密钥,用于对原文件解密)(同上)
(4)解密数据文件
CryptDecrypt(
hKey, // 密钥
0, // 如果数据同时进行散列和加密,这里传入一个散列对象
feof(hSource), // 如果是最后一个被加密的块,输入 TRUE. 如果不是输 .
// 入 FALSE 这里通过判断是否到文件尾来决定是否为最后一块。
0, // 保留
pbBuffer, // 输入被加密数据,输出加密后的数据
&dwCount)) // 输入被加密数据实际长度,输出加密后数据长度
(5)清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)
验证签名
(1)打开文件(同上)
(2)取得密钥容器(CSP)句柄(同上)
(3)导入 pbKeyBlob 公钥
CryptImportKey(hCryptProv, pbKeyBlob, dwBlobLen, 0, 0, &hPubKey)
(4)计算数据文件的Hash值,保存在Hash对象hHash中。(同上)
(5)验证数字签名
CryptVerifySignature(hHash, pbSignature, dwSigLen,hPubKey,NULL, 0)
(6)清理工作,如释放Buffer空间、密钥句柄、CSP句柄等。(同上)
(三)API函数
服务提供者函数
应用程序使用服务提供者函数来连接和断开一个CSP。
CryptAcquireContext | 获得指定CSP的密钥容器的句柄 |
---|---|
CryptContextAddRef | 对HCRYPTPROV句柄增加一个应用计数 |
CryptEnumProviders | 枚举当前计算机中的CSP |
CryptEnumProviderTypes | 枚举CSP的类型 |
CryptGetDefaultProvider | 对于指定CSP类型的却省CSP |
CryptGetProvParam | 得到一个CSP的属性 |
CryptInstallDefaultContext | 安装先前得到的HCRYPTPROV上下文作为当前却省的上下文 |
CryptReleaseContext | 释放由CryptAcquireContext得到的句柄 |
CryptSetProvider和CryptSetProviderEx | 为指定CSP类型指定一个却省的CSP |
CryptSetProvParam | 指定一个CSP的属性 |
CryptUninstallDefaultContext | 删除先前由CryptInstallDefaultContext安装的却省上下文 |
密钥的产生和交换函数
密钥产生函数创建、配置和销毁加密密钥。他们也用于和其他用户进行交换密钥。
CryptAcquireCertificatePrivateKey | 对于指定证书上下文得到一个HCRYPTPROV句柄和dwKeySpec |
---|---|
CryptDeriveKey | 从一个密码中派生一个密钥 |
CryptDestoryKey | 销毁密钥 |
CryptDuplicateKey | 制作一个密钥和密钥状态的精确复制 |
CryptExportKey | 把CSP的密钥做成BLOB 传送到应用程序的内存空间中 |
CryptGenKey | 创建一个随机密钥 |
CryptGenRandom | 产生一个随机数 |
CryptGetKeyParam | 得到密钥的参数 |
CryptGetUserKey | 得到一个密钥交换或签名密钥的句柄 |
CryptImportKey | 把一个密钥BLOB传送到CSP 中 |
CryptSetKeyParam | 指定一个密钥的参数 |
编码/解码函数
有一些编码/解码函数,他们可以用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码。
CryptDecodeObject | 对lpszStructType结构进行解码 |
---|---|
CryptDecodeObjectEx | 对lpszStructType结构进行解码,此函数支持内存分配选项 |
CryptEncodeObject | 对lpszStructType结构进行编码 |
CyptEncodeObjectEx | 对lpszStructType结构进行编码,此函数支持内存分配选项 |
数据加密/解密函数
这些函数支持数据的加密/解密操作。
CryptEncrypt 和CryptDecrypt | 要求在被调用前指定一个密钥。这个密钥可以由CryptGenKey、CryptDeriveKey 或CryptImportKey 产生。创建密钥时要指定加密算法。 |
---|---|
CryptSetKeyParam函数 | 可以指定额外的加密参数。 |
CryptDecrypt | 使用指定加密密钥来解密一段密文 |
CryptEncrypt | 使用指定加密密钥来加密一段明文 |
CryptProtectData | 执行对DATA_BLOB结构的加密 |
CryptUnprotectData | 执行对DATA_BLOB结构的完整性验证和解密 |
哈希和数字签名函数
这些函数在应用程序中完成计算哈希、创建和校验数字签名。
CryptCreateHash | 创建一个空哈希对象 |
---|---|
CryptDestoryHash | 销毁一个哈希对象 |
CryptDuplicateHash | 复制一个哈希对象 |
CryptGetHashParam | 得到一个哈希对象参数 |
CryptHashData | 对一块数据进行哈希,把它加到指定的哈希对象中 |
CryptHashSessionKey | 对一个会话密钥进行哈希,把它加到指定的哈希对象中 |
CryptSetHashParam | 设置一个哈希对象的参数 |
CryptSignHash | 对一个哈希对象进行签名 |
CryptVerifySignature | 校验一个数字签名 |
证书库函数
一个用户站点可以收集许多证书。这些证书是为这个站点的用户所使用的,证书描述了这个用户的具体身份。对于每个人,可能有一个以上的证书。证书库和其相关的函数提供了对库获得、枚举、验证和使用证书库里的信息。
CertAddStoreToCollection | 在证书库中增加一个证书 |
---|---|
CertCloseStore | 关闭一个证书库句柄 |
CertControlStore | 如果证书缓冲区和证书本身内容不相符时,允许给应用程序发一个通知 |
CertDuplicateStore | 通过增加引用计数来复制证书库句柄 |
CertEnumPhysicalStore | 对于指定系统库枚举物理库 |
CertEnumSystemStore | 枚举所有可用的系统库 |
CertEnumSystemStoreLocation | 枚举可用系统库的所有位置 |
CertGetStoreProperty | 得到一个库的属性 |
CertOpenStore | 使用指定库类型来打开证书库 |
CertOpenSystemStore | 打开一个系统证书库 |
CertRegisterPhysicalStore | 在一个注册系统库里增加一个物理库 |
CertRegisterSystemStore | 注册一个系统库 |
CertRemoveStoreFromCollection | 从一个库集合里删除证书库 |
CertSaveStore | 保存证书库 |
CertSetStoreProperty | 设置证书属性 |
CertUnregisterPhysicalStore | 从系统库中删除一个物理库 |
CertUnregisterSystemStore | 反注册一个指定系统库 |
二、pkcs11
(一)学习pkcs #11
PKCS #11标准定义了与密码令牌(如硬件安全模块(HSM)和智能卡)的独立于平台的API,并将API本身命名为“Cryptoki”(“PKCS#11”通常用于指代API以及定义它的标准)。 API定义了最常用的加密对像类型(RSA密钥,X.509证书,DES / 三重DES密钥等)以及使用,创建/生成,修改和删除这些对象所需的所有功能。
pkcs密码中间件位于上层应用和底层安全设备之间,应用基于 PKCS#11 标准接口开发各类应用程序。主要包括2个库:
主API库:提供给应用的PKCS11接口。
tokenDLL库:由主API库调用,完成从上向下到指定设备的套接。
安全密码设备:安全服务资源和实施的载体,完成具体安全功能支撑。
PKCS #11模型中重要的概念之一是slot,也称为槽。一个slot为一个密码设备对象。某个打开的slot会话称之为session。Session之间存在不同的验证权限,而同一个slot的不同的session之间存在操作的互相影响性,同时在某些状况下,权限会发生同步。另外一个重要的概念是对象,PKCS #11中支持几种重要的对象,如公钥、私钥、对称密钥,数据对象等。
PKCS#11创建和支持下列对象:
PKCS#11的对象可根据其生命期长短的不同分成两大类:一类是持久存储的类对象,这类对象被保存在USB Key的安全存储区域当中,直到应用程序主动删除这些对象;另一类是会话对象,这类对象只存在于运行时建立的特定会话(Session对象)当中,一旦会话结束,这类对象也跟着被删除。
PKCS#11的对象除了生命期长短有分别之外,在访问权限上也有限制。所有的对象都可根据访问权限的不同分成两大类:一类是公开对象,这类对象是任何用户都可以访问的;另一类是私有对象,这一类对象只有身份被验证的用户才有权访问。决定对象的访问限制类型的模板属性是CKA_PRIVATE。
(二)使用方式
初始化 PKCS #11 库
C_Initialize()语法如下:
C_Initialize(CK_VOID_PTR pInitArgs);
pInitArgs 是空值 NULL_PTR 或是指向 CK_C_INITIALIZE_ARGS 结构的指针。通过 NULL_PTR,该库可以将 Oracle Solaris 互斥锁用作锁定原语,在多个线程之间仲裁对内部共享结构的访问。请注意,Oracle Solaris 加密框架不接受互斥锁。由于 cryptoki 库的此实现可以安全高效地处理多线程,因此建议使用 NULL_PTR。应用程序还可以使用 pInitArgs 来设置诸如 CKF_LIBRARY_CANT_CREATE_OS_THREADS 之类的标志。C_Finalize() 表示应用程序使用 PKCS #11 库结束会话。
可用插槽的列表
C_GetSlotList() 使用的是可用插槽的列表。如果除了 pkcs11_softtoken.so 以外尚未安装任何其他加密提供者,则 C_GetSlotList() 仅返回缺省插槽。C_GetSlotList() 使用以下语法:
C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList,
CK_ULONG_PTR pulCount);
如果 tokenPresent 设置为 TRUE,则会将搜索限制在那些存在令牌的插槽。如果 pSlotList 设置为 NULL_PTR,则 C_GetSlotlist() 仅返回插槽的数量。pulCount是指向用于接收插槽计数的位置的指针。
如果 pSlotList 指向用于接收插槽的缓冲区,则 pulCount 将设置为 CK_SLOT_ID 元素的最大预期数量。在返回时,pulCount 将设置为 CK_SLOT_ID 元素的实际数量。
通常,PKCS #11应用程序会调用 C_GetSlotList() 两次。第一次调用 C_GetSlotList() 用于获取进行内存分配的插槽数量,第二次调用 C_GetSlotList() 用于检索插槽。
获取指定令牌
C_GetMechanismList() 用于获取指定令牌所支持的机制类型的列表。语法如下所示:
C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList,
CK_ULONG_PTR pulCount);
slotID 用于标识令牌的插槽。pulCount 是指向用于接收机制数量的位置的指针。如果 pMechanismList 设置为 NULL_PTR,则 *pulCount 将返回机制的数量。否则,必须将 *pulCount 设置为列表的大小,pMechanismList 必须指向用于存放列表的缓冲区。
(三)函数接口
接口类型 | 函数名称 | 描述 |
---|---|---|
通用接口函数 | 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接口标准为微软所颁发,在windows操作系统上通行。CSP中重要的概念是容器,一个容器中具有一对公私钥。而证书却是这一对密钥的附加属性了。
CSP总共有23个函数接口。简介如下:
接口类型 | 函数名称 | 描述 |
---|---|---|
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 | 校验一个数字签名 |
三、GMT 0016-2012
(一)学习GMT 0016-2012
接口规范
GMT 0016-2012是国家规定的智能密码钥匙密码应用接口规范,规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。适用于智能密码钥匙产品的研制、使用和检测。
规范性引用文件
下列文件对于本标准的应用是必不可少的。凡是注日期的引用文件。仅所注日期的版本适用于本文件;凡是不注日期的引用文件,其最新版本(包括所有的修改单)适用于本文件;GM/T 0006密码应用标识规范;GM/T AAAA SM2密码算法使用规范
应用application | 包括容器、设备认证密钥和文件的一种结构,具备独立的权限管理。 |
---|---|
容器container | 密码设备中用于保存密钥所划分的唯一性存储空间。 |
设备device | 本标准中将智能密码钥匙统称为设备。 |
设备认证device authentication | 智能密码钥匙对应用程序的认证。 |
设备认证密钥device authentication key | 用于设备认证的密钥。 |
设备标签label | 设备的别名,可以由用户进行设定并存储于设备内部。 |
消息鉴别码message authentication code ; MAC | 消息鉴别算法的输出。 |
管理PIN administrator PIN | 管理员的口令,为ASCII字符串。 |
用户PIN user PIN | 用户的口令,为ASCII字符串。 |
(二)使用方式
层次关系
智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间,如下图所示。
应用结构
(三)函数
设备管理系列函数
访问控制系列函数
应用管理系列函数
文件管理系列函数
容器管理系列函数
密码服务系列函数
四、GMT 0018-2012#
(一)学习GMT 0018-2012
接口标准
GMT 0018-2012标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口标准。适用于服务类密码设备的研制、使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测。
规范性引用文件
下列文件对于本文件的应用是必不可少的。凡是注日期的引用文件,仅注日期的版本适用于本文件;凡是不注日期的引用文件,其最新版本(包括所有的修改单)适用于本文件;GM/T 0006密码应用标识规范;GM/T AAAA SM2密码算法使用规范。
其中还包括了对部分术语的规定:
算法标识algorithm identifier | 用于对密码算法进行唯一标识的符号。 |
---|---|
非对称密码算法/公钥密码算法asymmetric cryptographic algorithm/public key cryptograplrithmts | 加解密使用不同密钥的密码算法。 |
解密decipherment/decryption | 加密过程对应的逆过程。 |
设备密钥device key pair | 存储在设备内部的用于设备管理的非对称密钥对,包含签名密钥对和加密密钥对。 |
加密encipherment/encryption | 对数据进行密码变换以产生密文的过程。 |
密钥加密密钥key encryption key ;KEK | 对密钥进行加密保护的密钥。 |
公钥基础设施public key infrastructure;PKI | 用公钥密码技术建立的普遍适用的基础设施,为用户提供证书管理和密钥管理等安全服务。 |
私钥访问控制码private key access password | 用于验证私钥使用权限的口令字。 |
对称密码技术/对称密码体制symmetric cryptographic technique | 原发者和接收者均采用同一秘密密钥进行变换的密码技术(体制)。其中,加密密钥与解密密钥相同,或者一个密钥可以从另一个密钥导出的密码体制。 |
会话密钥session key | 处于层次化密钥结构中的最低层,仅在一次会话中使用的密钥。 |
用户密钥user key | 存储在设备内部的用于应用密码运算的非对称密钥,包含签名密钥对和加密密钥对。 |
(二)使用方式
在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机,密码卡,智能密码终端等设备组成,通过本标准规定的密码设备应用接口向通用密码服务层提供基础密码服务。如下图所示。
基础密码服务包括密钥生成、单一的密码运算、文件管理等的服务。
本标准采用C语言描述接口函数。如无特别说明,函数中参数的长度单位均为字节数。
(三) 函数
设备管理类函数
设备管理类函数包括以下具体函数:
A.打开设备:SDF_OpenDevice
B.关闭设备:SDF_CloseDevice
C.创建会话:SDF_OpenSession
D.关闭会话:SDF_CloseSession
E.获取设备信息:SDF_GetDeviceInfo
F.产生随机数:SDF_GenerateRandom
G.获取私钥使用权限:SDF_GetPrivateKeyAccessRight
H.释放私钥使用权限: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_GenerateKeyWiuhECC
产生协商数据并计算会话密钥∶SDF_GenerateAgreementDataAndKeyWithECC
基于 ECC算法的数字信封转换∶SDF_ExchangeDigitEnvelopeBaseOnECC
生成会话密钥并用密钥加密密钥加密输出∶SDF_GenerateKeyWithKEK
导入会话密钥并用密钥加密密钥解密∶SDF_ImportKeyWithKEK
销毁会话密钥∶SDF_DestroyKey
非对称算法运算类函数
五、龙脉GM3000Key调用
(一)Windows crypticAPI实现加密
#include <stdio.h>
#define _WIN64
#include <windows.h>
#include <wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define KEYLENGTH 0x00800000
void HandleError(char *s);
#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 8
BOOL EncryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword);
int main()
{
CHAR szSource[100];
CHAR szDestination[100];
CHAR szPassword[100];
printf("Encrypt a file. \n\n");
printf("Enter the name of the file to be encrypted: ");
scanf("%s",szSource);
printf("Enter the name of the output file: ");
scanf("%s",szDestination);
printf("Enter the password:");
scanf("%s",szPassword);
if(EncryptFile(szSource, szDestination, szPassword))
{
printf("Encryption of the file %s was a success. \n", szSource);
printf("The encrypted data is in file %s.\n",szDestination);
}
else
{
HandleError("Error encrypting file!");
}
}
extern BOOL EncryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword)
{
FILE *hSource;
FILE *hDestination;
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
PBYTE pbBuffer;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
if(hSource = fopen(szSource,"rb"))
{
printf("The source plaintext file, %s, is open. \n", szSource);
}
else
{
HandleError("Error opening source plaintext file!");
}
if(hDestination = fopen(szDestination,"wb"))
{
printf("Destination file %s is open. \n", szDestination);
}
else
{
HandleError("Error opening destination ciphertext file!");
}
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
printf("A cryptographic provider has been acquired. \n");
}
else
{
if(CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
CRYPT_NEWKEYSET))//创建密钥容器
{
//创建密钥容器成功,并得到CSP句柄
printf("A new key container has been created.\n");
}
else
{
HandleError("Could not create a new key container.\n");
}
}
if(CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
printf("A hash object has been created. \n");
}
else
{
HandleError("Error during CryptCreateHash!\n");
}
if(CryptHashData(
hHash,
(BYTE *)szPassword,
strlen(szPassword),
0))
{
printf("The password has been added to the hash. \n");
}
else
{
HandleError("Error during CryptHashData. \n");
}
if(CryptDeriveKey(
hCryptProv,
ENCRYPT_ALGORITHM,
hHash,
KEYLENGTH,
&hKey))
{
printf("An encryption key is derived from the password hash. \n");
}
else
{
HandleError("Error during CryptDeriveKey!\n");
}
CryptDestroyHash(hHash);
hHash = NULL;
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
if(ENCRYPT_BLOCK_SIZE > 1)
dwBufferLen = dwBlockLen + ENCRYPT_BLOCK_SIZE;
else
dwBufferLen = dwBlockLen;
if(pbBuffer = (BYTE *)malloc(dwBufferLen))
{
printf("Memory has been allocated for the buffer. \n");
}
else
{
HandleError("Out of memory. \n");
}
do
{
dwCount = fread(pbBuffer, 1, dwBlockLen, hSource);
if(ferror(hSource))
{
HandleError("Error reading plaintext!\n");
}
if(!CryptEncrypt(
hKey,
0,
feof(hSource),
0, //保留
pbBuffer, //输入被加密数据,输出加密后的数据
&dwCount, //输入被加密数据实际长度,输出加密后数据长度
dwBufferLen)) //pbBuffer的大小。
{
HandleError("Error during CryptEncrypt. \n");
}
fwrite(pbBuffer, 1, dwCount, hDestination);
if(ferror(hDestination))
{
HandleError("Error writing ciphertext.");
}
}
while(!feof(hSource));
if(hSource)
fclose(hSource);
if(hDestination)
fclose(hDestination);
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
return(TRUE);
}
void HandleError(char *s)
{
fprintf(stderr,"An error occurred in running the program. \n");
fprintf(stderr,"%s\n",s);
fprintf(stderr, "Error number %x.\n", GetLastError());
fprintf(stderr, "Program terminating. \n");
exit(1);
}
(二)Windows crypticAPI实现解密
#include <stdio.h>
#define _WIN64
#include <windows.h>
#include <wincrypt.h>
#define MY_ENCODING_TYPE (PKCS_7_ASN_ENCODING | X509_ASN_ENCODING)
#define KEYLENGTH 0x00800000
void HandleError(char *s);
#define ENCRYPT_ALGORITHM CALG_RC4
#define ENCRYPT_BLOCK_SIZE 8
BOOL DecryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword);
int main()
{
CHAR szSource[100];
CHAR szDestination[100];
CHAR szPassword[100];
printf("Decrypt a file. \n\n");
printf("Enter the name of the file to be decrypted: ");
scanf("%s",szSource);
printf("Enter the name of the output file: ");
scanf("%s",szDestination);
printf("Enter the password:");
scanf("%s",szPassword);
if(!DecryptFile(szSource, szDestination, szPassword))
{
printf("\nError decrypting file. \n");
}
else
{
printf("\nDecryption of file %s succeeded. \n", szSource);
printf("The decrypted file is %s .\n",szDestination);
}
}
extern BOOL DecryptFile(
PCHAR szSource,
PCHAR szDestination,
PCHAR szPassword)
{
FILE *hSource;
FILE *hDestination;
HCRYPTPROV hCryptProv;
HCRYPTKEY hKey;
HCRYPTHASH hHash;
PBYTE pbBuffer;
DWORD dwBlockLen;
DWORD dwBufferLen;
DWORD dwCount;
BOOL status = FALSE;
if(!(hSource = fopen(szSource,"rb")))
{
HandleError("Error opening ciphertext file!");
}
if(!(hDestination = fopen(szDestination,"wb")))
{
HandleError("Error opening plaintext file!");
}
if(!CryptAcquireContext(
&hCryptProv,
NULL,
NULL,
PROV_RSA_FULL,
0))
{
HandleError("Error during CryptAcquireContext!");
}
if(!CryptCreateHash(
hCryptProv,
CALG_MD5,
0,
0,
&hHash))
{
HandleError("Error during CryptCreateHash!");
}
if(!CryptHashData(
hHash,
(BYTE *)szPassword,
strlen(szPassword),
0))
{
HandleError("Error during CryptHashData!");
}
if(!CryptDeriveKey(
hCryptProv,
ENCRYPT_ALGORITHM,
hHash,
KEYLENGTH,
&hKey))
{
HandleError("Error during CryptDeriveKey!");
}
CryptDestroyHash(hHash);
hHash = 0;
dwBlockLen = 1000 - 1000 % ENCRYPT_BLOCK_SIZE;
dwBufferLen = dwBlockLen;
if(!(pbBuffer = (BYTE *)malloc(dwBufferLen)))
{
HandleError("Out of memory!\n");
}
do
{
dwCount = fread(
pbBuffer,
1,
dwBlockLen,
hSource);
if(ferror(hSource))
{
HandleError("Error reading ciphertext!");
}
if(!CryptDecrypt(
hKey,
0,
feof(hSource),
0,
pbBuffer,
&dwCount))
{
HandleError("Error during CryptDecrypt!");
}
fwrite(
pbBuffer,
1,
dwCount,
hDestination);
if(ferror(hDestination))
{
HandleError("Error writing plaintext!");
}
}
while(!feof(hSource));
status = TRUE;
if(hSource)
fclose(hSource);
if(hDestination)
fclose(hDestination);
if(pbBuffer)
free(pbBuffer);
if(hKey)
CryptDestroyKey(hKey);
if(hHash)
CryptDestroyHash(hHash);
if(hCryptProv)
CryptReleaseContext(hCryptProv, 0);
return status;
}
void HandleError(char *s)
{
fprintf(stderr,"An error occurred in running the program. \n");
fprintf(stderr,"%s\n",s);
fprintf(stderr, "Error number %x.\n", GetLastError());
fprintf(stderr, "Program terminating. \n");
exit(1);
} // End of HandleError