20181223何家豪 实验一-密码引擎-加密API研究

研究学习密码引擎API

Crypto API

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

摘要

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

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返回具体错误信息。

BOOL CryptCreateHash(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTKEY hKey,
DWORD dwFlags,
HCRYPTHASH *phHash
)

调用此方法生成一个摘要运算的对象。hProv为上一步返回的CSP句柄;Algid为摘要算法,比如可以是CALG_SHA1;hKey和dwFlags都设置成0;phHash为返回的摘要运算对象。返回值同上。

BOOL CryptHashData(
HCRYPTHASH hHash,
BYTE* pbData,
DWORD dwDataLen,
DWORD dwFlags
)

调用CryptHashData方法进行摘要运算。phHash为上一步返回的摘要运算对象;pbData为原文;dwDataLen为原文长度;dwFlags为0。方法返回值同上。

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分配空间,它返回的摘要值。

对称加密

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

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

BOOL CryptDeriveKey(
HCRYPTPROV hProv,
ALG_ID Algid,
HCRYPTHASH hBaseData,
DWORD dwFlags,
HCRYPTKEY* phKey
)

派生密钥。Algid为加密算法,比如CALG_DES、CALG_3DES什么的;hBaseData就是上一步返回的摘要对象;dwFlags是密钥类型,如果调用的CSP没有特别要求,设置为0;phKey为返回的密钥对象。
也可以调用CryptGenKey生成一个会话密钥,用来加密数据,而这个会话密钥可以使用数据接收者的公钥加密传输。不过这种方式实际已经包含在非对称加解密中,因此很少直接拿来用。

BOOL CryptSetKeyParam(
HCRYPTKEY hKey,
DWORD dwParam,
BYTE* pbData,
DWORD dwFlags
)

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

CryptSetKeyParam(hKey, KP_MODE, CRYPT_MODE_CBC, 0);//设置成CBC模式
CryptSetKeyParam(hKey, KP_IV, pbIV, 0);//设置初始向量
BOOL CryptEncrypt(
HCRYPTKEY hKey,
HCRYPTHASH hHash,
BOOL Final,
DWORD dwFlags,
BYTE *pbData,
DWORD *pdwDataLen,
DWORD dwBufLen);

调用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返回具体错误信息。

对称解密

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

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

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

架构

会话状态

对象

机制

根据机制标记,可以分为几类:

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

操作

调用流程

GMT 0016-2012

中国商用密码标准

商用密码行业标准的分类

当前,商用密码的行业标准分为基础类标准、应用类标准、检测类标准和管理类标准。

基础类标准

基础类标准为其他三类标准提供了底层、共性支撑(如术语、算法、协议、产品等);

检测类标准

检测类标准为基础类标准和应用类标准提供了合法性检测的功能,保障商用密码使用的合法性;

管理类标准

管理类标准为其他三类标准提供了管理功能;

应用类标准

应用类标准为上层具体的密码产品、服务应用提供支持。

关于商用密码强制性国家标准和推荐性国家标准、行业标准

我国标准按照实施效力分为强制性标准和推荐性标准。强制性标准仅有国家标准一级。推荐性标准包括推荐性国家标准和行业标准。也就是说,商用密码行业标准都是推荐性标准。

强制性标准必须执行,不符合强制性标准的产品、服务,不得生产、销售、进口或者提供。违反强制性标准的,依法承担相应的法律责任。

国家鼓励采用推荐性标准,即从业单位自愿采用推荐性标准。同时国家还会采取一些正向激励措施,鼓励企业采用推荐性标准。但在有些情况下,推荐性标准的效力会发生转化,必须执行,例如:

  1. 推荐性标准被相关法律、法规、规章等引用,则该推荐性标准具有相应的强制约束力,应当按照法律、法规、规章的相关规定予以实施。
  2. 推荐性标准被企业进行了自我声明公开的,企业必须执行该推荐性标准。企业生产的产品与明示标准不一致的,根据《产品质量法》等法律法规承担相应的法律责任。
  3. 推荐性标准被合同双方作为产品或服务交付的质量依据的,该推荐性标准对合同双方具有约束力,双方必须执行该推荐性标准,并依据《合同法》的规定承担法律责任。

