使用OpenSSL Engine访问PKCS#11及CryptoAPI
近来做了一些PKI、数字证书方面的东西,对Windows下CryptoAPI和OpenSSL做了一些分析。下图是在一次分享中提到的OpenSSL架构:

由于以前对于engine方面的东西一直没有涉及到,最近研究OpenSC后,对使用Engine访问PKCS#11接口、CryptoAPI接口有了更深的理解。用Python写了一个简单的例子:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
All right reserved.
author: xjump.me#at#gmail.com
file: test_engine.py
useage:
plug-in the alipay usb key and
run `python test_engine.py`
ref:
http://www.openssl.org/
http://www.opensc.org/
http://stackoverflow.com/questions/2195179/need-help-using-m2crypto-engine-to-access-usb-token
'''
import os
from M2Crypto import Engine, m2, X509
cwd = os.getcwd()
def test_pkcs11():
e = Engine.load_dynamic_engine("pkcs11", os.path.join(cwd, "engine_pkcs11.dll"))
pkcs11 = Engine.Engine("pkcs11")
pkcs11.ctrl_cmd_string("MODULE_PATH", r"C:\Windows\System32\WatchData\Watchdata AliPay CSP v3.3\WDP11_ZFD_v33.dll")
m2.engine_init(m2.engine_by_id("pkcs11"))
cert = e.load_certificate("slot_4097")
print cert.as_text()
pkcs11.finish()
def test_capi():
e = Engine.load_dynamic_engine("capi", os.path.join(cwd, "capi.dll"))
capi = Engine.Engine("capi")
m2.engine_init(m2.engine_by_id("capi"))
capi.ctrl_cmd_string("list_containers",None)
capi.ctrl_cmd_string("list_certs",None)
capi.ctrl_cmd_string("list_csps",None)
capi.finish()
if __name__=="__main__":
test_capi()
test_pkcs11()
Engine.cleanup()
插入支付盾,可以读出公钥证书。对于PKI证书来说,安全性取决于私钥,获取公钥证书是无害的:

而对于capi,上面的命令("list_containers","list_certs","list_csps")将列出证书容器、证书、当前系统安装的CSP。列出的CSP如下图所示:

capi引擎支持的所有命令在openssl-x.y.z/engines/e_capi.c文件中:
static const ENGINE_CMD_DEFN capi_cmd_defns[] = {
{CAPI_CMD_LIST_CERTS,
"list_certs",
"List all certificates in store",
ENGINE_CMD_FLAG_NO_INPUT},
{CAPI_CMD_LOOKUP_CERT,
"lookup_cert",
"Lookup and output certificates",
ENGINE_CMD_FLAG_STRING},
{CAPI_CMD_DEBUG_LEVEL,
"debug_level",
"debug level (1=errors, 2=trace)",
ENGINE_CMD_FLAG_NUMERIC},
{CAPI_CMD_DEBUG_FILE,
"debug_file",
"debugging filename)",
ENGINE_CMD_FLAG_STRING},
{CAPI_CMD_KEYTYPE,
"key_type",
"Key type: 1=AT_KEYEXCHANGE (default), 2=AT_SIGNATURE",
ENGINE_CMD_FLAG_NUMERIC},
{CAPI_CMD_LIST_CSPS,
"list_csps",
"List all CSPs",
ENGINE_CMD_FLAG_NO_INPUT},
{CAPI_CMD_SET_CSP_IDX,
"csp_idx",
"Set CSP by index",
ENGINE_CMD_FLAG_NUMERIC},
{CAPI_CMD_SET_CSP_NAME,
"csp_name",
"Set CSP name, (default CSP used if not specified)",
ENGINE_CMD_FLAG_STRING},
{CAPI_CMD_SET_CSP_TYPE,
"csp_type",
"Set CSP type, (default RSA_PROV_FULL)",
ENGINE_CMD_FLAG_NUMERIC},
{CAPI_CMD_LIST_CONTAINERS,
"list_containers",
"list container names",
ENGINE_CMD_FLAG_NO_INPUT},
{CAPI_CMD_LIST_OPTIONS,
"list_options",
"Set list options (1=summary,2=friendly name, 4=full printout, 8=PEM output, 16=XXX, "
"32=private key info)",
ENGINE_CMD_FLAG_NUMERIC},
{CAPI_CMD_LOOKUP_METHOD,
"lookup_method",
"Set key lookup method (1=substring, 2=friendlyname, 3=container name)",
ENGINE_CMD_FLAG_NUMERIC},
{CAPI_CMD_STORE_NAME,
"store_name",
"certificate store name, default \"MY\"",
ENGINE_CMD_FLAG_STRING},
{CAPI_CMD_STORE_FLAGS,
"store_flags",
"Certificate store flags: 1 = system store",
ENGINE_CMD_FLAG_NUMERIC},
{0, NULL, NULL, 0}
};
经过测试,OpenSC的engine_pkcs11实现功能较为完善,而OpenSSL的CAPI引擎访问CryptoAPI实现较为简单。当然,windows下直接使用CryptoAPI访问PKI体系更方便。