zoukankan      html  css  js  c++  java
  • 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
    )
    

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

    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卡、智能卡等不同形态的密码设备,而无需和某一个设备供应商的专属设备或专属接口绑定。

  • 相关阅读:
    java基础篇3---变量值转换(不使用中间变量)
    java基础篇2---逆序输出
    Java基础篇1---数据类型转换
    java基础篇8-----字符大小写字母的转换
    java基础篇7----java.util中的Scanner类的使用
    java基础篇0----运算符
    java基础篇5---循环结构
    ECMAScript arguments 对象(摘自W3C)
    JS中的this的应用总结
    定时器 + 简单的动画效果
  • 原文地址:https://www.cnblogs.com/soujiokita/p/14701797.html
Copyright © 2011-2022 走看看