Is it possible to generate correct PKCS12 (.pfx) file in Python?

Question:

I need to generate a PKCS12 file in python that will contain self-signed certificate and private key for it. I assembled the following python code for this task:

import OpenSSL
key = OpenSSL.crypto.PKey()
key.generate_key( OpenSSL.crypto.TYPE_RSA, 1024 )
cert = OpenSSL.crypto.X509()
cert.set_serial_number(0)
cert.get_subject().CN = "me"
cert.set_issuer( cert.get_subject() )
cert.gmtime_adj_notBefore( 0 )
cert.gmtime_adj_notAfter( 10*365*24*60*60 )
cert.set_pubkey( key )
cert.sign( key, 'md5' )
open( "certificate.cer", 'w' ).write( 
  OpenSSL.crypto.dump_certificate( OpenSSL.crypto.FILETYPE_PEM, cert ) )
open( "private_key.pem", 'w' ).write( 
  OpenSSL.crypto.dump_privatekey( OpenSSL.crypto.FILETYPE_PEM, key ) )
p12 = OpenSSL.crypto.PKCS12()
p12.set_privatekey( key )
p12.set_certificate( cert )
open( "container.pfx", 'w' ).write( p12.export() )

This code creates a .cer file that i can view in Windows and that seems correct. It also creates a “.pfx” file that is intended to be a “PKCS#12” container with certificate and corresponding private key – a thing needed to sign executables. Unfortunately, if i try to open this “.pfx” file on Windows it fails with “file is invalid” error, and parsing it via command-line tool also fails:

certutil -asn container.pfx

Fails with “decode error” at the middle of the file.

Is it something i’m doing wrong in my code or Python + OpenSSL are not intended to create valid PKCS#12 files under Windows?

P.S. I’m using latest ActivePython 2.7 32-bit distribution.

Asked By: grigoryvp

||

Answers:

I have an assumption, that you need to open container.pfx in binary mode:

open( "container.pfx", 'wb' ).write( p12.export() )
Answered By: Andrey Atapin
from datetime import datetime

from cryptography import x509
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import pkcs12, PrivateFormat

common_name = "John Doe"
password = "secret"
one_day = datetime.timedelta(1, 0, 0)

private_key = rsa.generate_private_key(
     public_exponent=65537,
     key_size=4096,
)
public_key = private_key.public_key()

builder = x509.CertificateBuilder()
builder = builder.subject_name(
    x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, u"John Doe")])
)
builder = builder.issuer_name(x509.Name([
     x509.NameAttribute(NameOID.COMMON_NAME, u'John Doe'),
]))
builder = builder.issuer_name(ca.issuer)
builder = builder.not_valid_before(datetime.datetime.today() - one_day)
builder = builder.not_valid_after(datetime.datetime.today() + (one_day * 30))
builder = builder.serial_number(x509.random_serial_number())
builder = builder.public_key(public_key)

cert = builder.sign(
    private_key=private_key, algorithm=hashes.SHA256())

encryption = (
     PrivateFormat.PKCS12.encryption_builder().
     kdf_rounds(50000).
     key_cert_algorithm(pkcs12.PBES.PBESv1SHA1And3KeyTripleDESCBC).
     hmac_hash(hashes.SHA1()).build(f"{password}".encode())
)

p12 = pkcs12.serialize_key_and_certificates(
    f"{common_name}".encode(), private_key, cert, None, encryption
)


with open(f"{common_name}.p12", "wb") as p12_file:  # .p12 and .pfx are the same
    p12_file.write(p12)
Answered By: ikreb
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.