create-gpg-shadow-key-from-x509-cert-req.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. #!/usr/bin/env python3
  2. import cryptography.hazmat.backends
  3. import cryptography.hazmat.primitives.serialization
  4. import cryptography.x509
  5. import math
  6. DEFAULT_SMARTCARD_APP_ID_HEX = 'D2760001240102010001234567890000'
  7. def convert_to_sexp(data):
  8. if isinstance(data, int):
  9. return convert_to_sexp(data.to_bytes(
  10. math.ceil(data.bit_length() / 8),
  11. 'big',
  12. ))
  13. elif isinstance(data, str):
  14. return convert_to_sexp(data.encode())
  15. elif isinstance(data, bytes):
  16. return str(len(data)).encode() + b':' + data
  17. else:
  18. return b'(' + b''.join(convert_to_sexp(i) for i in data) + b')'
  19. def create_gpg_key(input_path, smartcard_app_id_hex):
  20. backend = cryptography.hazmat.backends.default_backend()
  21. with open(input_path, 'rb') as f:
  22. req = cryptography.x509.load_pem_x509_csr(f.read(), backend)
  23. assert req.is_signature_valid
  24. pubnums = req.public_key().public_numbers()
  25. key_data = ['shadowed-private-key', [
  26. 'rsa',
  27. ['n', pubnums.n],
  28. ['e', pubnums.e],
  29. ['shadowed', 't1-v1', [int(smartcard_app_id_hex, 16), 'OPENPGP.1']],
  30. ]]
  31. key = convert_to_sexp(key_data)
  32. with open('gpg-key.sexp', 'wb') as f:
  33. f.write(key)
  34. def _init_argparser():
  35. import argparse
  36. argparser = argparse.ArgumentParser(
  37. description='create a shadowed-private-key in sexp format for gnupg\'s private-keys-v1.d folder'
  38. + ' containing the public key of a PEM-encoded X.509 certificate signing request (CSR)',
  39. )
  40. argparser.add_argument(
  41. 'input_path',
  42. help='path to PEM-encoded X.509 signing request',
  43. )
  44. argparser.add_argument(
  45. '--smartcard-app-id',
  46. dest='smartcard_app_id_hex',
  47. metavar='hex-string',
  48. default=DEFAULT_SMARTCARD_APP_ID_HEX,
  49. help='default: %(default)s',
  50. )
  51. return argparser
  52. def main(argv):
  53. argparser = _init_argparser()
  54. args = argparser.parse_args(argv[1:])
  55. create_gpg_key(**vars(args))
  56. return 0
  57. if __name__ == '__main__':
  58. import sys
  59. sys.exit(main(sys.argv))