3

l used java to create a pkcs12 keystore where l stored two keys, now l want to retrieve these keys using python. ln java l load the keystore and use keystore.getkey(keyalias). how can l do that with python?

  • l first tried pyjks but it wasn't supporting pkcs12 keystores
  • then l tried pyopenssl but l the documentation didn't mention a method to actually to load an existing keystore.
  • l also l found this piece of code from an old post here:
# 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.

but the get_privatekey() doesn't accept args which means that can't set the key alias that l want to retrieve the same way l do in 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 Answers2

1

One question before I begin, are the two keys that you are starting with "secret keys" (aka symmetric), or are they "private keys"?

If your keys are "private keys", you should be able to convert the PKCS12 keystore to a JKS keystore, and read the private keys using pyjks.

To convert a keystore format from the command line on RHEL7, use the following:

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

If your keys are "secret keys", the JKS format doesn't support "secret key" entry types. Therefore, try to convert your keystore from the PKCS12 format to a JCEKS format keystore. The JCEKS format is one that both

  • Supports "secret key" entry types
  • Is claimed to be supported by pyjks

To convert a keystore format from the command line on RHEL7, use the following:

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

This new JCEKS format keystore should now be able to get read in via python using pyjks

M Ross
  • 29
  • 4
0

I know this is an older post, but I wanted to answer the direct question of - how to pass an alias to pyopenssl to get a private key. I still think that the other answer is a viable solution, but maybe a bit more context would be helpful for some.


The short answer, unfortunately, is that you can't specify the alias for a specific key in openssl with python. PyOpenSSL uses cryptography underneath to load a pkcs12 file

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) ...

the lib.X509_alias_get0(X509*, int*) function comes from the cryptography library (which actually is a C function from _openssl.lib as far as I can tell. Here is its usage in the openssl code base and definition here).

In any case, it doesn't look like the API of pyOpenSSL would allow to specify an alias. There is nothing in their documentation to suggest that, nor in their issue tracker.

But what about using cryptography directly. PyOpenSSL does warn us that it's a good idea to use the direct api in any case...

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

Sure, we can re-do the steps that pyOpenSSL does and load our .p12 file, but oh no...

The cryptography documentations has a warning for us

cryptography only supports a single private key and associated certificates when parsing PKCS12 files at this time.

And in fact, it does seem we can't currently load different keys with different aliases through these libraries.


Why tho?

I can only assume that the reason this is done is because most .p12 users have only a single certificate and a single private key that match against one alias in their file. That is, they don't use the functionality of this specific standard to hold multiple pairs.

I'm judging this based on the wiki

But in practice it is normally used to store just one private key and its associated certificate chain.

PKCS #12 files are usually created using OpenSSL, which only supports a single private key from the command line interface.


However - if you only have one private key and certificates and you still want to make sure that the alias for those are correct, pyOpenSSL does have a method get_friendlyname(), which returns the alias of the loaded file. It's not really that helpful in your specific case (OP), but it can be done as an additional check.

mnestorov
  • 4,116
  • 2
  • 14
  • 24