很多系统,如炒股软件采用客户端/服务器结构,用户安装客户端炒股软件在本地计算机,并与证券公司的服务器系统连接,以完成股票的买卖等操作。
客户端与服务器端采用SSL安全通道连接,以防止第三方窃听和篡改通信的内容。但连接时不使用双向认证或者只使用文件证书来进行双向认证。这样做会存在一些风险。
1、 任何人获取了本地主机的访问权限就可以连接到服务器端。
2、 文件证书保存在本地计算机的磁盘上,任何人获取了本地计算机的访问权限就可对其修改、替换或者删除.
所以,要使用USBKey证书来替换文件证书来解决文件证书的安全风险。另外,有的软件公司提出了一个USBKey证书和文件证书两者兼顾的想法,既当客户端检查到有USBKey插入,并能够读出用户的证书时,则使用USBKey来实现服务器端认证客户端,并不需要用户输入PIN码,并当有多个USBKey插入时,也不需要用户选择。
如未检查到USBKey插入,则还使用原来的文件证书. 另外,需求不需要USBKey支持对称密码算法,只需要支持客户端认证算法。思考他们的需求,他们可能是出于以下的考虑。
1、 他们可能是对OpenSSL客户端认证如何使用USBKey证书不太了解,并基于文件证书的认证是他们已经存在的应用模式.
2、 他们不需要USBKey支持对称的密码算法,可能是基于股票交易的实时性需求方面的考虑.
USBKey在OpenSSL客户端中的使用有两个方面的应用,第一是实现客户端认证,第二是实现对称算法的加密和解密。以上的需求,只要满足客户端认证的需求就可以了。
OpenSSL实现客户端认证的方法有两种。在OpenSSL0.9.8i以下的版本中,客户端认证是通过回调函数的方法来实现的。在OpenSSL0.9.8i以上的版本中,客户端认证是通过引擎来实现的,并兼容回调函数的方法,既当设置了客户端认证所要使用的引擎,则使用引擎。未设置引擎,而设置了回调函数,则使用回调函数的方法。
实现客户端认证我们可提供三种方案。
l 在SSL_CTX结构中定义了成员int (*client_cert_cb)(SSL *ssl, X509 **x509, EVP_PKEY **pkey);的回调函数,用户获取客户端证书及相关联的密钥,此函数返回1,指明函数调用成功,返回其它值,表示获取客户端证书及密钥失败。获取的密钥被包装在EVP_PKEY结构中,之后, EVP_SignFinal函数将调用这个密钥来执行签名的操作.
l 我们可提供给用户一个动态库,中实现一个此回调函数的定义的导出函数,这个导出函数中实现从USBKey中取回用户的证书及相关联的密钥(公钥和私钥)的功能
l 以RSA算法为例,动态库生成一个RSA结构及RSA算法的实现,以提供给EVP_SignFinal函数来调用它,具体的实现如下。
i. RSA结构中的const RSA_METHOD *meth 成员变量需设置为USBKey操作RSA密钥的相关算法实现的函数,并必须实现 int (*rsa_sign)(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen, const RSA *rsa); 函数。
ii. 变量meth中的成员flags ,必须设置为RSA_FLAG_SIGN_VER。
iii. RSA结构中的CRYPTO_EX_DATA ex_data成员,将用来保存与私钥相关联的信息。(如PKCS11的私钥对象的句柄,及访问此私钥的密码等)
iv. RSA的结构中的BIGNUM *n成员,保存公钥的模
v. RSA的结构中的BIGNUM *e成员,保存公钥的指数
l 这个成员变量可通过SSL_CTX_set_client_cert_cb函数来设置.
l 在OpenSSL0.9.8i以上版本的源码中,实现了基于windows crypt api的调用的引擎,它满足客户端认证的需求,需实现了以下的控制功能
vi. 设置所用的CSP的名称
vii. 设置所用的证书store的名称,默认是’MY’
l 在Openssl0.9.8i以上的版本的引擎的数据结构中,存在成员ENGINE_SSL_CLIENT_CERT_PTR load_ssl_client_cert,它指明并需实现获取客户端证书及相关密钥的函数的指针。
viii. 定义为typedef int (*ENGINE_SSL_CLIENT_CERT_PTR)(ENGINE *, SSL *ssl, STACK_OF(X509_NAME) *ca_dn, X509 **pcert, EVP_PKEY **pkey, STACK_OF(X509) **pother, UI_METHOD *ui_method, void *callback_data);
ix. 主要的参数指明如何在本地的证书库如何过滤证书的操作。
1. ca_dn ,指明了所需的客户端证书的颁发者
2. pcert , 指明所要输出的客户端证书
3. pkey, 指明所要输出的客户端证书相关联的密钥
4. ui_method ,指明需弹出用户的界面来选择证书的方法
l 以RSA算法为例,引擎要生成一个RSA结构及RSA算法的实现,以提供给EVP_SignFinal函数来调用它,具体的实现如下。
x. RSA结构中的const RSA_METHOD *meth 成员变量需设置为USBKey操作RSA密钥的相关算法实现的函数,并必须实现 int (*rsa_sign)(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen, const RSA *rsa); 函数。
xi. 变量meth中的成员flags ,必须设置为RSA_FLAG_SIGN_VER。
xii. RSA结构中的CRYPTO_EX_DATA ex_data成员,将用来保存与私钥相关联的信息。(如PKCS11的私钥对象的句柄,及访问此私钥的密码等)
xiii. RSA的结构中的BIGNUM *n成员,保存公钥的模
xiv. RSA的结构中的BIGNUM *e成员,保存公钥的指数
l 需设置openssl0.9.8i以上版本中的SSL_CTX结构中的成员变量client_cert_engine为引擎的实例,可使用SSL_CTX_set_client_cert_engine函数来设置
l 在OpenSSL0.9.8i以上版本的源码中,实现基于PKCS11的调用的引擎,它满足客户端认证的需求,需实现了以下的控制功能
xv. 设置PKCS11的DLL的路径
xvi. 设置访问私钥对象的用户PIN
xvii. 设置所访问的USBKey的对应的slot
l 在Openssl0.9.8i以上的版本的引擎的数据结构中,存在成员ENGINE_SSL_CLIENT_CERT_PTR load_ssl_client_cert,它指明并需实现获取客户端证书及相关密钥的函数的指针。
xviii. 定义为typedef int (*ENGINE_SSL_CLIENT_CERT_PTR)(ENGINE *, SSL *ssl, STACK_OF(X509_NAME) *ca_dn, X509 **pcert, EVP_PKEY **pkey, STACK_OF(X509) **pother, UI_METHOD *ui_method, void *callback_data);
xix. 主要的参数指明如何在本地的证书库如何过滤证书的操作。
1. ca_dn ,指明了所需的客户端证书的颁发者
2. pcert , 指明所要输出的客户端证书
3. pkey, 指明所要输出的客户端证书相关联的密钥
4. ui_method ,指明需弹出用户的界面来选择证书的方法
l 以RSA算法为例,引擎要生成一个RSA结构及RSA算法的实现,以提供给EVP_SignFinal函数来调用它,具体的实现如下。
xx. RSA结构中的const RSA_METHOD *meth 成员变量需设置为USBKey操作RSA密钥的相关算法实现的函数,并必须实现 int (*rsa_sign)(int type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen, const RSA *rsa); 函数。
xxi. 变量meth中的成员flags ,必须设置为RSA_FLAG_SIGN_VER。
xxii. RSA结构中的CRYPTO_EX_DATA ex_data成员,将用来保存与私钥相关联的信息。(如PKCS11的私钥对象的句柄,及访问此私钥的密码等)
xxiii. RSA的结构中的BIGNUM *n成员,保存公钥的模
xxiv. RSA的结构中的BIGNUM *e成员,保存公钥的指数
l 需设置openssl0.9.8i以上版本中的SSL_CTX结构中的成员变量client_cert_engine为引擎的实例,可使用SSL_CTX_set_client_cert_engine函数来设置
在OpenSSL中,存在一个被命名为 'dynamic’ 的引擎,OpenSSL编译时,会自动的被编译到lib中,它的功能就是动态的加载一个SSL应用所需要的引擎。这样的引擎往往被称为动态引擎。
动态引擎往往是一个DLL文件或者一个SO文件,他必须有两个导出的函数,一个函数名是v_check,执行版本检查的功能。一个函数名bind_engine,支持使应用程序能够调用此引擎的功能函数。
当加载一个动态引擎时,需要使用 'dynamic’ 的引擎的如下的控制流程。
1、 ENGINE_ctrl_cmd_string(e, "SO_PATH", “dll的路径”, 0);
n 设置DLL文件或者SO文件的路径
2、 ENGINE_ctrl_cmd_string(e, "ID", “动态引擎的描述串”, 0);
n 设置动态引擎的描述符串
3、 ENGINE_ctrl_cmd_string(e, "LOAD", NULL, 0);
n 加载此动态引擎
1. 回调函数的方法只能够实现获取客户端证书及相关联的密钥,从而实现客户端认证的需求,并不能够支持OpenSSL所需的对称密码算法的功能需求。另外,这种方法可适应多种平台,使用起来比较灵活,也可根据用户的需求增加额外的功能。
2. 使用CSP引擎的方法将仅限于windows平台,在OpenSSL0.9.8i以上的版本中可以直接使用它的客户端认证的功能,而不需要再开发。但是,如果用户需要SSL的对称算法的支持,则需要再开发。
3. 使用PKCS11引擎的方法可支持多种平台,可满足用户的客户端认证及对称密码算法加密与解密的功能需求,并可根据用户的需求增加额外的功能,但开发的工作量相对较大。
From:http://14521448.blog.hexun.com/53920443_d.html