#!/usr/bin/env python3 import cryptography.hazmat.backends import cryptography.hazmat.primitives.serialization import cryptography.x509 import math DEFAULT_KEY_OUTPUT_PATH = 'gpg-key.sexp' DEFAULT_SMARTCARD_APP_ID_HEX = 'D2760001240102010001234567890000' def convert_to_sexp(data): if isinstance(data, int): return convert_to_sexp(data.to_bytes( math.ceil(data.bit_length() / 8), 'big', )) elif isinstance(data, str): return convert_to_sexp(data.encode()) elif isinstance(data, bytes): return str(len(data)).encode() + b':' + data else: return b'(' + b''.join(convert_to_sexp(i) for i in data) + b')' def create_gpg_key(input_path, gpg_key_output_path, smartcard_app_id_hex): backend = cryptography.hazmat.backends.default_backend() with open(input_path, 'rb') as f: req = cryptography.x509.load_pem_x509_csr(f.read(), backend) assert req.is_signature_valid pubnums = req.public_key().public_numbers() key_data = ['shadowed-private-key', [ 'rsa', ['n', pubnums.n], ['e', pubnums.e], ['shadowed', 't1-v1', [int(smartcard_app_id_hex, 16), 'OPENPGP.1']], ]] key = convert_to_sexp(key_data) with open(gpg_key_output_path, 'wb') as f: f.write(key) def _init_argparser(): import argparse argparser = argparse.ArgumentParser( description='create a shadowed-private-key in sexp format for gnupg\'s private-keys-v1.d folder' + ' containing the public key of a PEM-encoded X.509 certificate signing request (CSR)', ) argparser.add_argument( 'input_path', help='path to PEM-encoded X.509 signing request', ) argparser.add_argument( '--gpg-key-output-path', dest='gpg_key_output_path', default=DEFAULT_KEY_OUTPUT_PATH, help='path to sexp-encoded shadowed-private-key to be created (default: "%(default)s")', ) argparser.add_argument( '--smartcard-app-id', dest='smartcard_app_id_hex', metavar='hex-string', default=DEFAULT_SMARTCARD_APP_ID_HEX, help='default: %(default)s', ) return argparser def main(argv): argparser = _init_argparser() args = argparser.parse_args(argv[1:]) create_gpg_key(**vars(args)) return 0 if __name__ == '__main__': import sys sys.exit(main(sys.argv))