Created
March 23, 2022 04:03
-
-
Save chronos-tachyon/2a3e37b97e20f719c2f3a4766e09f88d to your computer and use it in GitHub Desktop.
A tool for splitting a key+cert+chain X.509 PEM file into separate key, cert, and chain PEM files.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# | |
# Written by Donald King <chronos@chronos-tachyon.net> | |
# Public Domain. | |
# | |
# ==== CC0 https://creativecommons.org/publicdomain/zero/1.0/ ==== | |
# [To the extent possible under law, I have waived all copyright ] | |
# [and related or neighboring rights to this script. This work is] | |
# [published from the United States of America. ] | |
import argparse | |
import os | |
import re | |
RE_BEGIN = re.compile(r'^-----BEGIN ([0-9A-Z]+(?: [0-9A-Z]+)*)-----$') | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-i', '--input', help='path to input PEM file', required=True) | |
parser.add_argument('-k', '--key', help='path to output private key PEM file', required=True) | |
parser.add_argument('-c', '--cert', help='path to output certificate or cert + CA chain PEM file', required=True) | |
parser.add_argument('-C', '--ca', help='path to output CA chain PEM file') | |
parser.add_argument('--crlf', help='output PEM files in CRLF format, instead of LF') | |
parser.add_argument('--reverse', action='store_true', help='output the CA chain in reverse order (root first, then intermediate)') | |
args = parser.parse_args() | |
private_key = None | |
leaf_certificate = None | |
chain_certificates = [] | |
eol = '\r\n' if args.crlf else '\n' | |
mode = 0 | |
tag = None | |
partial = None | |
end = None | |
with open(args.input, 'r') as fp: | |
for line in fp: | |
line = line.rstrip('\r\n') | |
if mode == 0 and not line: | |
pass | |
elif mode == 0: | |
m = RE_BEGIN.match(line) | |
if not m: | |
raise ValueError('expected /-----BEGIN .*-----/, got {!r}'.format(line)) | |
tag = m.group(1) | |
if tag.endswith(' PRIVATE KEY'): | |
mode = 1 | |
elif tag == 'CERTIFICATE' and leaf_certificate is None: | |
mode = 2 | |
elif tag == 'CERTIFICATE': | |
mode = 3 | |
else: | |
raise ValueError('expected BEGIN FOO PRIVATE KEY or BEGIN CERTIFICATE, got {!r}'.format('BEGIN ' + tag)) | |
partial = line + eol | |
end = '-----END ' + tag + '-----' | |
elif line != end: | |
partial += line + eol | |
elif mode == 1: | |
partial += line + eol | |
private_key = partial | |
mode = 0 | |
tag = None | |
partial = None | |
end = None | |
elif mode == 2: | |
partial += line + eol | |
leaf_certificate = partial | |
mode = 0 | |
tag = None | |
partial = None | |
end = None | |
elif mode == 3: | |
partial += line + eol | |
chain_certificates.append(partial) | |
mode = 0 | |
tag = None | |
partial = None | |
end = None | |
if args.reverse: | |
chain_certificates.reverse() | |
if args.ca: | |
with open(args.ca, 'x') as fp: | |
for chain_certificate in chain_certificates: | |
fp.write(chain_certificate) | |
with open(args.cert, 'x') as fp: | |
fp.write(leaf_certificate) | |
if not args.ca: | |
for chain_certificate in chain_certificates: | |
fp.write(chain_certificate) | |
os.umask(0o077) | |
with open(args.key, 'x') as fp: | |
fp.write(private_key) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment