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

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

作者:20191322吴泳淋

密码引擎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研究

一、微软的CryptoAPI

  • Windows CryptoAPI是Microsoft 公司提出的安全加密应用服务框架,也是PKI推荐使用的加密 API。它提供了在Win32 环境下使用认证、编码、加密和签名等安全服务时的标准加密接口,用于增强应用程序的安全性与可控性。应用开发者可以在不了解复杂的加密机制和加密算法的情况下,简便、快速地开发出标准、通用和易于扩展的安全加密应用程序。
  • CryptoAPI 提供的功能主要有:密钥管理、数据加密和解密、数字签名和验证、证书管理、可信根证书管理、数据编码和解码、数字证书编码和解码、PKCS#7 标准格式编码和解码等。
  • 利用CryptoAPI,开发者可以给基于Windows的应用程序添加安全服务,包括: ASN.1编码及解码、数据加密/解密、身份论证、数字证书管理,同时支持PKI、对称密码技术等。应用程序开发者直接使用这些安全功能而无须了解其底层实现。

1. 加密密钥

  • 密钥是密码体系操作的中心,它们必须安全保存,任何人获得某个密钥则对此密钥有关的任何数据都拥有了访问的权限。例如,某人得到了加密某文件的密钥,则他就可以解密此文件了。再比如,获取了对某消息签名的密钥,则可以伪造数字签名。
  • 在CryptoAPI中,支持两种类型密钥:会话密钥、公/私密钥对。
  • 会话密钥也称为对称密钥,用于对称加密算法,例如RC2、RC4、DES等。在CryptoAPI中,一般使用CryptGenKey 或 CryptDeriveKey函数产生会话密钥。为了保证密钥的安全性,这些密钥都保存在CSP内部。当然,用户也可以通过CryptExportKey函数把密钥以加密密钥块形式导出到具体应用空间内,以备以后使用或传输给其他用户。
  • 公/私密钥对(包括公钥、私钥)用于非对称加密算法,例如RSA等。非对称加密算法主要用于加解密会话密钥和数字签名。在CryptoAPI中,一般来说,大多数 CSP产生的密钥容器包含两对密钥对,一对用于加密会话密钥,称为交换密钥对,一对用于产生数字签名,称为签名密钥对;但也有些CSP没有存储密钥对,或者存储不止两对密钥对。
  • 在CryptoAPI中,所有的密钥都存储在CSP,CSP负责产生、销毁密钥,并使用它们完成各种密码操作。也可以利用CryptoAPI函数从CSP中导出密钥。

2. 数据编码/解码

  • 通过通信媒介(例如电话线路)传输数据,必须先把数据序列化,即把数据转化为0、1串。在序列化操作中,接到数据的计算机必须能够把数据转化为其原始格式。序列化完成的操作规则称为通信协议,它由软件和数据传输硬件共同完成,其协议一般包含多层,图3-3为一个简化了的通信协议层。
  • 下图表示:计算机1应用程序层先传输原文数据到编码/解码层,编码/解码层编码原文数据为计算机字节流,然后发送到硬件层,硬件层把字节流数据转化为0、1串流传输到计算机2。计算机2接到数据后,反向操作,转化0、1串流为计算机字节流,发送到编码/解码层,编码/解码层解码计算机字节流为原文数据。
  • 一种可接受的软件设计原理是使用抽象化,即根据问题或对象的一般参数来描述,而不是要描述解决问题的所有细节或对象的所有细节。利用抽象化,设计者说明软件对象的特定性质而不必关心其具体实现,它简化了说明文档。抽象化是大多数现代软件规范的特点,并且多数通信协议都包含一些列的抽象化。

img

  • 简化通信协议层
    • 描述抽象对象的一个普遍方式为:ASN.1(抽象语法标记1),ASN.1在CCITT推荐文件X.208中定义,描述对象转换为0、1串的ASN.1规则称为DER(精确编码规则),在CCITT推荐文档X.509 8.7节定义。CryptoAPI采用的就是这种编码方式,表示数据发送方发送时先把数据抽象为ASN.1对象,然后使用DER编码规则把ASN.1对象转化为可传输的0、1串;接收方接到数据后,利用DER解码规则把0、1串转化为ASN.1对象,然后再把ASN.1对象转化为具体应用支持的数据对象。