智能密码钥匙密码应用接口规范

国家密码管理局2010年4月22日发布《智能IC卡及智能密码钥匙密码应用接口规范》,自发布之日起施行。

GMT 0018-2012

密码设备应用接口规范等

下列缩略语适用于本部分:

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

GM/T 0006设备定义信息如下:

实际数字结构定义:

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;

GB/T 0018-2012

**# 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 coef RSArefMAX_PLEN];
}RSArefPrivateKey;

ECC加密如下:

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

作为一个编程初学者来说,API函数也许是一个时常耳闻却感觉有些神秘的东西。单看它的复杂语法,就足令人望而生畏,但是任何事物在我们深入了解它之前,总是会有这种感觉的。我们这篇API入门教程的目的,就是要把API函数的来龙去脉告诉大家,破除对API函数的畏惧,使它成为我们编程的好助手。
大家可能在许多书上看到过API的英文全称(Application Programming Interface),WIN32 API也就是MicrosoftWindows 32位平台的应用程序编程接口。对这个定义的理解,需要追溯到操作系统的发展历史上,当WINDOWS操作系统开始占据主导地位的时候,开发WINDOWS平台下的应用程序成为人们的需要。而在WINDOWS程序设计领域处于发展的初期,WINDOWS程序员所能使用的编程工具唯有API函数,这些函数是WINDOWS提供给应用程序与操作系统的接口,他们犹如“积木块”一样,可以搭建出各种界面丰富,功能灵活的应用程序。所以可以认为API函数是构筑整个WINDOWS框架的基石,在它的下面是WINDOWS的操作系统核心,而它的上面则是所有的华丽的WINDOWS应用程序。
但是,那时的WINDOWS程序开发还是比较复杂的工作,程序员必须熟记一大堆常用的API函数,而且还得对WINDOWS操作系统有深入的了解。然而随着软件技术的不断发展,在WINDOWS平台上出现了很多优秀的可视化编程环境,程序员可以采用“即见即所得”的编程方式来开发具有精美用户界面和功能强大的应用程序。
这些优秀可视化编程环境操作简单、界面友好(诸如VB、VC++、DELPHI等),在这些工具中提供了大量的类库和各种控件,它们替代了API的神秘功能,事实上这些类库和控件都是构架在WIN32 API函数基础之上的,是封装了的API函数的集合。它们把常用的API函数的组合在一起成为一个控件或类库,并赋予其方便的使用方法,所以极大的加速了WINDOWS应用程序开发的过程。有了这些控件和类库,程序员便可以把主要精力放在程序整体功能的设计上,而不必过于关注技术细节。
实际上如果我们要开发出更灵活、更实用、更具效率的应用程序,必然要涉及到直接使用API函数,虽然类库和控件使应用程序的开发简单的多,但它们只提供WINDOWS的一般功能,对于比较复杂和特殊的功能来说,使用类库和控件是非常难以实现的,这时就需要采用API函数来实现。
这也是API函数使用的场合,所以我们对待API函数不必刻来研究每一个函数的用法,那也是不现实的(能用的到的API函数有几千个呢)。正如某位大虾所说:API不要去学,在需要的时候去查API帮助就足够了。

所谓API本来是为C和C++程序员写的。API说来说去,就是一种函数,他们包含在一个附加名为DLL的动态连接库文件中。用标准的定义来讲,API就是Windows的32位应用程序编程接口,是一系列很复杂的函数,消息和结构,它使编程人员可以用不同类型的编程语言编制出的运行在Windows95和Windows NT操作系统上的应用程序。可以说,如果你曾经学过VC,那么API对你来说不是什么问题。但是如果你没有学过VC,或者你对Windows95的结构体系不熟悉,那么可以说,学习API将是一件很辛苦的事情。

