近来做了一些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体系更方便。