3. 数据加/解密

  • 把明文转化为密文的过程称为加密,反之把密文转化为明文的过程称为解密。加密较大的数据,CryptoAPI中约定用对称加密算法,这种算法中,在加密和解密过程中都使用同一个对称密钥或会话密钥。CryptoAPI中,通过其封装好的加解密函数来实现数据加解密操作,不同CSP提供不同的加解密算法,但常用的算法,一般CSP都提供。

4. 哈希与数字签名

  • 哈希与数字签名一般用于确定数据的完整性和身份鉴别。CryptoAPI中,通过其封装好的哈希与数字签名函数来实现相关操作。微软提供的CSP产生的数字签名遵循RSA标准(PKCS#6),其他CSP可能遵循其他标准。

5. 数字证书

  • 数字证书主要用于安全通信中的身份鉴别。CryptoAPI中,对数字证书的使用管理分为证书与证书库函数、证书验证函数。

6. 数字消息

  • 任何被编码的数据都可称为消息,它是发送给其他人或实体(或从其他人或实体接收到)的编码数据,包括数字证书。在CryptoAPI环境中,消息一般会被签名或加密,或者同时被签名和加密。在CryptoAPI2.0版本中,消息处理函数一般分为两类:底层消息函数,简单消息函数。低级消息函数直接和PKCS#7 消息工作,简化消息函数是比较高级的函数,是对几个低级消息函数和证书函数的封装,用来执行指定任务。

7.摘要算法

1) CryptAcquireContext
BOOL CryptAcquireContext (
HCRYPTPROV* phProv,
LPCTSTR pszContainer,
LPCTSTR pszProvider,
DWORD dwProvType,
DWORD dwFlags
)
  • 摘要运算的第一步要调用CryptAcquireContext方法。实际上,下面介绍的每一个密码运算基本都要先通过调用此方法,设置相应的密码运算参数,并返回相应的CSP句柄,用于后面的运算。
  • phProv是返回的CSP句柄;pszContainer是要使用的密钥是在容器;摘要运算不涉及密钥,所以这里设置为NULL;pszProvider为使用到的CSP的名称,如果设置为NULL,则CryptAcquireContext会调用系统缺省CSP;dwProvType为所使用的CSP的类型,一般这里设置为PROV_RSA_FULL(0x1);dwFlags为标志值,如果是涉及到私钥的运算,如签名或解密,应设置为0,但如果是摘要、加密或验证等不涉及私钥的运算,强烈不建议这里设置成0,而应设置成CRYPT_VERIFYCONTEXT(0xF0000000),就是告诉Windows接下来的密码运算是不会访问私钥的。
  • 这样做的原因是,在实际应用中,摘要、加密或验证一般采取软实现的方式,尤其是加密和验证,即采用Windows自带的CSP。这种情况下,如果dwFlags设置成0,Windows的CSP会试图访问其私钥存储区域。而Windows CSP保护其私钥存储区域的口令是使用Windows系统管理员账户的口令加密的。因此,如果用户修改过Windows系统管理员账户的口令,那么其保护私钥存储区域的口令将无法解密,就会造成已经存在的私钥存储区域访问失败,传导到上层的CryptAcquireContext方法失败。为了减少上述不必要的麻烦,切记这里的dwFlags参数如无必须,应设置成CRYPT_VERIFYCONTEXT。
  • 此方法调用成功返回true(-1),否则返回false(0),并可以调用GetLastError返回具体错误信息。
2)CryptCreateHash
BOOL CryptCreateHash(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTKEY hKey,
DWORD dwFlags,
HCRYPTHASH *phHash
)
  • 调用此方法生成一个摘要运算的对象。
  • hProv为上一步返回的CSP句柄;Algid为摘要算法,比如可以是CALG_SHA1;hKey和dwFlags都设置成0;phHash为返回的摘要运算对象。
  • 返回值同上。
3) CryptHashData
BOOL CryptHashData(
HCRYPTHASH hHash,
BYTE* pbData,
DWORD dwDataLen,
DWORD dwFlags
)
  • 调用CryptHashData方法进行摘要运算。
  • phHash为上一步返回的摘要运算对象;pbData为原文;dwDataLen为原文长度;dwFlags为0。
  • 方法返回值同上。
