发动机清理后pkcs11engine不会注销()

时间:2013-01-09 06:05:33

标签: python logout m2crypto pkcs#11

我想做以下事情:

  1. 在安全令牌上生成密钥对(我使用Aladdin令牌)(PyKCS11)
  2. 生成PKCS#10请求(我使用M2Crypto + engine_pkcs11)并将其发送给CA.
  3. 从CA接收签名的X.509证书并将其写入安全令牌。
  4. 请求生成完成如下:

        def generate_request(
            self, uid, cn=None, user_pin=None, keyid=TOKEN_TEMP_KEY_ID):
        """Generate PKCS#10 certificate request
        @param keyid: ID of key to use for certificate request generation
        @type keyid: str
        @param uid: UID to put in request subject distinguished name
        @type uid: str
        @param cn: Common name (if any) of subject to use for certificate
        request generation
        @type cn: str
        @param user_pin: user PIN code. User privileges are required for
        signing of certificate request
        @type user_pin: str
        @return PKCS10 request in PEM format signed by token's private key
        """
        Engine.load_dynamic()
        e = Engine.Engine('dynamic')
        e.ctrl_cmd_string('SO_PATH', PKCS11_ENGINE_PATH)
        e.ctrl_cmd_string('LIST_ADD', '1')
        e.ctrl_cmd_string('LOAD', None)
        e.ctrl_cmd_string('MODULE_PATH', PKCS11_LIBRARY_PATH)
        a = Engine.Engine('pkcs11')
        a.init()
        # Hex-encoded key id should be provided to that function
        k = a.load_private_key(hexlify(keyid), pin=user_pin)
        req = X509.Request()
        subject_name = PKCS10_DN_PREFIX + (('UID', MBSTRING_ASC, uid, -1, -1, 0),)
        if cn:
            subject_name = subject_name + (('CN', MBSTRING_ASC, cn, -1, -1, -1),)
        name = X509.X509_Name()
        for entry in subject_name:
            name.add_entry_by_txt(*entry)
        req.set_subject(name)
        req.set_pubkey(k)
        req.sign(k, 'sha1')
        reqpem = req.as_pem()
        Engine.cleanup()
        return reqpem
    

    以下是将证书写入安全令牌的代码:

    def write_certificate(self, cert_pem, so_pin):
        """Write certificate to token
        @param cert_pem: certificate in pem format
        @type cert_pem: str
        @param so_pin: PIN code of security officer
        @type so_pin: str
        """
        cert = X509.load_cert_string(cert_pem)
        if cert.check_ca(): # What label to use?
            label = TOKEN_CA_CERT_LABEL
        else:
            label = TOKEN_USER_CERT_LABEL
        tCert = (
            (PyKCS11.LowLevel.CKA_CLASS, PyKCS11.LowLevel.CKO_CERTIFICATE),
            (PyKCS11.LowLevel.CKA_CERTIFICATE_TYPE, PyKCS11.LowLevel.CKC_X_509),
            (PyKCS11.LowLevel.CKA_TOKEN, True),
            (PyKCS11.LowLevel.CKA_PRIVATE, False),
            (PyKCS11.LowLevel.CKA_LABEL, label),
            (PyKCS11.LowLevel.CKA_ID, make_key_id(cert.get_pubkey())),
            (PyKCS11.LowLevel.CKA_SUBJECT, cert.get_subject().as_der()),
            (PyKCS11.LowLevel.CKA_ISSUER, cert.get_issuer().as_der()),
            (PyKCS11.LowLevel.CKA_SERIAL_NUMBER, cert.get_serial_number()),
            (PyKCS11.LowLevel.CKA_VALUE, cert.as_der()))
        s = self.lib.openSession(self.slot, PyKCS11.CKF_RW_SESSION)
        s.login(so_pin, PyKCS11.LowLevel.CKU_SO)
        s.createObject(tCert)
        s.logout()
        s.closeSession()
    

    问题是请求生成后我得到CKR_USER_ANOTHER_ALREADY_LOGGED_IN错误。我查看了engine_pkcs11源代码,并在engine_pkcs11.c(https://github.com/OpenSC/engine_pkcs11/blob/master/src/engine_pkcs11.c)文件中有一个名为static EVP_PKEY *pkcs11_load_key的函数。这很长,所以这是其中的一部分:

        /* Now login in with the (possibly NULL) pin */
        if (PKCS11_login(slot, 0, pin)) {
            /* Login failed, so free the PIN if present */
            if (pin != NULL) {
                OPENSSL_cleanse(pin, pin_length);
                free(pin);
                pin = NULL;
                pin_length = 0;
            }
            fail("Login failed\n");
        }
    

    因此,据我所知,当使用密钥时执行登录(PKCS#10请求生成需要密钥)。如果执行登录,那么我相信也应该执行相应的注销,但我找不到。以下是ENGINE_finish()函数的来源:

    int pkcs11_finish(ENGINE * engine)
    {
        if (ctx) {
        PKCS11_CTX_unload(ctx);
        PKCS11_CTX_free(ctx);
        ctx = NULL;
    }
    if (pin != NULL) {
        OPENSSL_cleanse(pin, pin_length);
        free(pin);
        pin = NULL;
        pin_length = 0;
    }
    return 1;
    }
    

    是否有可能以某种方式(可能隐含地)在步骤2中从安全令牌注销?

1 个答案:

答案 0 :(得分:1)

最后,我可以这样做:

在加载引擎之前,我打开新会话并登录 在我完成工作后,我关闭会话(使用closeSession()是不够的)

所以请求生成就像这样。

    s = self.lib.openSession(self.slot, PyKCS11.CKF_RW_SESSION)
    s.login(user_pin)
    Engine.load_dynamic()

... 我在发动机清理后添加了

    a.finish()
    Engine.cleanup()        
    s.logout()
    s.closeAllSessions()

我还必须在PyKCS11源代码中对Session类进行一些修补,因为有一个拼写错误(closeAllSession而不是closeAllSessions):

def closeAllSessions(self):
    """
    C_CloseAllSessions
    """
    rv = self.lib.C_CloseAllSessions(self.slot)
    if rv != CKR_OK:
        raise PyKCS11Error(rv)

希望这有帮助