如何使用python加载pkcs12密钥库

时间:2020-03-24 18:22:06

标签: python pyopenssl

l使用Java创建了一个pkcs12密钥库,其中存储了两个密钥,现在我想使用python检索这些密钥。在Java中,加载密钥库并使用keystore.getkey(keyalias)。我怎么用python做到这一点?

  • 我首先尝试了pyjks,但它不支持pkcs12密钥库
  • 然后我尝试了pyopenssl,但是l文档中没有提到实际加载现有密钥库的方法。
  • 我也从这里的旧帖子中找到了这段代码:
# load OpenSSL.crypto
from OpenSSL import crypto

# open it, using password. Supply/read your own from stdin.
p12 = crypto.load_pkcs12(open("/path/to/cert.p12", 'rb').read(), passwd)

# get various properties of said file.
# note these are PyOpenSSL objects, not strings although you
# can convert them to PEM-encoded strings.
p12.get_certificate()     # (signed) certificate object
p12.get_privatekey()      # private key.
p12.get_ca_certificates() # ca chain.

,但是 get_privatekey()不接受args,这意味着无法设置l想要检索的别名别名,就像我在java中一样:

public static SecretKey getEntry(KeyStore keyStore, String keyAlias, Optional<String> password) {
        SecretKey key = null;

        try {
            key = (SecretKey) keyStore.getKey(keyAlias, password.orElse("").toCharArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return key;
    }

2 个答案:

答案 0 :(得分:0)

在我开始之前的一个问题是,您是用“秘密密钥”(又称对称密钥)开头的两个密钥,还是“私钥”?

如果您的密钥是“私有密钥”,则应该能够将PKCS12密钥库转换为JKS密钥库,并可以使用pyjks读取私有密钥。

要从RHEL7上的命令行转换密钥库格式,请使用以下命令:

keytool -importkeystore -srckeystore <path_to_your_pkcs12_keystore> -srcstoretype PKCS12 -deststoretype JKS -destkeystore <path_to_new_jks_keystore>

如果您的密钥是“秘密密钥”,则JKS格式不支持“秘密密钥”条目类型。因此,尝试将您的密钥库从PKCS12格式转换为JCEKS格式的密钥库。 JCEKS格式是

  • 支持“秘密密钥”条目类型
  • 据称受pyjks支持

要从RHEL7上的命令行转换密钥库格式,请使用以下命令:

keytool -importkeystore -srckeystore <path_to_your_pkcs12_keystore> -srcstoretype PKCS12 -deststoretype JCEKS -destkeystore <path_to_new_jceks_keystore>

这个新的JCEKS格式密钥库现在应该可以使用pyjks通过python读取

答案 1 :(得分:0)

我知道这是一篇较旧的帖子,但我想回答以下直接问题 - 如何将别名传递给 pyopenssl 以获取私钥。我仍然认为另一个答案是一个可行的解决方案,但也许更多的上下文对某些人会有所帮助。


不幸的是,简短的回答是您无法使用 python 为 openssl 中的特定键指定别名。 PyOpenSSL 在下面使用 cryptography 加载 pkcs12 文件

def load_pkcs12(buffer, passphrase=None):
    """
    Load pkcs12 data from the string *buffer*. If the pkcs12 structure is
    encrypted, a *passphrase* must be included.  The MAC is always
    checked and thus required.
    See also the man page for the C function :py:func:`PKCS12_parse`.
    :param buffer: The buffer the certificate is stored in
    :param passphrase: (Optional) The password to decrypt the PKCS12 lump
    :returns: The PKCS12 object
    """
    ... (omitting a lot of code) ...
    if cert[0] == _ffi.NULL:
        pycert = None
        friendlyname = None
    else:
        pycert = X509._from_raw_x509_ptr(cert[0])

        friendlyname_length = _ffi.new("int*")
        friendlyname_buffer = _lib.X509_alias_get0(
            cert[0], friendlyname_length
        )
        friendlyname = _ffi.buffer(
            frien
dlyname_buffer, friendlyname_length[0]
        )[:]
        if friendlyname_buffer == _ffi.NULL:
            friendlyname = None
    ... (more code to omit) ...

lib.X509_alias_get0(X509*, int*) 函数来自 cryptography 库(据我所知,它实际上是来自 C_openssl.lib 函数。这是它在 { {3}} 和 openssl code base)。

无论如何,pyOpenSSL 的 API 似乎不允许指定别名。他们的文档中没有任何提示,他们的问题跟踪器中也没有。

但是直接使用 cryptography 怎么样。 PyOpenSSL 确实警告我们在任何情况下使用直接 api 是个好主意...

<stdin>:1: DeprecationWarning: PKCS#12 support in pyOpenSSL is deprecated. You should use the APIs in cryptography.

当然,我们可以重新执行 pyOpenSSL 执行的步骤并加载我们的 .p12 文件,但是哦不...

definition here 向我们发出警告

<块引用>

此时解析 PKCS12 文件时,密码学仅支持单个私钥和相关证书。

事实上,我们目前似乎无法通过这些库加载具有不同别名的不同键。


为什么要这样做?

我只能假设这样做的原因是因为大多数 .p12 用户只有一个证书和一个私钥,与他们文件中的一个别名相匹配。也就是说,他们不使用这个特定标准的功能来保存多对。

我是根据 cryptography documentations

来判断的 <块引用>

但实际上它通常只用于存储一个私钥及其相关的证书链。

PKCS #12 文件通常使用 OpenSSL 创建,它仅支持来自命令行界面的单个私钥。


但是 - 如果您只有一个私钥和证书,并且您仍想确保它们的别名正确无误,pyOpenSSL 确实有一个方法 get_friendlyname(),它返回加载文件的别名。这对您的具体情况 (OP) 来说真的没有多大帮助,但可以作为额外检查来完成。