4) CryptGetHashParam
BOOL CryptGetHashParam(
HCRYPTHASH hHash,
DWORD dwParam,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwFlags
)
  • 调用CryptGetHashParam可以返回摘要的各种相关数据信息,这里先返回摘要的数据长度。
  • dwParam设置为HP_HASHSIZE(0x0004);pbData为返回长度值;pdwDataLen为长度值所占字节数;dwFlags为0。
  • 调用成功后,再调用一次CryptGetHashParam方法返回摘要值。
  • 这次dwParam设置为HP_HASHVAL(0x0002);按照上一次调用返回的长度值为pbData分配空间,它返回的摘要值。

8.对称加密

对称加密中常用的方式是根据用户输入的口令加解密文档,即基于口令派生出加解密密钥

1)CryptAcquireContext
  • 返回CSP句柄,参数设置与摘要运算时一致。
2)CryptCreateHash
  • 生成摘要运算对象。
3)CryptHashData
  • 生成摘要。pbData为调用加密功能的上位程序输入的加密口令。
4)BOOL CryptDeriveKey
  • 派生密钥。
  • Algid为加密算法,比如CALG_DES、CALG_3DES什么的;hBaseData就是上一步返回的摘要对象;dwFlags是密钥类型,如果调用的CSP没有特别要求,设置为0;phKey为返回的密钥对象。
  • 也可以调用CryptGenKey生成一个会话密钥,用来加密数据,而这个会话密钥可以使用数据接收者的公钥加密传输。不过这种方式实际已经包含在非对称加解密中,因此很少直接拿来用。
5)BOOL CryptSetKeyParam
  • 设置密钥参数。如果采用的是RC2\RC4等流加密算法,这一步可以省略。
  • 如果采用的是分组加密算法,那应该在这一步设置加密模式等参数。比如
    • CryptSetKeyParam(hKey, KP_MODE, CRYPT_MODE_CBC, 0);//设置成CBC模式
    • CryptSetKeyParam(hKey, KP_IV, pbIV, 0);//设置初始向量
6)BOOL CryptEncrypt
  • 调用CryptEncrypt进行加密。
  • hHash可以传NULL,除非加密的同时还要对原数据进行摘要运算;
  • 可以多次调用CryptEncrypt对原文分块进行加密,因此参数Final为true时表示没有分块加密或当前是最后一块加密,否则为false。
  • 要注意的是,这里的分块和分组加密里的分组是不同的概念,分组是加密算法本身的处理过程,而这里的分块是调用加密功能的业务逻辑,它们处于不同的层面。但分块长度必须是分组长度的整数倍;
  • dwFlags传0;pbData传原文,调用后输出密文;pdwDataLen为要加密原文长度,调用后返回密文长度;dwBufLen是为pbData分配的缓冲区长度,在采用分组加密的情况先,密文长度会比明文长度长一些,所以dwBufLen的值应该设置的足够大,以满足返回加密结果的要求。
  • 一般的做法是调用两次CryptEncrypt,第一次调用时pbData传NULL,dwBufLen传0,调用后pdwDataLen输出密文所需长度;第二次调用时 dwBufLen设置的值不小于第一次调用后pdwDataLen即可。
  • 此方法同样调用成功返回true,否则返回false,并可以调用GetLastError返回具体错误信息。

9.对称解密

对称解密与加密相对应,调用顺序和参数设置基本一致。

  • CryptAcquireContext
    CryptCreateHash
    CryptHashData
    CryptDeriveKey
    CryptSetKeyParam
    BOOL CryptDecrypt(
    HCRYPTKEY hKey,
    HCRYPTHASH hHash,
    BOOL Final,
    DWORD dwFlags,
    BYTE *pbData,
    DWORD *pdwDataLen
    )
    
  • 此方法前四个参数意义与CryptEncrypt相同;

  • pbData输入密文,调用后输出明文;pdwDataLen输入为密文长度,调用后输出明文长度。

  • 返回值与CryptEncrypt一致。

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

