123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100 |
- #!/usr/bin/env python3
- import binascii
- import cryptography.hazmat.backends
- import cryptography.hazmat.primitives.serialization
- import cryptography.x509
- import ctypes
- import math
- DEFAULT_KEY_OUTPUT_PATH_PATTERN = '{keygrip_hex}.key'
- DEFAULT_SMARTCARD_APP_ID_HEX = 'D2760001240102010001234567890000'
- gcrypt = ctypes.cdll.LoadLibrary('libgcrypt.so')
- gcrypt.gcry_pk_get_keygrip.restype = ctypes.POINTER(ctypes.c_char)
- 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 keygrip_from_key_sexp(key_sexp_data):
- sexp = ctypes.c_void_p()
- assert not gcrypt.gcry_sexp_new(
- ctypes.pointer(sexp),
- key_sexp_data,
- len(key_sexp_data),
- 0,
- )
- keygrip = gcrypt.gcry_pk_get_keygrip(sexp, None)[:20]
- gcrypt.gcry_sexp_release(sexp)
- return keygrip
- def create_gpg_key(input_path, gpg_key_output_path_pattern, 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_sexp_data = convert_to_sexp(key_data)
- keygrip = keygrip_from_key_sexp(key_sexp_data)
- keygrip_hex = binascii.hexlify(keygrip).upper().decode()
- with open(gpg_key_output_path_pattern.format(keygrip_hex=keygrip_hex), 'wb') as f:
- f.write(key_sexp_data)
- 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_pattern',
- metavar='path-pattern',
- default=DEFAULT_KEY_OUTPUT_PATH_PATTERN,
- help='path to sexp-encoded shadowed-private-key to be created.'
- + ' if specified, {keygrip_hex} will be substituted.'
- + ' (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))
|