USBKey使用openssl链接
2015-09-05 23:36
309 查看
Openssl对USBKey的研究
Key内置的公钥算法实现对用户身份的认证。由于用户私钥保存在密码锁中,理论上使用任何方式都无法读取,因此保证了用户认证的安全性。
SSL是Secure Sockets Layer(安全套接层协议)的缩写,可以在Internet上提供秘密性传输。Netscape公司在推出第一个Web浏览器的同时,提出了SSL协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时实现支持。已经成为Internet上保密通讯的工业标准。
SSL能使用户/服务器应用之间的通信不被攻击者窃听,并且始终对服务器进行认证,还可选择对用户进行认证。SSL协议要求建立在可靠的传输层协议(TCP)之上。SSL协议的优势在于它是与应用层协议独立无关的,高层的应用层协议(例如:HTTP,FTP,TELNET等)能透明地建立于SSL协议之上。SSL协议在应用层协议通信之前就已经完成加密算法、通信密钥的协商及服务器认证工作。在此之后应用层协议所传送的数据都会被加密,从而保证通信的私密性。
Openssl的知识不是一下两下能说完的,后面再详细解释openssl的具体功能,这里直接切入正题,看看openssl是怎么驱动USBKey的。
Openssl加载engine-pkcs11引擎库的时候要调用engine_pkcs11源码中的bind_fn函数,来设置一些参数,具体代码如下所示:
if (!ENGINE_set_id(e, PKCS11_ENGINE_ID) ||
!ENGINE_set_destroy_function(e,pkcs11_engine_destroy) ||
!ENGINE_set_init_function(e, pkcs11_init)||
!ENGINE_set_finish_function(e,pkcs11_finish) ||
!ENGINE_set_ctrl_function(e,pkcs11_engine_ctrl) ||
!ENGINE_set_cmd_defns(e, pkcs11_cmd_defns)||
!ENGINE_set_name(e, PKCS11_ENGINE_NAME) ||
#ifndef OPENSSL_NO_RSA
!ENGINE_set_RSA(e, PKCS11_get_rsa_method())||
#endif
#ifndef OPENSSL_NO_DSA
!ENGINE_set_DSA(e,DSA_get_default_method()) ||
#endif
#ifndef OPENSSL_NO_DH
!ENGINE_set_DH(e, DH_get_default_method())||
#endif
!ENGINE_set_RAND(e, RAND_SSLeay()) ||
#if 0
!ENGINE_set_BN_mod_exp(e, BN_mod_exp) ||
#endif
!ENGINE_set_load_pubkey_function(e,pkcs11_load_public_key) ||
!ENGINE_set_load_privkey_function(e,pkcs11_load_private_key) ) {
return 0;
} else {
return 1;
}
主要包括引擎初始化,主要是初始化一些回调函数,包括导入私钥、导入公钥、导入证书。由于这里没有导入证书的接口,所以需要增加一个导入证书的接口函数,增加如下代码:
!ENGINE_set_load_ssl_client_cert_function(e,load_ssl_client_cert)
其中load_ssl_client_cert是导入证书,这个函数源码中没有实现需要自己实现。后面着重实现这部分代码。
首先启动openssl。启动的时候用root用户来启动。否则在操作USBKey设备的时候会失败。
第二、加载引擎,这里的引擎使用engine-pkcs11提供的引擎方法。这里的引擎有个接口方法没有实现,需要自己来实现这个接口。后面再解释。加载引擎的方法为:
engine -t dynamic -pre SO_PATH:/usr/local/lib/engines/engine_pkcs11.so-pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -preMODULE_PATH:/home/wuyan/rockontrol/haikey.RAR/haikey-0.2.4/lib/htpkcs11.so
其中SO_PATH为pkcs11的引擎,ID为引擎的id,就是后面调用引擎的名字。MODULE_PATH为USBKey的驱动,也就是USBKey的接口函数。结果如下:
(dynamic) Dynamic engine loading support
[Success]:SO_PATH:/usr/local/lib/engines/engine_pkcs11.so
[Success]: ID:pkcs11
[Success]: LIST_ADD:1
[Success]: LOAD
[Success]:MODULE_PATH:/home/wuyan/rockontrol/haikey.RAR/haikey-0.2.4/lib/htpkcs11.so
Loaded: (pkcs11) pkcs11 engine
[ available ]
如果是available说明你的引擎调用成功了。
第三就是进行ssl链接,openssl的s_client工具实现了简单的ssl连接。可以通过它来做ssl链接的测试。在和服务器进行ssl链接的时候,需要读取USBKey中的公钥、私钥和证书。为了使用第二步加载的引擎,需要使用engine参数来指名引擎的id,引擎的id在第二步已经设置过为pkcs11,engine-pkcs11引擎中为了找到对应的证书需要提供一个USBKey对应的id号。通过key参数来指定。有了这两个参数就可以通过openssl引擎正确读取USBkey的公钥和私钥了,但是还不能正确的获取证书,获取证书在openssl中必须通过ssl3协议进行ssl链接。所以需要指定ssl3参数。然后指定client_engine
pkcs11这个参数就是指定导入客户端证书的回调函数。也就是在上面实现的load_ssl_client_cert,在进行ssl连接的时候需要导入证书,否则失败。然后用s_client测试就可以。命令如下:
s_client -engine pkcs11 -key id_ebda8c5d40140168c4e0078f41b995ca3dad981f -keyform engine -ssl_client_engine pkcs11-ssl3 -connect ***.***.***.***:443
s,
SSL_get_client_CA_list(s),
px509,ppkey, NULL, NULL, NULL);
进而调用
e->load_ssl_client_cert(e, s, ca_dn,pcert, ppkey, pother,
ui_method,callback_data);
这就是我们上面所设置的回调函数。从上面可以看出我们只需要&x509, &pkey这两个东西,所以在回调函数中就是要获取这两个东西。好在engine_pkcs11已经提供了这两个函数的接口。贴代码吧:
int load_ssl_client_cert(ENGINE *e, SSL *ssl,
STACK_OF(X509_NAME)*ca_dn, X509 **pcert, EVP_PKEY **pkey,
STACK_OF(X509)**pother, UI_METHOD *ui_method, void *callback_data)
{
*pcert= pkcs11_load_cert(e, "ebda8c5d40140168c4e0078f41b995ca3dad981f");
if(*pcert == NULL)
return0;
*pkey= pkcs11_load_key(e, "ebda8c5d40140168c4e0078f41b995ca3dad981f",NULL, NULL, 0);
if(*pkey == NULL)
return0;
return1;
}
其中ebda8c5d40140168c4e0078f41b995ca3dad981f为USBKey的id号,当然也可以自动去USBKey中读取。其中pkcs11_load_cert和pkcs11_load_key都可以在engine_pkcs11引擎中找到原形。然后重新编译使用吧。
先学到这里,后面继续优化。下一章介绍ssl链接的流程
1. USBKey
USBKey是一种USB接口的硬件设备。它内置单片机或智能卡芯片,有一定的存储空间,可以存储用户的私钥以及数字证书,利用USBKey内置的公钥算法实现对用户身份的认证。由于用户私钥保存在密码锁中,理论上使用任何方式都无法读取,因此保证了用户认证的安全性。
2. Openssl
OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用程序供测试或其它目的使用。SSL是Secure Sockets Layer(安全套接层协议)的缩写,可以在Internet上提供秘密性传输。Netscape公司在推出第一个Web浏览器的同时,提出了SSL协议标准。其目标是保证两个应用间通信的保密性和可靠性,可在服务器端和用户端同时实现支持。已经成为Internet上保密通讯的工业标准。
SSL能使用户/服务器应用之间的通信不被攻击者窃听,并且始终对服务器进行认证,还可选择对用户进行认证。SSL协议要求建立在可靠的传输层协议(TCP)之上。SSL协议的优势在于它是与应用层协议独立无关的,高层的应用层协议(例如:HTTP,FTP,TELNET等)能透明地建立于SSL协议之上。SSL协议在应用层协议通信之前就已经完成加密算法、通信密钥的协商及服务器认证工作。在此之后应用层协议所传送的数据都会被加密,从而保证通信的私密性。
Openssl的知识不是一下两下能说完的,后面再详细解释openssl的具体功能,这里直接切入正题,看看openssl是怎么驱动USBKey的。
3. Openssl引擎
Engine机制目的是为了使OpenSSL能够透明地使用第三方提供的软件加密库或者硬件加密设备进行加密。OpenSSL的Engine机制成功地达到了这个目的,这使得OpenSSL已经不仅仅使一个加密库,而是提供了一个通用地加密接口,能够与绝大部分加密库或者加密设备协调工作。当然,要使特定加密库或加密设备更OpenSSL协调工作,需要写少量的接口代码,但是这样的工作量并不大,虽然还是需要一点密码学的知识。Engine机制的功能跟Windows提供的CSP功能目标是基本相同的。4. Engine-pkcs11
engine_pkcs11是opensc开源工程开发的PKCS#11的一个引擎,可以直接支持openssl。利用它可以实现openssl和USBKey的接口。Openssl加载engine-pkcs11引擎库的时候要调用engine_pkcs11源码中的bind_fn函数,来设置一些参数,具体代码如下所示:
if (!ENGINE_set_id(e, PKCS11_ENGINE_ID) ||
!ENGINE_set_destroy_function(e,pkcs11_engine_destroy) ||
!ENGINE_set_init_function(e, pkcs11_init)||
!ENGINE_set_finish_function(e,pkcs11_finish) ||
!ENGINE_set_ctrl_function(e,pkcs11_engine_ctrl) ||
!ENGINE_set_cmd_defns(e, pkcs11_cmd_defns)||
!ENGINE_set_name(e, PKCS11_ENGINE_NAME) ||
#ifndef OPENSSL_NO_RSA
!ENGINE_set_RSA(e, PKCS11_get_rsa_method())||
#endif
#ifndef OPENSSL_NO_DSA
!ENGINE_set_DSA(e,DSA_get_default_method()) ||
#endif
#ifndef OPENSSL_NO_DH
!ENGINE_set_DH(e, DH_get_default_method())||
#endif
!ENGINE_set_RAND(e, RAND_SSLeay()) ||
#if 0
!ENGINE_set_BN_mod_exp(e, BN_mod_exp) ||
#endif
!ENGINE_set_load_pubkey_function(e,pkcs11_load_public_key) ||
!ENGINE_set_load_privkey_function(e,pkcs11_load_private_key) ) {
return 0;
} else {
return 1;
}
主要包括引擎初始化,主要是初始化一些回调函数,包括导入私钥、导入公钥、导入证书。由于这里没有导入证书的接口,所以需要增加一个导入证书的接口函数,增加如下代码:
!ENGINE_set_load_ssl_client_cert_function(e,load_ssl_client_cert)
其中load_ssl_client_cert是导入证书,这个函数源码中没有实现需要自己实现。后面着重实现这部分代码。
5. 实现
这是openssl对USBKey实现的步骤,他采用openssl引擎的机制来调用USBKey的接口实现证书的认证等一系列过程。本次采用的USBKey为海泰生产的USBKey。在这里介绍一下怎么样使用USBKey通过openssl和服务器进行ssl链接。首先启动openssl。启动的时候用root用户来启动。否则在操作USBKey设备的时候会失败。
第二、加载引擎,这里的引擎使用engine-pkcs11提供的引擎方法。这里的引擎有个接口方法没有实现,需要自己来实现这个接口。后面再解释。加载引擎的方法为:
engine -t dynamic -pre SO_PATH:/usr/local/lib/engines/engine_pkcs11.so-pre ID:pkcs11 -pre LIST_ADD:1 -pre LOAD -preMODULE_PATH:/home/wuyan/rockontrol/haikey.RAR/haikey-0.2.4/lib/htpkcs11.so
其中SO_PATH为pkcs11的引擎,ID为引擎的id,就是后面调用引擎的名字。MODULE_PATH为USBKey的驱动,也就是USBKey的接口函数。结果如下:
(dynamic) Dynamic engine loading support
[Success]:SO_PATH:/usr/local/lib/engines/engine_pkcs11.so
[Success]: ID:pkcs11
[Success]: LIST_ADD:1
[Success]: LOAD
[Success]:MODULE_PATH:/home/wuyan/rockontrol/haikey.RAR/haikey-0.2.4/lib/htpkcs11.so
Loaded: (pkcs11) pkcs11 engine
[ available ]
如果是available说明你的引擎调用成功了。
第三就是进行ssl链接,openssl的s_client工具实现了简单的ssl连接。可以通过它来做ssl链接的测试。在和服务器进行ssl链接的时候,需要读取USBKey中的公钥、私钥和证书。为了使用第二步加载的引擎,需要使用engine参数来指名引擎的id,引擎的id在第二步已经设置过为pkcs11,engine-pkcs11引擎中为了找到对应的证书需要提供一个USBKey对应的id号。通过key参数来指定。有了这两个参数就可以通过openssl引擎正确读取USBkey的公钥和私钥了,但是还不能正确的获取证书,获取证书在openssl中必须通过ssl3协议进行ssl链接。所以需要指定ssl3参数。然后指定client_engine
pkcs11这个参数就是指定导入客户端证书的回调函数。也就是在上面实现的load_ssl_client_cert,在进行ssl连接的时候需要导入证书,否则失败。然后用s_client测试就可以。命令如下:
s_client -engine pkcs11 -key id_ebda8c5d40140168c4e0078f41b995ca3dad981f -keyform engine -ssl_client_engine pkcs11-ssl3 -connect ***.***.***.***:443
6. 获取证书的实现
在第四节的时候我们在引擎中增加了一个获取证书的接口,它在engine_pkcs11中没有实现,这里我们就介绍一下他的实现。首先我们先看openssl是怎么获取证书的,因为我们是用ssl3协议进行连接,在s3_clnt.c文件中int ssl3_send_client_certificate(SSL *s)函数中有ssl_do_client_cert_cb(s,&x509, &pkey)函数,次函数就是要获取客户端证书。他会调用ENGINE_load_ssl_client_cert(s->ctx->client_cert_engine,s,
SSL_get_client_CA_list(s),
px509,ppkey, NULL, NULL, NULL);
进而调用
e->load_ssl_client_cert(e, s, ca_dn,pcert, ppkey, pother,
ui_method,callback_data);
这就是我们上面所设置的回调函数。从上面可以看出我们只需要&x509, &pkey这两个东西,所以在回调函数中就是要获取这两个东西。好在engine_pkcs11已经提供了这两个函数的接口。贴代码吧:
int load_ssl_client_cert(ENGINE *e, SSL *ssl,
STACK_OF(X509_NAME)*ca_dn, X509 **pcert, EVP_PKEY **pkey,
STACK_OF(X509)**pother, UI_METHOD *ui_method, void *callback_data)
{
*pcert= pkcs11_load_cert(e, "ebda8c5d40140168c4e0078f41b995ca3dad981f");
if(*pcert == NULL)
return0;
*pkey= pkcs11_load_key(e, "ebda8c5d40140168c4e0078f41b995ca3dad981f",NULL, NULL, 0);
if(*pkey == NULL)
return0;
return1;
}
其中ebda8c5d40140168c4e0078f41b995ca3dad981f为USBKey的id号,当然也可以自动去USBKey中读取。其中pkcs11_load_cert和pkcs11_load_key都可以在engine_pkcs11引擎中找到原形。然后重新编译使用吧。
先学到这里,后面继续优化。下一章介绍ssl链接的流程
相关文章推荐
- OpenSSL编程之RSA
- 怎样安装openssl 2011-12-11
- 如何正确使用Nodejs 的 c++ module 链接到 OpenSSL
- Java OpenSSL生成的RSA公私钥进行数据加解密详细介绍
- linux openssl基础介绍
- 使用openssl实现rsa非对称加密算法示例
- openSUSE下的Ruby安装openssl出错解决方法
- 一个检测OpenSSL心脏出血漏洞的Python脚本分享
- 针对OpenSSL安全漏洞调整Nginx服务器的方法
- 图解openssl实现私有CA
- 使用Openssl验证证书链
- 使用openssl操作P12证书
- mac 源码安装Openssl
- Windows XP下编译openssl-1.0.0 (上)
- foolscap实现rpc(三)
- 升级OpenSSL修复高危漏洞Heartbleed
- openssl升级版本,防止 Heartbleed 漏洞
- openssl 升级为1.0.1g
- arch linux上安装 httpd+php+mysql+ openssl