二、PKCS #11

  • 在密码系统中,PKCS#11是公钥加密标准(PKCS, Public-Key Cryptography Standards)中的一份子 ,由RSA实验室(RSA Laboratories)发布[1],它为加密令牌定义了一组平台无关的API ,如硬件安全模块和智能卡。
    • 由于没有一个真正的标准加密令牌,这个API已经发展成为一个通用的加密令牌的抽象层。 PKCS#11 API定义最常用的加密对象类型( RSA密钥,X.509证书,DES /三重DES密钥等)和所有需要使用的功能,创建/生成,修改和删除这些对象。注意:pkcs#11只提供了接口的定义, 不包括接口的实现,一般接口的实现是由设备提供商提供的,如usbkey的生产厂商会提供 符合PKCS#11接口标准的API的实现。这样你只要通过接口调用API函数即可实现其功能。
    • PKCS#11主要是应用于智能卡和HSM 。大多数商业证书颁发机构软件使用PKCS#11访问CA的签名密钥或注册用户证书。跨平台的软件需要使用的PKCS #11的智能卡,如Mozilla Firefox和OpenSSL (扩展使用) PKCS#11 。为微软Windows编写的软件或许会用特定平台的MS-CAPI API来取代。
    • Cryptoki 为一个或多个密码设备提供一个接口,这些设备通过大量的槽在系统中运行。每个对应于一个物理阅读器或另一个设备接口的槽可包含一个令牌。当一台密码设备存在于阅读器中,一个令牌就存在于该槽中。当然,由于Cryptoki提供槽和令牌的逻辑视图,所以可能有其它的物理译码。多个槽可能共享一个阅读器。问题在于一个系统有相当多的槽,应用程序能连接到这些槽的其中任何一个或全部槽的令牌上。
  • 密码设备可以按照某一命令集执行某些密码操作,这些命令通常要经过标准设备驱动程序,例如PCMCIA卡服务程序或槽服务程序。Cryptoki使每个密码设备看起来逻辑上很象其它设备,而不管什么技术实现的。因此,应用程序不必直接与设备驱动器接口(或甚至不必知道包括那些设备);Cryptoki 隐藏了这些细节。的确,基础设备完全能用软件来实现,(例如,在一个服务器上运行的处理程序),不须专用硬件。
  • Cryptoki 或许可以作为支持接口功能的库来实现,而应用程序则与该库连接。应用程序可以直接与Cryptoki 连接,或者,Cryptoki 是一个所谓的“共享”库(或动态连接库),在这种情况下,应用程序动态地连接库。用Microsoft Windows和OS/2操作系统可以比较容易地生成数据库,并且在UNIX和DOS中也可相对容易地生成“共享”库。

1.PKCS#11创建和支持的对象

对象 说明
CKO_DATA 应用程序定义的对象。对象的数据结构可由应用程序任意定义,但是数据意义的解释由应用程序负责。
CKO_SECRET_KEY 对称加密算法使用的密钥。
CKO_CERTIFICATE X.509
CKO_PUBLIC_KEY RSA
CKO_PRIVATE_KEY RSA
CKO_MECHANISM 算法对象
1)PKCS#11的对象可根据其生命期长短的不同分成两大类:
  • 一类是持久存储的类对象,这类对象被保存在USB Key的安全存储区域当中,直到应用程序主动删除这些对象;
  • 另一类是会话对象,这类对象只存在于运行时建立的特定会话(Session对象)当中,一旦会话结束,这类对象也跟着被删除。
  • 决定对象生命期的模板属性是CKA_TOKEN,这是个布尔值,所有的对象都有这一属性。
    • 当该值为TRUE时,该对象将被保存到Key内的存储空间,否则,该对象保存在会话空间中,当会话结束后,该对象即销毁。
2)PKCS#11的对象除了生命期长短有分别之外,在访问权限上也有限制。所有的对象都可根据访问权限的不同分成两大类:
  • 一类是公开对象,这类对象是任何用户都可以访问的;
  • 另一类是私有对象,这一类对象只有身份被验证的用户才有权访问。
  • 决定对象的访问限制类型的模板属性是CKA_PRIVATE。这是个布尔值,所有的对象都有这一属性。应用程序可根据需要决定对象应为私有对象还是公开对象。

2.指令列表

接口类型 函数名称 描述
通用接口函数 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

