22

I have a valid certificate issued by the spanish authority (FNMT) and I want to play with it to learn more about it. The file has extension .p12

I would like to read the information in it (first and last name) and check if the certificate is valid. Is it possible to do that with pyOpenSSL? I guess I have to use the crypto module in OpenSSL. Any help or useful link? Trying reading here: http://packages.python.org/pyOpenSSL/openssl-crypto.html but not much information :-(

nemesisdesign
  • 8,159
  • 12
  • 58
  • 97

3 Answers3

48

It's fairly straight-forward to use. This isn't tested, but should work:

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

For more examples, have a look through the unit test code of pyopenssl. Pretty much every way you might want to use the library is there

See also here or without adverts here.

jrial
  • 478
  • 4
  • 10
  • 1
    Source link without advertisements: http://bazaar.launchpad.net/~exarkun/pyopenssl/trunk/view/head:/OpenSSL/test/test_crypto.py :) – Jean-Paul Calderone Jun 14 '11 at 18:14
  • 1
    Sorry, I don't see adverts with Adblock... Edited in. –  Jun 14 '11 at 18:37
  • This is always printing None. get_certificate, get_privatekey, etc. always returns None. Did it work for anyone ? – ssal Mar 26 '13 at 10:51
  • 2
    How about python3 solution? I'm having problem with load_pkcs12, because there's no file command now and any manoeuvres I've tried with open() give me lot's of errors. : ( when trying: `p12 = load_pkcs12(open('foo.p12', 'rb').read(), passwd)` I'm receiving: `OpenSSL.crypto.Error: [('asn1 encoding routines', 'ASN1_get_object', 'header too long')]` – Stan Mar 28 '14 at 05:01
  • @Stan - are you sure your .p12 file is in the correct format? Try opening it with the openssl tool: `openssl pkcs12 -noout -info -in foo.p12` and see what output you get. – jrial Jun 02 '16 at 13:17
  • ```c = p12.get_certificate() # (signed) certificate object print(c, p, ca) print('版本', c.get_version()) print('签名算法', c.get_signature_algorithm()) print('序列号:', c.get_serial_number()) print('证书是否过期:', c.has_expired()) print('在此之前无效:', c.get_notBefore()) print('在此之后无效', c.get_notAfter()) #主题名称 subject = c.get_subject() print(subject.get_components()) #签发者名称 suer = x509name = c.get_issuer() print(suer.get_components()) #扩展 print('扩展数:', c.get_extension_count()) print('扩展1:', c.get_extension(0)) ``` – iHTCboy Jun 27 '19 at 03:15
13

As pyOpenSSL.crypto.load_pkcs12 is now deprecated, here is the equivalent solution using cryptography, with loading inside a requests Session as a bonus.

from cryptography.hazmat.primitives import serialization
from requests import Session

with open("./cert.p12", "rb") as f:
    (
        private_key,
        certificate,
        additional_certificates,
    ) = serialization.pkcs12.load_key_and_certificates(
        f.read(), CLIENT_CERT_KEY.encode()
    )
# key will be available in user readable temporary file for the time of the
# program run (until key and cert get gc'ed)
key = tempfile.NamedTemporaryFile()
cert = tempfile.NamedTemporaryFile()
key.write(
    private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.PKCS8,
        encryption_algorithm=serialization.NoEncryption(),
    )
)
key.flush()
cert.write(
    certificate.public_bytes(serialization.Encoding.PEM),
)
cert.flush()
session = Session()
session.cert = (cert.name, key.name)
tardyp
  • 1,142
  • 11
  • 9
  • 3
    This worked, I just had to add a manual import of the method (`import cryptography.hazmat.primitives.serialization.pkcs12`), otherwise I get an attribute error (`AttributeError: module 'cryptography.hazmat.primitives.serialization' has no attribute 'pkcs12'`) – Luisg123v Feb 11 '21 at 01:10
3

Maybe is wrong answering to an old Q, but I thought that it may help someone that find this Q after me. This solution work for python 3, and I think is a little bit better. I found it in the repo of zeep and is a class to encapsule the usage.

Class

import os
from OpenSSL import crypto

class PKCS12Manager():

    def __init__(self, p12file, passphrase):
        self.p12file = p12file
        self.unlock = passphrase
        self.webservices_dir = ''
        self.keyfile = ''
        self.certfile = ''

        # Get filename without extension
        ext = os.path.splitext(p12file)
        self.filebasename = os.path.basename(ext[0])

        self.createPrivateCertStore()
        self.p12topem()

    def getKey(self):
        return self.keyfile

    def getCert(self):
        return self.certfile

    def createPrivateCertStore(self):
        home = os.path.expanduser('~')
        webservices_dir = os.path.join(home, '.webservices')
        if not os.path.exists(webservices_dir):
            os.mkdir(webservices_dir)
        os.chmod(webservices_dir, 0o700)
        self.webservices_dir = webservices_dir

    def p12topem(self):
        p12 = crypto.load_pkcs12(open(self.p12file, 'rb').read(), bytes(self.unlock, 'utf-8'))

        # PEM formatted private key
        key = crypto.dump_privatekey(crypto.FILETYPE_PEM, p12.get_privatekey())

        self.keyfile = os.path.join(self.webservices_dir, self.filebasename + ".key.pem")
        open(self.keyfile, 'a').close()
        os.chmod(self.keyfile, 0o600)
        with open(self.keyfile, 'wb') as f:
            f.write(key)


        # PEM formatted certificate
        cert = crypto.dump_certificate(crypto.FILETYPE_PEM, p12.get_certificate())

        self.certfile = os.path.join(self.webservices_dir, self.filebasename + ".crt.pem")
        open(self.certfile, 'a').close()
        os.chmod(self.certfile, 0o644)
        with open(self.certfile, 'wb') as f:
            f.write(cert)

Usage

from requests import Session
from zeep import Client
from zeep.transports import Transport

# https://github.com/mvantellingen/python-zeep/issues/824
pkcs12 = PKCS12Manager('cert.p12', 'password_for_cert')
session = Session()
session.cert = (pkcs12.getCert(), pkcs12.getKey())

transport = Transport(session=session)
client = Client('url_service', transport=transport)
set92
  • 322
  • 4
  • 13