Reputation: 22282
How do/can I generate a PKCS#12 file using python and the cryptography module?
It's pretty easy using said module to generate the contents of .pem file for a private key:
keyPEMBytes = privateKey.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption())
Also easy to generate the contents of a .cer/.pem file for an associated cert:
certBytes = certificate.public_bytes(encoding=serialization.Encoding.PEM)
But I need them (and their chain) balled up on a single .p12 (PKCS12 file). Said module documents how to parse/consume PKCS12 formats, but nothing (that I can find) about how one can generate them.
My understanding of PKI stuff is hit and miss though, so maybe I'm just not searching the right keyword in the documentation?
I can create the .p12 file at the command line on Linux using
openssl pkcs12 -export -out myIdentity.p12 -inkey myPrivKey.pem -in myCert.crt -certfile myCertChain.crt
So I could just wrap calls like this with subprocess/cmd and mess with tempfiles/pipes. I was hoping to keep it all in memory/python though.
Is there a different python TLS library that I should be considering, that can do this?
Upvotes: 14
Views: 15496
Reputation: 2785
This use the python module cryptography
and generate a p12 file:
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"
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=4096,
)
cert = x509.load_pem_x509_certificate("cert.pem")
encryption = (
PrivateFormat.PKCS12.encryption_builder().
kdf_rounds(50000).
key_cert_algorithm(pkcs12.PBES.PBESv1SHA1And3KeyTripleDESCBC).
hmac_hash(hashes.SHA256()).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_file.write(p12)
You have to use PBESv1SHA1And3KeyTripleDESCBC
because the Windows and macOS certificate store only supports 3DES.
Upvotes: 4
Reputation: 11
Quite annoying that there isn't a clear example documented in the cryptography module. Here's the magic incantation to convert pem files (cert + key) into a p12 file protected by a password.
from cryptography import x509
from cryptography.hazmat.primitives.serialization import BestAvailableEncryption, load_pem_private_key, pkcs12
hostname = "host.domain.com"
crt_file = "cert.pem"
key_file = "privkey.pem"
p12_file = 'host.p12'
password = "password"
with open(crt_file, mode='rb') as file:
crt = x509.load_pem_x509_certificate(file.read())
with open(key_file, mode='rb') as file:
key = load_pem_private_key(file.read(), None)
with open(p12_file, 'wb') as file:
file.write(
pkcs12.serialize_key_and_certificates(
hostname.encode(), key, crt, None,
BestAvailableEncryption(password.encode())))
Upvotes: 1
Reputation: 14089
As you noted, cryptography
can parse PKCS12 (at least the subset used by 99.99% of people) with load_key_and_certificates, but serialization to PKCS12 is not currently supported.
I'm one of the core developers for the project and in general cryptography
's feature set is driven by users filing issues that explain their use case and need for a particular feature. I'd recommend writing something up on the tracker for discussion. It sounds like your needs would be covered by a simple API looking roughly like:
from cryptography.hazmat.primitives.serialization.pkcs12 import generate_pkcs12
pem_pkcs12 = generate_pkcs12(
BestAvailableEncryption(b"somepassword"),
key,
[cert1, cert2]
)
Update: this feature is implemented in pyca/cryptography 3.0:
Upvotes: 19
Reputation: 29
#This assumes you have server.key and server.pem already created
import sys
import os
from OpenSSL import crypto
try:
PFX_Passcode = os.environ['PFX_Passcode']
except KeyError:
sys.exit('One or more required environment variables not set, required: PFX_Passcode')
with open('server.key', "r") as f:
privkeydata = f.read()
with open('server.pem', 'r') as f:
certdata = f.read()
cert = crypto.load_certificate(crypto.FILETYPE_PEM, certdata)
privkey = crypto.load_privatekey(crypto.FILETYPE_PEM, privkeydata)
p12 = crypto.PKCS12()
p12.set_privatekey(privkey)
p12.set_certificate(cert)
p12data = p12.export(PFX_Passcode)
with open('server.pfx', 'wb') as pfxfile:
pfxfile.write(p12data)
Upvotes: 2
Reputation: 34677
Perhaps the code here might be useful? It's not the cryptography module -- it's chilkat, but it does the job.
import sys
import chilkat
# First load our certificate into a certificate object,
# and then get it as a cert chain object.
cert = chilkat.CkCert()
success = cert.LoadFromFile("qa_data/rsaes-oaep/cert.pem")
if (success != True):
print(cert.lastErrorText())
sys.exit()
# This is the certificate PEM that we loaded:
# -----BEGIN CERTIFICATE-----
# MIIDozCCAougAwIBAgIJAMRwugDmvniwMA0GCSqGSIb3DQEBCwUAMGgxCzAJBgNV
# BAYTAlVTMQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHV2hlYXRvbjEhMB8GA1UECgwY
# SW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRcwFQYDVQQDDA5DaGlsa2F0V2lkZ2V0
# czAeFw0xNzA0MTgxMzQxNDVaFw0yMjA0MTcxMzQxNDVaMGgxCzAJBgNVBAYTAlVT
# MQswCQYDVQQIDAJJTDEQMA4GA1UEBwwHV2hlYXRvbjEhMB8GA1UECgwYSW50ZXJu
# ZXQgV2lkZ2l0cyBQdHkgTHRkMRcwFQYDVQQDDA5DaGlsa2F0V2lkZ2V0czCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMXhMR7TNHSaHgrEGvAmHNqw/8xV
# 5TcngVO//yHBniDHas5b2zm0AkhY5EW9dn0wTWHivoRZC2soH7/bxRi4uYUaxgve
# YLXw6DO2MHSpuTvhSG3+AjsBDa9kXegn9XT1MdPHk9PrHR0sKXGuimkLf4r+Q1oM
# iGlhbUyRATwVlmo6AndgniDnj2RFBYV8E8SQ+7SnLPJdXZRDbLNVY/DY6D9stRAY
# rHuz/WBlVW7TQVuWwe7Cd9bfFqjqzLwLksKevWvHGteYDGeiCi+uvMMNfp/Br79Y
# zJYF/YpTJYE0POBiMwth1FUIYKpZf/O2eUd6RW9h6hocC5QWU9LAq+7kKFMCAwEA
# AaNQME4wHQYDVR0OBBYEFKBCsni1BfOyH3dSoY8yL4mDeNbpMB8GA1UdIwQYMBaA
# FKBCsni1BfOyH3dSoY8yL4mDeNbpMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
# BQADggEBAF2iV69ma5XegVWpxbc0gui63RE8NjW2dBW0fa4a4fwNH2f8oetZEjw4
# kscx7p4ESxyPtDUkLNplAth+D8NNh9IQDWIld+FZrJsis4tkpVcpMsbmB2CW8fL2
# IVnVznAN+/PMYEAkeSOnovUGlREHZZu4b9rMVm89+aoCQ9z+byjW9kLKHyvLnUai
# uEHp+0EVFP91CRSIcTCXsW1c5yZ8k5bqL0HlauuOO4TA3IegyYM9xBubmcxg8cRO
# 2F1k+Ge1lu3e+WY+lihaO0p762dF6g/SUMMrFneCPdIhvt4u7Esc9FDxE3xZEq3x
# cS5sHTxtTM0LVBi378/x5m1hln5hSJU=
# -----END CERTIFICATE-----
# Get it as a certificate chain.
# certChain is a CkCertChain
certChain = cert.GetCertChain()
if (cert.get_LastMethodSuccess() != True):
print(cert.lastErrorText())
sys.exit()
# Next, load the corresponding private key from a PEM.
privKey = chilkat.CkPrivateKey()
success = privKey.LoadPemFile("qa_data/rsaes-oaep/privateKey.pem")
if (success != True):
print(privKey.lastErrorText())
sys.exit()
# This is the private key PEM:
# -----BEGIN RSA PRIVATE KEY-----
# MIIEpAIBAAKCAQEAxeExHtM0dJoeCsQa8CYc2rD/zFXlNyeBU7//IcGeIMdqzlvb
# ObQCSFjkRb12fTBNYeK+hFkLaygfv9vFGLi5hRrGC95gtfDoM7YwdKm5O+FIbf4C
# OwENr2Rd6Cf1dPUx08eT0+sdHSwpca6KaQt/iv5DWgyIaWFtTJEBPBWWajoCd2Ce
# IOePZEUFhXwTxJD7tKcs8l1dlENss1Vj8NjoP2y1EBise7P9YGVVbtNBW5bB7sJ3
# 1t8WqOrMvAuSwp69a8ca15gMZ6IKL668ww1+n8Gvv1jMlgX9ilMlgTQ84GIzC2HU
# VQhgqll/87Z5R3pFb2HqGhwLlBZT0sCr7uQoUwIDAQABAoIBAGCP5LWDIWzpLFHa
# or6gCqKZjyo6nFFO4Ohqn+jsH+slBTTQVGmTMy302uhBbYnnwUtMJ+ZTwaO3/85T
# Q5otwrJ2f0CZcx42AkoB1SGJFVBoPj0WoCYE/JWjZ8P7g+dgI8GR+cyHRjzPKSZF
# o1thdgrwyxMtXH/4QCsF89FLQ4xwCJUkj+w0KBkX8isWcBJnKip81MK8rxhgMJ6v
# 5+DBVvlvm4BRz0hva7HB3TaZgo9ZWZdoSJQ/l33yfIL8H0EJjC6uJ/Bn77JtDzcv
# 1oUF02Ebu8/Re7RpUqdTIb1GamrOCjuZN66SrCOsh9rRzueU3UTLzjb9FDv8FQDA
# 5Dm59KECgYEA6zJ2LaiUywth80rAC34jEF85aC3vyKK0MxYELLB9Nw6MNFD1PZly
# YkOcI2kxVV+1KqtRYFlfEpCL4flqVnqjVj5jTCCOXqO6RjnPLoupf0yLpj60raO6
# Ouw5EDGjfUDBQtkCQNOx0nZLftVy30Ck/V2gVw9sEgWQV0YRavuymKMCgYEA12HB
# eHHSo/dK7keiIK7RRQu2ibALXWXsGq9lUbnQAunKRCa3qQtc8OwGRZsdj9u8HwCB
# zDu5QIbaQCa1SyDz1CsOysOfTYNhh1SZ4GLiu7/b0v5ktgdtosT9niowo08Y4ZU+
# lA8p2eriylznacNOkA8O1g8m1etHqV+cVKUMvJECgYEApoeVO6DosZd+pCfiMIEA
# TMBPwl78L6BcXOuSyx2vmwMW7JgB/LqgTck3Ta9Wue9JNEwXE1RUR63HDZpu24p8
# Cj2kSyumAoaFBuI5uugyfCrzmiM+BNAvtTa69Kdg7feinzVOLh52AuG1dLmnW0HH
# UIoxVFNcHqqH1/OJs6lBfGcCgYEAkQXhn5gEnSMF8SuKnzcW8cbRQiSnglv+fnlb
# X4wd49hHuVvYjpp4GkDSeEvZu+PY54cP3tTYB/rFOizWJaAqjdHwQ9c0jJzKLrVo
# Zw4uXPzypz9j0K1vxQmfO8Bsv4mi10IGwOr04yalTlz/9NbtL1L0Sm4pWCD0P7eB
# K/12dgECgYBXNV5NcLpZhpjo8q05xAiFuwC2e29qYyVwcFVhYdYguutRKImZtjSE
# dndNAxa7EDL9NRc5SjX4NHAZoImADRSbgc2rIq2ePY8WORaR+iKsVx7PJGHSiXTL
# 30X9E3com8ctyTsZTnlJ4Dm7SefkN0NKzqwjxXxZLaUteWBPUt+YAg==
# -----END RSA PRIVATE KEY-----
# Create a PFX object instance, and add the private key + cert chain.
pfx = chilkat.CkPfx()
success = pfx.AddPrivateKey(privKey,certChain)
if (success != True):
print(pfx.lastErrorText())
sys.exit()
# Finally, write the PFX w/ a password.
success = pfx.ToFile("pfxPassword","qa_output/sample.pfx")
if (success != True):
print(pfx.lastErrorText())
sys.exit()
print("Success.")
Upvotes: 2