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

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