Skip to content

Instantly share code, notes, and snippets.

@markscottwright
Last active July 17, 2024 02:03
Show Gist options
  • Save markscottwright/86f29f8d4e9e02d2533efced6cb24083 to your computer and use it in GitHub Desktop.
Save markscottwright/86f29f8d4e9e02d2533efced6cb24083 to your computer and use it in GitHub Desktop.
expand a pkcs12 file into certificate and key files in PEM format, in python
# require cryptography and asn1crypto
import base64
import os
from asn1crypto.keys import PrivateKeyInfo
from asn1crypto.pkcs12 import Pfx, SafeContents
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
def decrypt_certificates(encrypted_content_info, password: bytes):
kdf = encrypted_content_info["content_encryption_algorithm"]['parameters']['key_derivation_func']
key_length = kdf['parameters']['key_length'].native
salt = kdf['parameters']['salt'].native
iteration_count = kdf['parameters']['iteration_count'].native
ciphertext = encrypted_content_info['encrypted_content'].native
iv = encrypted_content_info["content_encryption_algorithm"]['parameters']['encryption_scheme']['parameters'].native
plaintext = decrypt_pbes2(password, salt, iteration_count, key_length, iv, ciphertext)
for safebag in SafeContents.load(plaintext):
cert = safebag["bag_value"]["cert_value"].parsed
cert_der = cert.dump()
friendly_name = local_key_id = 'unknown'
for attr in safebag["bag_attributes"].native:
if attr['type'] == 'friendly_name':
friendly_name = attr["values"][0]
if attr['type'] == 'local_key_id':
local_key_id = attr["values"][0]
yield friendly_name, local_key_id, cert_der
def decrypt_pbes2(password: bytes, salt: bytes, iteration_count: int, key_length: int, iv: bytes, ciphertext: bytes):
key = PBKDF2HMAC(algorithm=SHA256(), salt=salt, iterations=iteration_count, length=key_length).derive(
password)
decryptor = Cipher(algorithms.AES(key), modes.CBC(iv)).decryptor()
plaintext = decryptor.update(ciphertext) + decryptor.finalize()
return plaintext
def main(pkcs12_file, password, outdir):
if isinstance(password, str):
password = password.encode("utf-8")
with open(pkcs12_file, "rb") as input_file:
p12bytes = input_file.read()
# https://datatracker.ietf.org/doc/html/rfc7292
p12 = Pfx.load(p12bytes)
for content_info in p12.authenticated_safe:
if content_info["content_type"].native == 'encrypted_data':
encrypted_content_info = content_info["content"]["encrypted_content_info"]
for friendly_name, local_key_id, cert_der in decrypt_certificates(encrypted_content_info, password):
filename = outdir + "/" + friendly_name + ".pem"
print(f"writing certificate {filename}")
with open(filename, "wb") as f:
f.write(b"-----BEGIN CERTIFICATE-----\n" + base64.encodebytes(
cert_der) + b"-----END CERTIFICATE-----\n")
elif content_info["content_type"].native == 'data':
sc = SafeContents.load(content_info["content"].native)
for safebag in sc:
friendly_name = "unknown"
for attr in safebag["bag_attributes"].native:
if attr['type'] == 'friendly_name':
friendly_name = attr["values"][0]
# if attr['type'] == 'local_key_id':
# local_key_id = attr["values"][0]
if safebag["bag_id"].native == "cert_bag":
cert_der = safebag["bag_value"]["cert_value"].parsed.dump()
filename = outdir + "/" + friendly_name + ".pem"
print(f"writing certificate {filename}")
with open(filename, "wb") as f:
f.write(b"-----BEGIN CERTIFICATE-----\n" + base64.encodebytes(
cert_der) + b"-----END CERTIFICATE-----\n")
elif safebag["bag_id"].native == "pkcs8_shrouded_key_bag":
enc_alg = safebag["bag_value"]["encryption_algorithm"]
assert enc_alg["algorithm"].native == 'pbes2'
assert enc_alg["parameters"]["key_derivation_func"]["algorithm"].native == 'pbkdf2'
assert enc_alg["parameters"]["encryption_scheme"]["algorithm"].native == "aes256_cbc"
safe_contents_bytes = decrypt_pbes2(
password,
enc_alg["parameters"]["key_derivation_func"]["parameters"]["salt"].native,
enc_alg["parameters"]["key_derivation_func"]["parameters"]["iteration_count"].native,
enc_alg["parameters"]["key_derivation_func"]["parameters"]["key_length"].native,
enc_alg["parameters"]["encryption_scheme"]["parameters"].native,
safebag["bag_value"]["encrypted_data"].native)
private_key = PrivateKeyInfo.load(safe_contents_bytes)
filename = outdir + "/" + friendly_name + ".key"
print(f"writing key {filename}")
with open(filename, "wb") as f:
f.write(b"-----BEGIN PRIVATE KEY-----\n" + base64.encodebytes(
private_key.dump()) + b"-----END PRIVATE KEY-----\n")
os.makedirs("./out", exist_ok=True)
main("test.p12", "foo123", "./out")
# main("c:/Program Files/Eclipse Adoptium/jdk-21.0.1.12-hotspot/lib/security/cacerts", "changeit", "./out")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment