具体实施方式
下面结合附图,对优选实施例作详细说明。应该强调的是,下述说明仅仅是示例性的,而不是为了限制本发明的范围及其应用。
实施例1
在引擎被调用之前,先在引擎中建立硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系。图1是硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系示意图。图1中,硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系具体是:将硬件加密设备中的加解密算法和OpenSSL接口中的加解密算法中算法参数一致的加解密算法作为具有映射关系的加解密算法。算法参数包括密钥长度、分组长度、初始向量长度。
图2是硬件加密设备的加解密算法传入引擎得到引擎的加解密算法示意图。图2中,引擎中会创建加密对象,用于存储与加解密算法相关的信息。在引擎被调用加载后,硬件加密设备中的加解密算法,会传入引擎中的对称加密对象中,从而实现硬件加密设备的加解密算法传入引擎,得到引擎的加解密算法。
之后,获取引擎的加解密算法的指针、OpenSSL接口中的加解密算法列表值和各加解密算法ID。判断引擎的加解密算法的指针是否为空,如果引擎的加解密算法的指针为空,则说明引擎中传入的硬件加密设备的加解密算法的指针为空,此时将OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法的指针,并返回OpenSSL接口中的加解密算法列表值。图3是OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法的指针示意图。图3中,当引擎的加解密算法的指针为空时,因为引擎的加解密算法存储的是硬件加密设备的加解密算法,所以引擎的加解密算法的指针为空说明,硬件加密设备中没有加解密算法。此时,将OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法的指针,在引擎的加解密算法被调用时,引擎会根据被赋值的指针,调用OpenSSL接口中的加解密算法。
如果引擎的加解密算法的指针不为空,则根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系和算法ID,返回与硬件加密设备的加解密算法相对应的OpenSSL接口中的加解密算法。图4是返回与硬件加密设备的加解密算法相对应的OpenSSL接口中的加解密算法示意图。图4中,引擎的加解密算法的指针不为空时,说明硬件加密设备中包含加解密算法,此时根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系和算法ID,找到与硬件加密设备的加解密算法对应的OpenSSL接口中的加解密算法,返回该OpenSSL接口中的加解密算法,即将OpenSSL接口中的加解密算法存储在引擎的对称加密对象中。这样,引擎的对称加密对象中,存储的就是与硬件加密设备中的加解密算法有映射关系的OpenSSL接口中的加解密算法了。
实施例2
引擎是OpenSSL预留的用于加载第三方加密库的,主要包括了动态库加载的代码和加密函数指针管理的一系列接口。要使用引擎,OpenSSL首先会加载该引擎,并选择要使用的算法或者使用支持的所有加解密算法,这样应用程序在调用加解密函数时,就会指向所加载的第三方加密库中的加解密算法,而不是原先的OpenSSL的libeay32.dll库里的加解密算法,引擎的主要原理是使用第三方加密库中的函数指针或硬件加密设备的接口指针来替换OpenSSL中默认的加解密函数,从而实现动态加载第三方加密库。
在本实施例中,所述硬件加密引擎通过硬件加密接口PKCS#11(密码令牌)接口动态库(当然,也可以是硬件加密接口CSP接口动态库)与硬件加密设备进行连接,以完成数据加解密操作,所述PKCS#11接口动态库由硬件加密设备的开发者提供,所述硬件加密设备包括客户端的智能密钥设备(如USB KEY),服务端的加密机等;PKCS#11接口动态库的内部细节不在本发明描述的范围。
本发明提供的硬件加密引擎通过注册在OpenSSL接口中的bind_engine()、init()、do_cipher()和clean_up()等四个函数实现。其中,引擎绑定接口bind_engine()用于绑定引擎;密钥初始化接口init()用于获取硬件加密接口动态库中的加解密算法并初始化密钥及密钥信息;数据加解密接口do_cipher()用于进行数据的分包加密或解密操作;引擎释放接口clean_up()用于释放引擎。
下面以标准C语言编程为例,并结合PKCS#11接口动态库与OpenSSL接口来说明本发明中硬件加密引擎(以下简称引擎)的实现过程。
如图5所示,当bind_engine()接口被上层应用程序OpenSSL接口调用时,硬件加密引擎执行如下操作:
步骤101、引擎加载PKCS#11接口动态库。
优选地,本步骤通过调用计算机的系统函数loadlibrary()来完成,该PKCS#11接口动态库的文件名是预先约定的。
步骤102、引擎获取PKCS#11接口动态库的函数列表。
优选地,本步骤通过调用PKCS#11接口中的C_GetFunctionList()函数完成;
进一步地,本步骤还可以先通过调用计算机系统函数GetProcAddress()尝试获取PKCS#11接口中的C_GetFunctionList()函数在PKCS#11接口的入口点,调用C_GetFunctionList()函数成功后,就可获得其他PKCS#11接口的入口点,并且能够调用这些接口获得PKCS#11接口动态库的函数列表;如果尝试失败,则报错返回。
具体地,PKCS#11接口动态库的函数列表可以是CK_FUNCTION_LIST_PTR。
需要说明的是,PKCS#11接口动态库的函数列表包含PKCS#11接口动态库中函数指针的指针。
步骤103、引擎通过调用PKCS#11接口动态库中定义的函数C_Initialize()来初始化PKCS#11接口动态库。
具体地,调用PKCS#11接口动态库中定义的函数C_Initialize()是通过步骤102中获取的PKCS#11接口动态库的函数列表中函数C_Initialize()指针的指针来实现的。
需要说明的是,根据PKCS#11接口的规范标准,在进行其他操作之前必须要首先调用该C_Initialize()接口。
步骤104、引擎创建并启动一个监控线程,用于监控硬件加密设备的插拔事件,并将硬件加密设备的插拔状态存储在自定义的数据结构中。
优选地,监控硬件加密设备的插拔事件(硬件加密设备的插入或拔除)是通过调用PKCS#11接口动态库中定义的函数C_WaitForSlotEvent()来实现的,并根据监控到的插拔状态来及时更新自定义数据结构。
其中,调用PKCS#11接口动态库中定义的函数C_WaitForSlotEvent()具体是通过步骤102中获取的PKCS#11接口动态库的函数列表中函数C_WaitForSlotEvent()指针的指针来实现调用的。
具体地,自定义数据结构是指槽列表信息的集合,其中,槽列表信息包含硬件加密设备的插拔状态信息。
具体地,PKCS#11接口动态库中定义的槽列表信息数据结构中包含槽描述、制造商ID、性能标识符、硬件序列号、固件序列号等信息。
步骤105、引擎获取槽列表信息,得到硬件加密设备的插拔状态。
优选地,引擎获取槽列表信息通过调用PKCS#11接口动态库中定义的函数C_GetSlotList()来实现,得到硬件加密设备的插拔状态,并获取当前连接到主机的硬件加密设备句柄,如果当前存在多个硬件加密设备连接到主机,则选择所述列表中的第一个硬件加密设备。
具体地,调用PKCS#11动态库中定义的函数C_GetSlotList()是通过步骤102中获取的PKCS#11接口动态库的函数列表中函数C GetSlotList()指针的指针来实现调用的。
步骤106、引擎与硬件加密设备建立连接,以便对硬件加密设备进行操作。
优选地,引擎与硬件加密设备建立接连是通过调用PKCS#11接口动态库中定义的函数C_OpenSession()来实现的。
具体地,调用PKCS#11接口动态库中定义的函数C_OpenSession()是通过步骤102中获取的PKCS#11接口动态库的函数列表中函数C_OpenSession()指针的指针来实现调用的。
需要说明的是:步骤105中,获取槽列表信息中硬件加密设备的插拔状态是为了能及时告知引擎该硬件加密设备的当前状态,如果,硬件加密设备被拔除,则引擎及时关闭与硬件加密设备的会话,如果,硬件加密设备被插入,则及时引擎开启与硬件加密设备的会话,以便提高工作效率,同时,避免了引擎在使用硬件加密设备时临时开启会话,而硬件加密设备是拔除状态,从而造成错误情况的出现。
步骤107、引擎通过ENGINE_new()函数来创建一个空的引擎对象engine。其中,ENGINE_new()函数是OpenSSL接口中已定义的。
步骤108、引擎为引擎对象engine设置id及名称,例如ENGINE_set_id(engine,”rt18651b”),ENGINE_set_name(engine,”BSDrt18651b engine”)。
步骤109、引擎获取硬件加密设备的算法列表;
具体地,通过调用PKCS#11接口中的C_GetMechanismList来获取算法列表;
例如,取得的算法列表是
{CKM_SHA_1, {0,0,CKF_DIGEST}},
{CKM_DES_ECB, {8,8,
CKF_ENCRYPT|CKF_DECRYPT|CKF_WRAP|CKF_UNWRAP}},
{CKM_DES_CBC, {8,8,
CKF_ENCRYPT|CKF_DECRYPT|CKF_WRAP|CKF_UNWRAP}},
{CKM_DES3_ECB, {24,24,
CKF_ENCRYPT|CKF_DECRYPT|CKF_WRAP|CKF_UNWRAP}},
{CKM_DES3_CBC, {24,24,
CKF_ENCRYPT|CKF_DECRYPT|CKF_WRAP|CKF_UNWRAP}},
步骤110、引擎设置加解密对象的EVP_CIPHER数据结构,以便供上层OpenSSL应用程序调用。
其中,EVP_CIPHER数据结构的定义是OpenSSL中已有的,具体如下:
struct evp_cipher_st
{
int nid;
int block_size;
int key_len;/* Default value for variable length ciphers*/
int iv_len;
unsigned long flags;/* Various flags*/
int(*init)(EVP_CIPHER_CTX*ctx,const unsigned char*key,
const unsigned char *iv,int enc);/*init key*/
int(*do_cipher)(EVP_CIPHER_CTX *ctx,unsigned char *out,
const unsigned char *in,unsigned int inl);/*encrypt/decrypt
data */
int(*clean_up)(EVP_CIPHER_CTX*);/*clean_up ctx */
int ctx_size;/*how big ctx->cipher_data needs to be */
int(*set_asn1_parameters)(EVP_CIPHER_CTX*,ASN1_TYPE*);/*
Populate a ASN1_TYPE with parameters */
int(*get_asn1_parameters)(EVP_CIPHER_CTX*,ASN1_TYPE*);/*Get
parameters from a ASN1_TYPE */
int(*ctrl)(EVP_CIPHER_CTX*,int type,int arg,void *ptr);/*
Miscellaneous operations */
void *app_data;/*Application data */
} /*EVP_CIPHER */;
typedef struct evp_cipher_st EVP_CIPHER;
nid:算法的ID号,在include/openssl/object.h中定义;
block_size:加解密的分组长度
key_len:密钥长度
iv_len:初始向量长度
flags:标志
(*init):初始化函数,提供密钥,IV向量,算法上下文CTX,加密还是解密
(*do_cipher):加解密函数,提供算法上下文CTX,输出数据,输入数据和输入数据长度
(*clean_up):资源释放
ctx_size:各算法相关数据大小,实际就是各算法的密钥数据
(*set_asn1_parameters):设置asn1参数
(*get_asn1_parameters):获取asn1参数
(*ctrl):其他控制操作
app_data:算法相关数据
其中,引擎设置EVP_CIPHER数据结构具体是通过ENGINE_set_ciphers()函数来实现的,并根据OpenSSL的定义,设置对应的nid。
其中,ENGINE_set_ciphers函数的定义如下:
int ENGINE_set_ciphers(ENGINE *e,ENGINE_CIPHERS_PTR f)。
e:引擎对象指针
f:引擎中对称算法选取的回调函数
f回调函数的定义如下:
typedef int(*ENGINE_CIPHERS_PTR)(ENGINE *e,const EVP_CIPHER**cipher,const int **nids,int nid)。
e:引擎对象指针
cipher:EVP_CIPHER指针的指针
nids是对称算法列表值(即int数组)
nid是算法ID号,在获取引擎对象时传入的。
具体地,引擎根据获取的算法列表来填充EVP-CIPHER数据结构;
例如,算法列表中的{CKM_DES3_CBC, {24,24,CKF_ENCRYPT|CKF_DECRYPT|CKF_WRAP|CKF_UNWRAP}},表示智能密钥设备支持CBC(分组链接加密)模式的3DES加解密操作,分组长度和密钥长度都是24字节。则,对应的EVP_CIPHER数据结构如下:
{
20,
24,
24,
24,
0
&Encrypt_DES3_CBC_Init,
&Encrypt_Update,
&Encrypt_Final,
sizeof(EVP_CIPHER_CTX),
NULL,
NULL,
NULL,
}
其中,Encrypt_DES3_CBC_Init、Encrypt_Updata和Encrypt_Filnal是引擎内部的接口,分别通过PKCS#11接口来完成加密操作。
步骤111、引擎判断从bind_engine()接口传入的加解密算法指针cipher是否为空,如果为空,则执行步骤112,否则,执行步骤113。
具体地,根据bind_engine()接口传入硬件加密设备的加解密算法,EVP_CIPHER获得硬件加密设备的加解密算法,成为引擎的加解密算法;
步骤112、引擎将OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法指针cipher,并返回OpenSSL接口中的加解密算法列表长度。
其中,所述加解密算法列表长度指的是加解密算法的数量。
当引擎的加解密算法的指针为空时,因为引擎的加解密算法存储的是硬件加密设备的加解密算法,所以引擎的加解密算法的指针为空说明,硬件加密设备中没有加解密算法。此时,将OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法的指针,在引擎的加解密算法被调用时,引擎会根据被赋值的指针,调用OpenSSL接口中的加解密算法。
步骤113、根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系和算法ID,找到与硬件加密设备的加解密算法对应的OpenSSL接口中的加解密算法,返回该OpenSSL接口中的加解密算法。
当引擎的加解密算法的指针不为空时,说明硬件加密设备中包含加解密算法,此时根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系和算法ID,找到与硬件加密设备的加解密算法对应的OpenSSL接口中的加解密算法,返回该OpenSSL接口中的加解密算法,即将OpenSSL接口中的加解密算法存储在引擎的对称加密对象中。这样,引擎的对称加密对象中,存储的就是与硬件加密设备中的加解密算法有映射关系的OpenSSL接口中的加解密算法了。
在bind_engine()函数调用结束,init()函数被调用之前,OpenSSL接口还要执行以下的操作:OpenSSL接口通过调用ENGINE_init()函数来初始化该引擎;OpenSSL接口通过调用ENGINE_set_default_ciphers()函数将引擎提供的加解密算法设置成为引擎默认的加解密算法;OpenSSL接口从引擎中获取EVP_CIPHER对象和算法ID,并调用EVP_Encryptlnit/EVP_Decryptlnit()函数,转入引擎的init()函数。
首先,需要知道的是,当init()接口被上层OpenSSL应用程序调用时,传入init()接口的参数有:
int(*init)(EVP_CIPHER_CTX*ctx,//上下文
const unsigned char *key,//对称密钥值
const unsined char *iv,//初始向量
int enc);
其中,init()函数中的上下文结构EVP_CIPHER_CTX如下:
struct evp_cipher_ctx_st
{
const EVP_CIPHER *cipher;
ENGINE *engine;/* functional reference if ’cipher’is
ENGINE-provided */
int encrypt;/* encrypt or decrypt */
int buf_len;/* number we have left */
unsigned char oiv[EVP_MAX_IV_LENGTH];/* original iv */
unsigned char iv[EVP_MAX_IV_LENGTH];/* working iv */
unsigned char buf[EVP_MAX_BLOCK_LENGTH];/* saved partial block */
int num;/* used by cfb/ofb mode */
void *app_data;/* application stuff */
int key_len;/* May change for variable length cipher */
unsigned long flags;/* Various flags */
void *cipher_data;/* per EVP data */
int final_used;
int block_mask;
unsigned char final[EVP_MAX_BLOCK_LENGTH];/* possible final block
*/
}/* EVP_CIPHER_CTX */;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
参数为:
cipher:算法指针
engine:加解密引擎
encrypt:加密或解密
buf_len:剩余空间
oiv:原始的初始向量
iv:当前的初始向量
buf:保存的部分块数据
num:cfb/ofb方式时的数据数量
app_data:应用相关数据
key_len:密钥长度
flags:标志
cipher_data:各算法相关部分,主要是各算法的key等
final_used:
block_mask:块的掩码
final:最后的分组块
如图6所示,则当init()接口被上层OpenSSL应用程序调用时,硬件加密引擎(以下简称引擎)执行如下操作:
步骤201、从init()函数的上下文结构EVP_CIPHER_CTX中,获取与所述硬件加密设备中加解密算法相对应的OpenSSL接口中的加解密算法ID,并记为第一算法ID。
具体地,通过上下文结构EVP_CIPHER_CTX中的ctx->cipher->nid变量来获取该第一算法ID。
其中,上下文结构中的ctx->cipher->nid变量是由引擎获取的cipher对象提供的。
步骤202、根据硬件加密设备中加解密算法与OpenSSL接口中加解密算法的映射关系,从PKCS#11接口动态库中,获取与第一算法ID相对应的硬件加密设备的加解密算法ID,并记为第二算法ID,将第二算法ID存储在引擎中,这样引擎便将硬件加密设备当前所要使用的加解密算法设置为了第二算法ID所对应的加解密算法了。
具体的,将所述第二算法ID存储到init()函数中的上下文结构中的cipher_data(ctx->cipher_data)字段中。
其中,在引擎中,将硬件加密设备与OpenSSL中算法参数一致的加解密算法建立一一对应的映射关系;算法参数一致具体是指密钥长度、分组长度、初始向量长度等参数要一致。
为了便于理解,在此举例详述根据硬件加密设备中加解密算法与OpenSSL接口中加解密算法的映射关系,获得第二算法ID的过程。若OpenSSL接口中的算法为AES,PKCS#11接口动态库中的算法是SSF33,在引擎对象中已经定义好了该两个算法的映射关系,AES算法与SSF33算法的算法参数是一致的,通过AES算法的上下文结构的ctx->cipher->nid变量中获取AES算法ID,如果得到了AES算法ID,根据映射关系查找SSF33算法,便得到了SSF33的算法ID,即为第二算法ID。
步骤203、在密钥信息集合中,查找第二算法ID,并判断是否能够查找到该第二算法ID,如果能查到,则执行步骤204;否则,执行步骤205。
判断是否能在密钥信息集合中查找到第二算法ID的密钥信息具体为:通过调用OpenSSL接口中的EVP_Encryptlnit/EVP_Decryptlnit()函数,并根据调用的EVP_Encryptlnit/EVP_Decryptlnit()函数时传入的密钥值,在密钥信息集合中查找第二算法ID。其中,根据传入的密钥值来找到密钥信息具体为:通过调用PKCS#11接口动态库的C_FindObjectslnit()、C_FindObjects()、C_FindObjectFilal()函数在密钥信息集合中进行查找,而查找结果是密钥句柄。
在采用CBC(分组链接)加密模式时,所述EVP_Encryptlnit/EVP_Decryptlnit()函数被调用时传入的参数还包含初始向量值。
另外,需要说明的是,EVP_CIPHER数据结构中定义了密钥长度、密钥分组长度、初始向量长度、密钥值、密钥句柄等信息,上述信息被称之为密钥信息,不同算法密钥的密钥信息构成密钥信息集合。
步骤204、将第二算法ID的密钥句柄存储到init()函数的上下文结构中。
具体地,将第二算法ID的密钥句柄存储到引擎中加解密对象的上下文结构中。
步骤205、创建第二算法ID的密钥,并将所述密钥的密钥信息添加到密钥信息集合中。
创建密钥具体是通过调用PKCS#11接口动态库的C_CreateObject()函数来创建密钥模板。其中,创建密钥模板包括密钥类型、密钥标识、密钥值以及密钥句柄等密钥信息。密钥标识标识密钥是加密密钥还是解密密钥;密钥句柄是供加解密函数使用的。
具体地,例如,当Encrypt_SSF33_CBC_Init()接口被调用时,执行下列操作:
步骤2051:通过PKCS#11接口C_CreateObject创建密钥对象,将上层应用的密钥导入硬件加密设备。
进一步地,也可以通过PKCS#11接口C_GenerateKey控制硬件加密设备创建密钥。
步骤2052:通过PKCS#11接口C_EncryptInit()进行加密初始化操作,将算法设为CKM_SSF33_CBC。
其中,CKM_SSF33_CBC表示CBC模式的SSF33加解密操作。
Encrypt_Update和Encrypt_Final分别通过PKCS#11接口C_EncryptUpdate和PKCS#11接口C_EncryptFinal完成后续的加密操作。
其中,解密操作的处理与加密类似,不再赘述。
其中,具体算法与算法索引的对应关系保存在引擎内部。
在init()调用结束,do_cipher()函数调用之前,OpenSSL调用EVP_EncryptUpdata/EVP_DecryptUpdata()函数,而通过上述接口函数的被调用,引擎向上层应用提交硬件加密设备的算法列表,也确定了当前硬件加密设备所要使用的加解密算法,上层应用程序便可以使用硬件加密设备中的算法了,而这一步具体是通过调用do_cipher()接口来完成的。
首先,需要说明的是,当do_cipher()函数被调用时,传入的参数有:
int(*do_cipher)(EVP_CIPHER_CTX*ctx,//上下文
unsigned char *out,//加/解密输出数据
const unsigned char *in,//加/解密输入数据
unsigned int inl);//加/解密输入数据的长度
其中EVP_CIPHER_CTX数据结构的定义为
_ struct evp_cipher_ctx_st
{
const EVP_CIPHER *cipher;//算法
ENGINE *engine;//引擎
int encrypt;//加密或解密
int buf_len;//当前要处理的数据长度
unsigned char oiv[EVP_MAX_IV_LENGTH];//初始起始变量
unsigned char iv[EVP_MAX_IV_LENGTH];//当前初始变量
unsigned char buf[EVP_MAX_BLOCK_LENGTH];//保存的部分数据块
int num;//仅供CFB/OFB模式使用
void *app_data;//其他附加数据
int key_len;//密钥长度
unsigned long flags;//标志位
void *cipher_data;//各算法相关部分,主要是各算法的key等
int final_used;
int block_mask;//块的掩码
unsigned char final[EVP_MAX_BLOCK_LENGTH];//最后的分组块
} /* EVP_CIPHER_CTX */;
typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
其中的cipher由上层应用在引擎通过bind_engine()接口报告的算法列表中选定,engine由上层应用创建,并在调用bind_engine()时与算法列表建立关联,其他密钥数据由具体运算过程决定。
如图7所示,当do_cipher()接口被上层OpenSSL应用程序调用时,硬件加密引擎执行如下操作:
步骤301、根据do_cipher的上下文结构EVP_CIPHER_CTX,在密钥信息集合中,查找出与第二算法ID的密钥相对应的密钥信息,并从中取出密钥句柄。
步骤302、在PKCS#11接口动态库中,查找出与第二算法ID相同的加解密算法ID。
步骤303、控制硬件加密设备根据查找得到的加解密算法对传入的数据进行分包加密或解密操作,并输出结果。
do_cipher()函数调用结束后,OpenSSL结束对引擎的使用,并释放该引擎,通过clean_up()接口来完成。
当clean_up()接口被上层OpenSSL应用程序调用时,clean_up()接口主要进行清除第二算法ID对应的密钥及密钥信息的工作,清除方法是根据传入到引擎中的上下文结构,从密钥信息集合中查找出对应的密钥信息,将所述密钥及密钥信息进行清除。
具体地,当clean_up()接口被调用时,通过PKCS#11接口的C_DestroyObject函数来销毁硬件加密设备中的密钥及密钥信息;另外,硬件加密引擎在此之后还可以通过PKCS#11接口中的函数C_CloseSession关闭硬件加密引擎与硬件加密设备的连接;
需要说明的是,在该过程中还可以通过PKCS#11接口C_Finalize结束硬件加密引擎对整个PKCS#11接口的调用。
实施例3
在本实施例中,硬件加密引擎提供bind_engine()、init()、do_cipher()和clean_up()等四个接口。其中,引擎绑定接口bind_engine()用于绑定引擎;密钥初始化接口init()用于获取硬件加密接口动态库中的加解密算法并初始化密钥及密钥信息;数据加解密接口do_cipher()用于进行分包加密或解密操作;引擎释放接口clean_up()用于释放引擎。
在本实施例中,所述硬件加密引擎通过硬件加密接口CSP(Cryptographic Service Provider密码服务提供程序)接口动态库与硬件加密设备进行连接,以完成加解密操作。
通过CSP接口的CryptAcquireContext与硬件加密设备建立连接;
通过CSP接口的CryptGetProvParam取得硬件加密设备的算法列表;
通过CSP接口的CryptImportKey导入密钥;
通过CSP接口的CryptGenerateKey生成密钥;
通过CSP接口的CryptEncrypt进行加密;
通过CSP接口的CryptDecrypt进行解密;
通过CSP接口的CryptDestroyKey和CryptReleaseContext清理环境;
通过CSP接口的CryptAcquireContext(DELETE_KEYSET)销毁硬件加密设备中的密钥及密钥信息;
具体流程如下:
图8是引擎绑定流程图。图8显示了CSP接口动态库与OpenSSL接口的绑定过程,当bind_engine()接口被上层OpenSSL接口调用时,所述硬件加密引擎(以下简称引擎)执行以下操作:
步骤401、引擎设置CSP名称,并为硬件加密设备选择相应的CSP接口。
其中,硬件加密设备可以是客户端的智能密钥设备(例如USB Key)和服务端的加密机等。
步骤4011、在引擎中定义一个命令CSP_SET,该命令用来实现CSP名称的指定。
其中,实现CSP名称指定的CMD命令函数为ENGINE_CTRL_FUNC_PTR,其定义如下:
typedef int(*ENGINE_CTRL_FUNC_PTR)(ENGINE*,int,long,void*,void(*f)(void));
引擎在实现上述CMD命令函数时将CSP名称传入到引擎,同时将CSP名称保存在全局变量中;这样引擎在需要使用到CSP名称时(如调用CryptAcquireContext函数),使用该全局变量即可。
步骤4012、引擎通过调用ENGINE_set_ctrl_function()函数将上述实现的命令注册到引擎中。
其实,引擎设置CSP名称的原理和ENGINE_set_id和ENGINE_set_name相同。这样,外部使用引擎时可以通过调用ENGINE_ctrl_cmd_string(e,″CSP_SET″,″XXXXXX″,0)函数来设定CSP名称了。
需要说明的是,在使用CSP接口实现硬件加密引擎时,不同的硬件加密设备会有不同的CSP接口,引擎根据CSP名称加以区别,即CSP名称是和CSP接口相对应的。
步骤402、引擎获取硬件加密设备的插拔事件,为硬件加密设备的CSP接口获取一个句柄,与硬件加密设备建立了连接。
引擎通过调用系统函数WindowProc获得WM_DEVICECHANGE类型事件来获得硬件加密设备的插拔事件。其中,DBT_DEVICEARRIVAL消息为插入事件,DBT_DEVICEREMOVECOMPLETE消息为拔除事件。
需要说明的是,由于WM_DEVICECHANGE类型是获取所有USB设备的事件(USB设备是泛指带有USB接口的设备,而硬件加密设备可以是使用USB接口的硬件加密设备,当然也可以是不使用USB接口的硬件加密设备,而系统函数WM_DEVICECHANGE反映的是所有USB接口的设备),因此调用它也可能会收到非硬件加密设备的事件。因此还需要通过调用CryptAcquireContext(CSP接口的句柄)来确定接收到的插拔事件是否是硬件加密设备的插拔事件,通过判断调用CryptAcquireContext是否成功来区分,如果硬件加密设备是拔除状态的,则是获取不到该CSP接口的句柄。
另外,还需要说明的是,如果有新的硬件加密设备的插入,调用CryptAquireContext的同时还可以获取该硬件加密设备的上下文结构(即可以获取到硬件加密设备的CSP接口句柄)。
其中,CryptAcquireContext函数是CSP接口库中已有定义的函数。另外,还需要说明的是,计算机系统会根据引擎设置的CSP名称为引擎自动分配一个句柄;这样,引擎通过获取CSP接口句柄,以便对硬件加密设备进行操作。
步骤403、引擎通过OpenSSL接口中的ENGINE_new()函数来创建一个空的引擎对象engine。
步骤404、引擎为空的引擎对象engine设置id及名称,例如ENGINE_set_id(engine,”rt18651b”),ENGINE_set_name(engine,”BSDrt18651b engine”)。
步骤405、引擎获取硬件加密设备的算法列表。
具体地,引擎通过CSP接口的CryptGetProvParam取得硬件加密设备的算法列表;
步骤406、引擎设置EVP_CIPHER数据结构,以供上层OpenSSL应用程序调用。
具体地说明在步骤110中已有叙述,在此就不再赘述。
步骤407、判断bind_engine()接口传入的加解密算法的指针cipher是否为空,如果为空,则执行步骤408,否则,执行步骤409。
具体地,根据bind_engine()接口传入硬件加密设备的加解密算法,EVP_CIPHER获得硬件加密设备的加解密算法,成为引擎的加解密算法;
步骤408、引擎将OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法的指针cipher,并返回OpenSSL接口中的加解密算法列表长度。
其中,所述加解密算法列表长度指的是加解密算法的数量。
当引擎的加解密算法的指针为空时,因为引擎的加解密算法存储的是硬件加密设备的加解密算法,所以引擎的加解密算法的指针为空说明,硬件加密设备中没有加解密算法。此时,将OpenSSL接口中的加解密算法列表值赋值给引擎的加解密算法的指针,在引擎的加解密算法被调用时,引擎会根据被赋值的指针,调用OpenSSL接口中的加解密算法。
步骤409、根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系和算法ID,找到与硬件加密设备的加解密算法对应的OpenSSL接口中的加解密算法,返回该OpenSSL接口中的加解密算法。
当引擎的加解密算法的指针不为空时,说明硬件加密设备中包含加解密算法,此时根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系和算法ID,找到与硬件加密设备的加解密算法对应的OpenSSL接口中的加解密算法,返回该OpenSSL接口中的加解密算法,即将OpenSSL接口中的加解密算法存储在引擎的对称加密对象中。这样,引擎的对称加密对象中,存储的就是与硬件加密设备中的加解密算法有映射关系的OpenSSL接口中的加解密算法。
图9是获取CSP接口动态库中的加解密算法流程图。图9中,当init()接口被上层OpenSSL应用程序调用时,硬件加密引擎执行如下操作:
步骤501、从init()接口上下文结构中,获取与所述硬件加密设备中加解密算法相对应的OpenSSL接口中加解密算法的算法ID,记为第一算法ID。
具体地,通过上下文结构的ctx->cipher->nid变量来获取第一算法ID。
其中,上下文结构中的ctx->cipher->nid变量是由引擎中获取的cipher对象提供的。
步骤502、根据硬件加密设备中加解密算法与OpenSSL接口中的加解密算法的映射关系,从CSP接口动态库中,获取与第一算法ID对应的硬件加密设备中的加解密算法ID,记为第二算法ID,并将第二算法ID存储在引擎中,这样引擎就将硬件加密设备当前所要使用的加解密算法设置为了第二算法ID所对应的加解密算法了。
具体的,将所述第二算法ID存储到init()函数中的上下文结构中的cipher_data(ctx->cipher_data)字段中。
其中,在引擎中,将硬件加密设备与OpenSSL中算法参数一致的加解密算法建立一一对应的映射关系。算法参数一致具体是指密钥长度、分组长度、初始向量长度等参数要一致。
为了便于理解,在此举例详述根据硬件加密设备的加解密算法与OpenSSL接口中的加解密算法的映射关系,获得第二算法ID的过程。若OpenSSL接口中的算法为IDEA(International Data Encryption Algorithm),CSP接口动态库中的算法是SSF33,在引擎对象中已经定义好了这两个算法的映射关系,IDEA算法与三重数据加密标准SSF33算法的算法参数是一致的,通过高级加密标准AES算法的上下文结构的ctx->cipher->nid变量中获取IDEA算法ID,如果得到了AES算法ID,根据映射关系查找SSF33算法,得到SSF33算法的算法ID,即为第二算法ID。
步骤503、在密钥信息集合中,查找第二算法ID,并判断是否能够查到,如果能查到,则执行步骤504;否则,执行步骤505。
判断是否能在密钥信息集合中查找到第二算法ID的密钥信息具体为:通过调用OpenSSL接口中的EVP_Encryptlnit/EVP_Decryptlnit()函数,并根据调用的EVP_Encryptlnit/EVP_Decryptlnit()函数时传入的密钥值在密钥信息集合中查找密钥信息。其中,根据传入的密钥值来找到密钥信息具体为:在密钥信息集合中进行查找,而查找结果是密钥句柄。
在采用CBC(分组链接)加密模式时,所述EVP_Encryptlnit/EVP_Decryptlnit()函数的参数列表中包含初始向量值。
另外,需要说明的是,加解密算法对像EVP_CIPHER结构中定义了密钥长度、密钥分组长度、初始向量长度、密钥值、密钥句柄等信息,这些信息称之为密钥信息,不同密钥的密钥信息构成密钥信息集合。
步骤504、将第二算法ID的密钥句柄存储到init()函数的上下文结构中。
具体地,将密钥句柄存储到引擎的加密对象的上下文结构中。
步骤505、创建第二算法ID的密钥,并将所述密钥的密钥信息添加到密钥信息集合中。
创建密钥具体是通过调用CSP接口的CryptImportKey()函数来创建密钥。其中,创建密钥模板包括密钥值以及密钥句柄等密钥信息。
如图10所示,当do_cipher()接口被上层OpenSSL应用程序调用时,硬件加密引擎执行如下操作:
步骤601、在密钥信息集合中根据do_cipher()接口的上下文结构,查找出与第二算法ID对应的密钥信息,并从中取出密钥句柄。
步骤602、在CSP接口动态库中,查找出与第二算法ID相同的加解密算法ID。
步骤603、控制硬件加密设备根据查找得到的加解密算法对传入的数据进行分包加密或解密操作。
其中,分包加解密操作包括电子密码本加解密模式EBC和分组链接加解密模CBC式。
加解密操作完成后,cleap_up接口将被调用,以清理环境。
当clean_up()接口被上层OpenSSL应用程序调用时,主要进行清除第二算法ID的密钥及密钥信息的工作,清除方法是根据传入到引擎中的上下文结构,从密钥信息集合中查找出对应的密钥信息,将所述密钥及密钥信息清除。
具体地,当clean_up()函数被调用时,通过PKCS#11接口的C_DestroyObject函数来销毁硬件加密设备中的密钥及密钥信息;另外,硬件加密引擎在此之后还可以通过PKCS#11接口中的函数C_CloseSession关闭硬件加密引擎与硬件加密设备的连接;
需要说明的是,在该过程中还可以通过PKCS#11接口C_Finalize结束硬件加密引擎对整个PKCS#11接口的调用。
本发明提供的硬件加密引擎,将一些硬件加解密算法添加扩展到软件算法库中。同时,该硬件加密引擎在实现上还可以支持多线程和SSL通讯。为使引擎能够支持多线程,使用一个互斥锁和自定义的数据结构来实现并发控制。如果加解密算法还要支持SSL通讯协议(SSL协议定义用于加密和解密用的密钥是分开的),在创建密钥时,还要对密钥用途做标识。
另外,需要说明的是,以上实施例中所指的加解密算法均指对称加解密算法。
以上所述,仅为本发明较佳的具体实施方式,但本发明的保护范围并不局限于此,任何熟悉本技术领域的技术人员在本发明揭露的技术范围内,可轻易想到的变化或替换,都应涵盖在本发明的保护范围之内。因此,本发明的保护范围应该以权利要求的保护范围为准。