三、GMT 0016-2012

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

缩略语

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

1. 签名验签

  • RSA签名

    **img

  • RSA验签

    **img

  • ECC签名

    **img
    img

  • ECC验签

    **img

2. 加密

  • 加密初始化

    **img

  • 单组数据加密

    **img

  • 多组数据加密

    **img
    img

  • 结束加密

    **img

3. 解密

  • 解密初始化

    **img

  • 单组数据解密

    **img
    img

  • 多组数据解密

    **img

  • 结束解密

    **img

四、GMT 0018-2012

本标准规定了公钥密码基础设施应用技术体系下服务类密码设备的应用接口。

适用于服务类密码设备的研制、使用,以及基于该类密码设备的应用开发,也可用于指导该类密码设备的检测。

符号和缩略语

ECC 椭圆曲线算法(Elliptic Curve Cryptography)
IPK 内部加密公钥(Internal Public Key)
ISK 内部加密私钥(Internal Private Key)
EPK 外部加密公钥(External Public Key)
KEK 密钥加密密钥(Key Encrypt Key)

密码服务

1. 对称算法

  • 对称加密

    **img

  • 对称解密

    **img

  • 计算MAC

**img
img

2. 非对称算法

  • 外部公钥RSA运算

    ** img

  • 内部公钥RSA运算

    **img

  • 内部私钥RSA运算

    **img

五、代码实现

** img

#include "tool.h"
#include <windows.h>
#include <wincrypt.h>
#include <stdio.h>
#include <QDebug>

