]> git.draconx.ca Git - scripts.git/commitdiff
Add script to convert "JWK"-format RSA keys to normal. master
authorNick Bowler <nbowler@draconx.ca>
Wed, 2 Nov 2022 03:12:09 +0000 (23:12 -0400)
committerNick Bowler <nbowler@draconx.ca>
Wed, 2 Nov 2022 03:12:09 +0000 (23:12 -0400)
jwk2pem.py [new file with mode: 0755]

diff --git a/jwk2pem.py b/jwk2pem.py
new file mode 100755 (executable)
index 0000000..733b2c7
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/env python
+#
+# Copyright © 2022 Nick Bowler
+#
+# Tool to convert certbot's account keys in JWK format to X.509 PEM format.
+#
+# License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+# This is free software: you are free to do what the fuck you want to.
+# There is NO WARRANTY, to the extent permitted by law.
+
+import argparse
+import base64
+import json
+import os
+import subprocess
+import sys
+
+progname = "jwk2pem"
+version = "0"
+
+parser = argparse.ArgumentParser(
+    description='Convert RSA private keys in JWK format to PKCS#1 PEM format'
+)
+
+def errmsg(msg, prog=parser.prog):
+    print("%s: %s" % (prog, msg), file=sys.stderr)
+
+class VersionAction(argparse.Action):
+    def __init__(self, **kw):
+        super().__init__(nargs=0, help="show a version message and then exit", **kw)
+    def __call__(self, parser, namespace, values, option_string=None):
+        print("%s %s" % (progname, version))
+        print('''Copyright © 2022 Nick Bowler
+License WTFPL2: Do What The Fuck You Want To Public License, version 2.
+This is free software: you are free to do what the fuck you want to.
+There is NO WARRANTY, to the extent permitted by law.''')
+        sys.exit(0)
+parser.add_argument('--version', action=VersionAction)
+parser.add_argument('input', metavar='FILE', nargs='?',
+                    help='''read JWK data from FILE, instead of standard input.''')
+parser.add_argument('-o', '--output', metavar='FILE',
+                    help='''write PEM data to FILE, instead of standard output.''')
+args = parser.parse_args()
+
+infile = sys.stdin
+infilename = "stdin"
+if args.input:
+    infilename = args.input
+    infile = open(infilename, "r")
+
+outfile = sys.stdout
+outfilename = "stdout"
+if args.output:
+    outfilename = args.output
+    outfile = os.open(outfilename, os.O_WRONLY|os.O_CREAT|os.O_TRUNC, 0o600)
+
+data = json.load(infile)
+if data['kty'] != "RSA":
+    errmsg("%s: input is not an RSA private key in JWK format")
+    sys.exit(1)
+
+rsa = subprocess.Popen(
+    [ "openssl", "rsa", "-inform", "DER", "-outform", "PEM", "-out", "-" ],
+    stdin=subprocess.PIPE, stdout=outfile, stderr=sys.stderr
+)
+
+asn1parse = subprocess.Popen(
+    [ "openssl", "asn1parse", "-genconf", "-", "-noout", "-out", "-" ],
+    stdin=subprocess.PIPE, stdout=rsa.stdin, stderr=sys.stderr
+)
+
+asn1parse.stdin.write(b"asn1=SEQUENCE:jwk\n[jwk]\nversion=INTEGER:0\n")
+for param in ['n', 'e', 'd', 'p', 'q', 'dp', 'dq', 'qi']:
+    spec = "%s=INTEGER:0x%s\n" % (
+        param, base64.b64decode(data[param] + "===", '-_').hex()
+    )
+    asn1parse.stdin.write(spec.encode())
+
+asn1parse.stdin.close()
+asn1parse.wait()
+rsa.wait()