如果你打开WINDOWS的SYSTEM文件夹,你可以发现其中有很多附加名为DLL的文件。一个DLL中包含的API函数并不只是一个,数十个,甚至是数百个。我们能都掌握它嘛?回答是否定的∶不可能掌握。但实际上,我们真的没必要都掌握,只要重点掌握Windos系统本身自带的API函数就可以了。但,在其中还应当抛开掉同VB本身自有的函数重复的函数。如,VB
的etAttr命令可以获得文件属性,SetAttr可以设置文件属性。对API来讲也有对应的函数
GetFileAttributes和SetFileAttributes,性能都差不多。如此地一算,剩下来的也就5、600个。是的,也不少。但,我可以敢跟你说,只要你熟悉地掌握100个,那么你的编程水平比现在高出至少要两倍。尽管人们说VB和WINDOWS具有密切的关系,但我认为,API更接近
WINDOWS。如果你学会了API,首要的收获便是对WINDOWS体系结构的认识。这个收获是来自不易的。

API是预先定义的接口,以供程序员调用。是一套用来控制系统各个部件的预先定义的函数。操作方式如下:

  1. 在C++调用系统API之前需要声明相应的头文件。这里使用系统函数 MessageBoxA ,对应头文件: ”windows.h“。
  2. 使用尖括号,编译器会先在include目录搜索该头文件;如果未找到,才会在源代码所在目录搜索。使用双引号则相反,会先在源代码目录搜索;通常用于包含程序作者自行编写的头文件。
  3. 可以在第三部的图中看到 MessageBoxA 函数包含四个参数。函数括号内部为参数,每个参数之间用英文逗号隔开。当鼠标指在函数上时,会出现函数说明信息。
  4. 参数中的NULL为常量0,代表不设置或默认设置。也可以不使用常量,直接写作0。

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

整个操作系统分为用户空间和内核空间。
用户空间:顾名思义,用户可以访问的内存空间。
内核空间:只有操作系统可以访问,这是为了安全,所以屏蔽了用户空间。
但是用户又要与内核进行交互,怎么办呢。操作系统设置了系统调用函数,方便用户使用。也就是说,用户进入内核空间的唯一途径就是通过系统调用,当然,还有一些间接地方法,但是最终都是通过系统调用来执行函数,完成相应的功能。
API函数:类似于驱动函数,对应于多个系统调用函数以完成一定的功能。
所以API实际上是提供了一个接口,功能比系统调用复杂,使用也简单
例如:要复制一个文件的内容到另一个文件,API接口可能只要一个函数COPY(),提供特定的参数,源文件和目标文件,以及读取文件的长度等,就可以完成功能。但是这个函数需要调用很多系统调用。像打开源文件,读源文件内容,创建目标文件等等。
那么很自然的应用程序员更喜欢用API函数,因为它简单,方便。系统调用复杂。
同时API方便调用

龙脉GM3000Key接口调用

1、CSP接口标准

微软的加密应用接口,专为WIN32应用程序设计。

CryptoAPI本身不实现密码运算相关操作,而是操作系统通过调用CryptoSPI函数接口相应的加密服务提供者函数(CSP)来实现。

CSP的主要概念为:

容器(key container)、sessionkey、hashobject。

CSP是通过容器来组织密钥。一个容器可以存放两个RSA密钥对,一个用于签名验证,一个用于加解密。此外每个用户在加密对话期间还会随机产生许多会话密钥,CSP比较简单,只有23个函数。

2、P11接口标准

RSA实验室开发的,有跨平台的特性。

是PKCS的一部分,特指密码设备的因公编程接口,PKCS#11标准称为CryptoKi,是一个较底层的编程接口。

P11的主要概念是:令牌、会话、槽和对象。

P11没有容器概念,它对密钥数据的保存和组织主要通过对象,P11定义了三种对象类型:数据对象,证书对象和密钥对象。

3、SKF

SKF接口是国密标准中智能密码钥匙的C语言应用开发接口标准。

目前很多国内密码设备厂商都为其产品提供了SKF接口的开发包,开发者可以通过统一的SKF接口开发密码应用,访问来自不同设备供应商的USB-Key、TF卡、智能卡等不同形态的密码设备,而无需和某一个设备供应商的专属设备或专属接口绑定。

posted @ 2021-04-25 19:56  POI~20181223  阅读(122)  评论(0编辑  收藏  举报