BOOL CalHash(unsigned char* src,  unsigned char* hash,  int len)
{
	HCRYPTPROV hCryptProv;  //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。
  	LPCTSTR pszContainerName = TEXT("Hash");//用一个TEXT宏定义一个容器的名字,
  	if(CryptAcquireContext(  //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。
		&hCryptProv,    //指向一个CSP模块句柄指针,里面用指定的容器
		pszContainerName, //指定容器的名称
		NULL,       //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块
		PROV_RSA_AES, //确定密钥的类型
		0))
  	{
    	printf("CSP Create Success\n");
  	}else   //不成功的处理段
  	{
   		if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器
   		{
            if(CryptAcquireContext(&hCryptProv,
                pszContainerName,NULL,PROV_RSA_AES,
                CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。
            {
                 printf("A new key container has been created.\n");
            }else
            {
                printf("CSP Create Fail\n");
                return FALSE;
            }
   		}
   	}


  HCRYPTHASH hHash;
  //创建hash对象
  if(!CryptCreateHash( hCryptProv,
             CALG_MD5,
             0,
             0,
             &hHash))
  {
    printf("CryptCreateHash fail!");
    return FALSE;
  }

  if(!CryptHashData( hHash,
            (BYTE*)src,
            len,
            0
            ))
  {
    printf("CryptHashData fail!");
    return FALSE;
  }

  DWORD dwHashLen= 16;
  if(!CryptGetHashParam(hHash,
             HP_HASHVAL,
             (BYTE*)hash,
             &dwHashLen,
             0))
  {
    printf("CryptGetHashParam fail!");
    return FALSE;
  }

  CryptDestroyHash(hHash);
  CryptReleaseContext(hCryptProv, 0);
  return TRUE;
}


BOOL Encrypt(unsigned char* src,
       unsigned char* dest,
       char* passwd
       )

{
  HCRYPTPROV hCryptProv;  //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。
  LPCTSTR pszContainerName = TEXT("Encrypt");//用一个TEXT宏定义一个容器的名字,
  if(CryptAcquireContext(  //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。
               &hCryptProv,    //指向一个CSP模块句柄指针,里面用指定的容器
               pszContainerName, //指定容器的名称
               NULL,       //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块
               PROV_RSA_AES, //确定密钥的类型
               0))
  {
    printf("CSP Create Success\n");
  }
  else
  {
    //不成功的处理段
    if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器
    {
      if(CryptAcquireContext(&hCryptProv,
                  pszContainerName,NULL,PROV_RSA_AES,
                  CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器
      {
        printf("A new key container has been created.\n");
      }
  }
  HCRYPTHASH hCryptHash;
  //创建hash对象
  if(!CryptCreateHash( hCryptProv,
             CALG_MD5,
             0,
             0,
             &hCryptHash))
  {
    printf("CryptCreateHash fail!");
    return FALSE;
  }

  //用输入的密码作哈稀散列

  if(!CryptHashData( hCryptHash,lse
  {
    printf("CSP Create Fail\n");
    return FALSE;
  }

}
            (BYTE*)passwd,
            strlen(passwd),
            0))
  {
    printf("CryptHashData fail!");
    return FALSE;
  }

  //用哈稀散列生成会话密钥
  HCRYPTKEY hCryptKey;
  if(!CryptDeriveKey(hCryptProv,
            CALG_AES_128,
            hCryptHash,
            CRYPT_EXPORTABLE,
            &hCryptKey
            ))
  {
    printf("CryptDeriveKey fail!");
    return FALSE;
  }

  //对文件进行加密,hCryptKey已经与加密算法相关联了CALG_AES_128
  unsigned char buf[16];
  DWORD lenght = 16;
  memcpy(buf,src,16);
  if(!CryptEncrypt(hCryptKey,
           NULL,//如果数据同时进行散列和加密,这里传入一个散列对象
           0,//如果是最后一个块为TRUE
           0,
           (BYTE*)buf,//输入被加密的数据,输出加密数据
           &lenght,//输入输入数据长度,输出加密后数据长度
           16
           ))
  {
    qDebug() << "errre";
    qDebug() << GetLastError();
  }

  memcpy(dest,buf,16);
  CryptDestroyHash(hCryptHash);
  CryptReleaseContext(hCryptProv, 0);
  return TRUE;
}

BOOL Decrypt(unsigned char* src,
       unsigned char* dest,
       char* passwd
       )
{

  HCRYPTPROV hCryptProv;  //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。
  LPCTSTR pszContainerName = TEXT("Decrypt");//用一个TEXT宏定义一个容器的名字,
  if(CryptAcquireContext(  //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。
               &hCryptProv,    //指向一个CSP模块句柄指针,里面用指定的容器
               pszContainerName, //指定容器的名称
               NULL,       //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块
               PROV_RSA_AES, //确定密钥的类型
               0))   //常设为0,还有些其他的类型,请看MSDN
  {
    printf("CSP Create Success\n");
  }
  else
  {
    //不成功的处理段
    if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器
    {
      if(CryptAcquireContext(&hCryptProv,
                  pszContainerName,NULL,PROV_RSA_AES,
                  CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。
      {
        printf("A new key container has been created.\n");
      }
      else
      {
        printf("CSP Create Fail\n");
        return FALSE;
      }
    }
  }
  HCRYPTHASH hCryptHash;
  //创建hash对象
  if(!CryptCreateHash( hCryptProv,
             CALG_MD5,
             0,
             0,
             &hCryptHash))
  {
    printf("CryptCreateHash fail!");
    return FALSE;
  }

  //用输入的密码作哈稀散列

  if(!CryptHashData( hCryptHash,
            (BYTE*)passwd,
            strlen(passwd),
            0
            ))
  {
    printf("CryptHashData fail!");
    return FALSE;
  }

  //用哈稀散列生成会话密钥
  HCRYPTKEY hCryptKey;
  if(!CryptDeriveKey(hCryptProv,
            CALG_AES_128,
            hCryptHash,
            CRYPT_EXPORTABLE,
            &hCryptKey
            ))
  {
    printf("CryptDeriveKey fail!");
    return FALSE;
  }

  CryptDestroyHash(hCryptHash);

  //对文件进行加密,hCryptKey已经与加密算法相关联了CALG_AES_128

  unsigned char buf[16];
  DWORD lenght = 16;
  memcpy(buf,src,16);
  CryptDecrypt(hCryptKey,
         NULL,//如果数据同时进行散列和加密,这里传入一个散列对象
         0,//如果是最后一个块为TRUE
         0,
         (BYTE*)buf,//输入被加密的数据,输出加密数据
         &lenght//输入输入数据长度,输出加密后数据长度
         );

  memcpy(dest,buf,16);
  CryptReleaseContext(hCryptProv, 0);
  return TRUE;
}

BOOL Sign(unsigned char* src,
     unsigned char* dest,
     int      len)
{
  HCRYPTPROV hCryptProv;  //定义一个CSP模块的句柄。“CSP模块,请查看《加密解密二》222页,那里有简单的说明,这里就不说了。
  LPCTSTR pszContainerName = TEXT("Decrypt");//用一个TEXT宏定义一个容器的名字,
  if(CryptAcquireContext(  //这个函数是获取有某个容器的CSP模块的指针,成功返回TRUE。
              &hCryptProv,    //指向一个CSP模块句柄指针,里面用指定的容器
               pszContainerName, //指定容器的名称
               NULL,       //这个参数这里用的是缺省值,指得是缺省得CSP模块,你也可以传入一个LPCTSTR类型的字符串,指定CSP模块
               PROV_RSA_FULL, //确定密钥的类型
               0))   //常设为0,还有些其他的类型,请看MSDN
  {
    printf("CSP Create Success\n");
  }
  else
  {
    //不成功的处理段
    if(GetLastError() == NTE_BAD_KEYSET) // NTE_BAD_KEYSET意味着密钥容器不存在,下面就去创建一个新的密钥容器
    {
     if(CryptAcquireContext(&hCryptProv,
                  pszContainerName,NULL,PROV_RSA_AES,
                  CRYPT_NEWKEYSET)) // CRYPT_NEWKEYSET意味着当指定容器不存在的时候,去创建一个容器。
      {
        printf("A new key container has been created.\n");
      }
      else
      {
        printf("CSP Create Fail\n");
        return FALSE;
      }
    }
  }

  HCRYPTKEY hKey;     //创建一个密钥句柄
  if(CryptGetUserKey( // CryptGetUserKey是获取一个密钥句柄的函数,成功返回TRUE
             hCryptProv,  //指定容器的CSP模块句柄
             AT_SIGNATURE,  //指定私钥的类型
             &hKey))  //原来接收获取的密钥句柄
  {
    printf("A signature key is available.\n");
  }
  else
  {
    printf("No signature key is available.\n");
    if(GetLastError() == NTE_NO_KEY) // NTE_NO_KEY意味着密钥不存在,下面就生成一个密钥
    {
      printf("The signature key does not exist.\n");
      printf("Create a signature key pair.\n");
      if(CryptGenKey( // CryptGenKey生成一个密钥
               hCryptProv, //指定CSP模块的句柄
               AT_SIGNATURE, //对于公钥密码系统,生成一个私钥和一个公钥,这个参数指定了这个密钥是公钥,于是生成了一个密码对。如果不是公钥系统,则指定了密码算法,具体看MSDN。
               0,  //指定了生成密钥的类型,这个参数的说明挺多的,想获取更为详尽的资料请看MSDN。
               &hKey))
      {
        printf("Created a signature key pair.\n");
      }
      else
      {
        printf("Created a signature key fail.\n");
        return FALSE;
      }
    }

    else
    {
      printf("An error other than NTE_NO_KEY ");
      return FALSE;
    }

  }
  HCRYPTHASH hHash;
  if(!CryptHashData( hHash,
            (BYTE*)src,
            len,
            0
            ))
  {
    printf("CryptHashData fail!");
    return FALSE;
  }

  DWORD lenght = 32;
  if(!CryptSignHash(hHash,AT_SIGNATURE,NULL,CRYPT_TYPE2_FORMAT,(BYTE*)dest,&lenght))
  {
    printf("CryptSignHash fail!");
    return FALSE;
  }

  if(hKey) //将密钥句柄销毁
  {
    if(!(CryptDestroyKey(hKey)))
    {
      printf("Error during CryptDestroyKey.");
    }
    hKey = NULL;
  }
  CryptDestroyHash(hHash);
  CryptReleaseContext(hCryptProv, 0);
  return TRUE;
}

char *qstoc(const QString Qstr)
{

  QByteArray ba = Qstr.toLatin1();
  char *c_str;
  c_str = (char *)malloc(ba.length() + 1);
  memset(c_str, 0, ba.length());
  memcpy(c_str, ba.data(), ba.length());
  c_str[ba.length()] = '\0';
  return c_str;
}
posted @ 2022-05-12 16:46  20191322  阅读(166)  评论(0编辑  收藏  举报