72 lines
2.3 KiB
Python
72 lines
2.3 KiB
Python
#!python3
|
|
# dlitz 2025
|
|
|
|
from contextlib import ExitStack, contextmanager
|
|
import multiprocessing
|
|
import subprocess
|
|
import os
|
|
import re
|
|
|
|
class PKCS12Exporter:
|
|
|
|
openssl_prog = "openssl"
|
|
|
|
def export_pkcs12(self, privkey_data, cert_data, chain_data, passphrase):
|
|
assert re.search(r"^-----BEGIN (.*) PRIVATE KEY-----\n", privkey_data, re.M)
|
|
assert re.search(r"^-----BEGIN CERTIFICATE-----\n", cert_data, re.M)
|
|
assert "PRIVATE KEY" not in cert_data
|
|
assert chain_data is None or "PRIVATE KEY" not in chain_data
|
|
|
|
fullchain_data = cert_data + "\n" + (chain_data or "") + "\n"
|
|
|
|
all_data = privkey_data + "\n" + fullchain_data
|
|
|
|
with ExitStack() as stack:
|
|
passphrase_r, passphrase_w = stack.enter_context(self.open_pipes(text=True))
|
|
|
|
cmd = [
|
|
self.openssl_prog,
|
|
"pkcs12",
|
|
"-export",
|
|
"-passout", f"fd:{passphrase_r.fileno():d}",
|
|
"-macalg", "SHA256",
|
|
"-keypbe", "AES-256-CBC",
|
|
"-certpbe", "NONE",
|
|
]
|
|
|
|
passphrase_proc = multiprocessing.Process(target=self.data_writer, args=(passphrase_w, passphrase))
|
|
passphrase_proc.start()
|
|
passphrase_w.close()
|
|
|
|
print(cmd)
|
|
#subprocess.run(["bash", "-x", "-c", "ls -l /dev/fd/; cat /dev/fd/5; cat /dev/fd/3"], pass_fds=(passphrase_r.fileno(), privkey_r.fileno()))
|
|
#subprocess.run(cmd, pass_fds=(passphrase_r.fileno(), privkey_r.fileno()), input=fullchain_data.encode())
|
|
subprocess.run(cmd, pass_fds=(passphrase_r.fileno(), privkey_r.fileno()), input=all_data.encode())
|
|
|
|
passphrase_proc.terminate()
|
|
passphrase_proc.
|
|
|
|
@contextmanager
|
|
def open_pipes(self, *, text=False):
|
|
rfd, wfd = os.pipe()
|
|
rfile = wfile = None
|
|
try:
|
|
rfile = open(rfd, "r" if text else "rb")
|
|
wfile = open(wfd, "w" if text else "wb")
|
|
except:
|
|
if rfile is not None:
|
|
rfile.close()
|
|
else:
|
|
os.close(rfd)
|
|
if wfile is not None:
|
|
wfile.close()
|
|
else:
|
|
os.close(wfd)
|
|
with rfile as r, wfile as w:
|
|
yield r, w
|
|
|
|
@classmethod
|
|
def data_writer(cls, file, data):
|
|
file.write(data)
|
|
file.close()
|