一、微软的CryptoAPI
1.1 简介
Windows CryptoAPI是Microsoft 公司提出的安全加密应用服务框架,也是PKI推荐使用的加密 API。它提供了在Win32 环境下使用认证、编码、加密和签名等安全服务时的标准加密接口,用于增强应用程序的安全性与可控性。
CryptoAPI 提供的功能主要有:密钥管理、数据加密和解密、数字签名和验证、证书管理、可信根证书管理、数据编码和解码、数字证书编码和解码、PKCS#7 标准格式编码和解码等。
1.2 CryptoAPI体系架构
CryptoAPI处于应用程序和CSP之间,共由五大主要部分组成,其结构图如下图所示:
-
基本加密函数:用于选择CSP、建立CSP连接、产生密钥、交换及传输密钥等操作。
- 一个CSP 是实现所有加密操作的独立模块,于是在每一个加密应用程序中至少需要提供一个CSP来完成所需的加密操作。
-
证书编/解码函数:用于数据加密、解密、哈希等操作。这类函数支持数据的加密/解密操作;在应用程序中完成计算哈希、创建和校验数字签名操作;用来对证书、证书撤销列表、证书请求和证书扩展进行编码和解码操作。
-
证书库管理函数:用于数字证书及证书库管理等操作。这组函数用于管理证书、证书撤销列表和证书信任列表的使用、储存、获取等。
-
简单消息函数:用于消息处理,比如消息编码/解码、消息加/解密、数字签名及签名验证等操作。它是把多个低层函数包装在一起以完成某个特定任务,以方便用户的使用。
-
底层消息函数:低级消息函数对传输的PKCS#7 数据进行编码,对接收到的PKCS#7 数据进行解码,并且对接收到的消息进行解密和验证。
1.3 CSP函数接口
-
CSP连接函数
-
CSP密钥生成和交换函数
-
CSP加解密函数
-
CSP散列和数字签名函数
-
相关实践代码如下:
#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;
}
二、RAS公司的PKCS#11标准
2.1 简介
在密码系统中,PKCS#11 是公钥加密标准(PKCS)中的一份子 ,它为加密令牌定义了一组平台无关的API 。
-
重要概念1:slot,也称为槽。一个slot为一个密码设备对象。
-
某个打开的slot会话称之为session。
-
Session之间存在不同的验证权限。
-
同一个slot的不同的session之间存在操作的互相影响性,同时在某些状况下,权限会发生同步。
-
-
重要概念2:对象。PKCS#11创建和支持下列对象:
-
PKCS#11的对象可根据其 生命期长短 的不同分成两大类
- 持久存储的类对象:这类对象被保存在USB Key的安全存储区域当中,直到应用程序主动删除这些对象。
- 会话对象:这类对象只存在于运行时建立的特定会话Session当中,一旦会话结束,这类对象也跟着被删除。
决定对象生命期的模板属性是CKA_TOKEN,这是个布尔值,所有的对象都有这一属性。
- 当该值为TRUE时,该对象将被保存到Key内的存储空间
- 否则,该对象保存在会话空间中,当会话结束后,该对象即销毁。
-
PKCS#11的对象可根据访问权限的不同分成两大类
- 公开对象:这类对象是任何用户都可以访问的。
- 私有对象:这一类对象只有身份被验证的用户才有权访问。
决定对象的访问限制类型的模板属性是 CKA_PRIVATE 。这是个布尔值,所有的对象都有这一属性。
注意:pkcs#11只提供了接口的定义, 不包括接口的实现,一般接口的实现是由设备提供商提供的。
2.2 总体模型
2.3 常用函数
-
通用接口函数
-
槽和令牌管理函数
-
会话管理函数
-
对象管理函数
-
加密函数
-
解密函数
-
消息解密函数
-
签名和消息鉴别函数
-
双效加密函数
-
密钥管理函数
-
随机数生成函数
-
并行功能管理函数
三、GM/T 0016-2012
3.1 简介
这个标准规定了基于PKI密码体制的智能密码钥匙密码应用接口,描述了密码应用接口的函数、数据类型、参数的定义和设备的安全要求。
3.2 结构模型
-
层次关系:智能密码钥匙密码应用接口位于智能密码钥匙应用程序与设备之间
-
设备的应用结构:一个设备中存在设备认证密钥和多个应用,应用之间相互独立。设备的逻辑结构如下图所示:
-
应用由管理员PIN、用户PIN、文件和容器组成,可以存在多个文件和多个容器。
-
设备信息
-
设备管理系列函数
-
密码服务系列函数
四、GM/T 0018-2012
4.1 简介
本标准的目标是为公钥密码基础设施应用体系框架下的服务类密码设备制定统一的应用接口标准,通过该接口调用密码设备,向上层提供基础密码服务。为该类密码设备的开发、使用及检测提供标准依据和指导,有利于提高该类密码设备的产品化、标准化和系列化水平。
-
在公钥密码基础设施应用技术体系框架中,密码设备服务层由密码机、密码卡、智能密码终端等设备组成,通过该标准规定的密码设备应用接口向通用密码服务层提供基础密码服务,如下图所示:
4.2 常用函数
-
设备管理类函数
-
密钥管理类函数
-
非对称算法运算类函数
-
非对称算法运算类函数
-
杂凑运算类函数
-
用户文件操作类函数
-
对称加密规范如下:
-
对称解密规范如下: