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