aboutsummaryrefslogtreecommitdiff
path: root/pkcs8.py
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2017-03-28 21:34:15 -0500
committerRob Austein <sra@hactrn.net>2017-03-28 21:34:15 -0500
commit6e3faad9418e66b2d02e8857c3449ce3e5f93e78 (patch)
treedcc7df1a0a9728ee5b4d91d058b5f31bf2a2e708 /pkcs8.py
parent70afb31d4b4c887d5504404707849568a9e1e340 (diff)
Snapshot PKCS #8 Python hacks.
This will almost certainly merge into test scripts or libhal.py at some later date, right now just get it into git for archive.
Diffstat (limited to 'pkcs8.py')
-rw-r--r--pkcs8.py280
1 files changed, 280 insertions, 0 deletions
diff --git a/pkcs8.py b/pkcs8.py
new file mode 100644
index 0000000..cbd5c27
--- /dev/null
+++ b/pkcs8.py
@@ -0,0 +1,280 @@
+# Temporary sandbox for Python PKCS #8 hacks, probably integrate into
+# test scripts, libhal.py, etc once have figured this out.
+#
+# Both PyCrpto and the Python ecdsa package have their own ASN.1, so
+# why are we using yet another package? Because it's easier to
+# understand, that's why. Perhaps once we've debugged this we'll
+# recode it using one of the other packages to reduce external
+# dependencies, but for now, pyasn1 wins on ease of debugging.
+#
+# Also see the "native" encode and decode routines in pyasn1, which
+# supposedly encode and decode to built-in Python data types instead
+# of the fancy types from the pyasn1 library. Might be simpler, but
+# whole new mess so defer for now.
+
+# RFC 5208: PKCS #8
+# RFC 2313: PKCS #1.5 [rsa.c]
+# RFC 5915: EC keys [ecdsa.c]
+
+from pyasn1.type.univ import Sequence, SetOf, Integer, OctetString, ObjectIdentifier, BitString, Any
+from pyasn1.type.namedtype import NamedTypes, NamedType, OptionalNamedType
+from pyasn1.type.namedval import NamedValues
+from pyasn1.type.tag import Tag, tagClassContext, tagFormatSimple, tagFormatConstructed
+from pyasn1.type.constraint import SingleValueConstraint
+from pyasn1.codec.der.encoder import encode as DER_Encode
+from pyasn1.codec.der.decoder import decode as DER_Decode
+
+class AlgorithmIdentifier(Sequence):
+ componentType = NamedTypes(
+ NamedType( "algorithm", ObjectIdentifier()),
+ OptionalNamedType( "parameters", Any()))
+
+class AttributeTypeAndValue(Sequence):
+ componentType = NamedTypes(
+ NamedType( "type", ObjectIdentifier()),
+ NamedType( "value", Any()))
+
+class Attribute(Sequence):
+ componentType = NamedTypes(
+ NamedType( "type", ObjectIdentifier()),
+ NamedType( "vals", SetOf(componentType = Any())))
+
+# RFC 5208
+
+class PrivateKeyInfo(Sequence):
+ componentType = NamedTypes(
+ NamedType( "version", Integer(namedValues = NamedValues(("v1", 0))) .subtype(subtypeSpec = Integer.subtypeSpec + SingleValueConstraint(0))),
+ NamedType( "privateKeyAlgorithm", AlgorithmIdentifier()),
+ NamedType( "privateKey", OctetString()),
+ OptionalNamedType( "attributes", SetOf(componentType = Attribute()) .subtype(implicitTag = Tag(tagClassContext, tagFormatConstructed, 0))))
+
+class EncryptedPrivateKeyInfo(Sequence):
+ componentType = NamedTypes(
+ NamedType( "encryptionAlgorithm", AlgorithmIdentifier()),
+ NamedType( "encryptedData", OctetString()))
+
+# RFC 2313
+
+class RSAPrivateKey(Sequence):
+ componentType = NamedTypes(
+ NamedType( "version", Integer() .subtype(subtypeSpec = Integer.subtypeSpec + SingleValueConstraint(0))),
+ NamedType( "n", Integer()),
+ NamedType( "e", Integer()),
+ NamedType( "d", Integer()),
+ NamedType( "p", Integer()),
+ NamedType( "q", Integer()),
+ NamedType( "dP", Integer()),
+ NamedType( "dQ", Integer()),
+ NamedType( "u", Integer()))
+
+# RFC 5915
+
+class ECPrivateKey(Sequence):
+ componentType = NamedTypes(
+ NamedType( "version", Integer(namedValues = NamedValues(("ecPrivkeyVer1", 1))) .subtype(subtypeSpec = Integer.subtypeSpec + SingleValueConstraint(1))),
+ NamedType( "privateKey", OctetString()),
+ OptionalNamedType( "parameters", ObjectIdentifier() .subtype(explicitTag = Tag(tagClassContext, tagFormatSimple, 0))),
+ OptionalNamedType( "publicKey", BitString() .subtype(explicitTag = Tag(tagClassContext, tagFormatSimple, 1))))
+
+# Test data, generated by OpenSSL
+
+der_test_keys = dict(
+
+ ec_rfc5915 = '''
+ MHcCAQEEIFWaZOsQxLwZmIK4YAuf1d8S9Pnznvzcl9TjiMpvXkCYoAoGCCqGSM49
+ AwEHoUQDQgAEC/8vH5bL+3KNNF/NL+VmUKZQtjA59UsGtKP6FP4ZqFc3Y7Gie77/
+ lG1/L+s/6ircB1JkI8zaE3KYd7s+7IYIEQ==
+ '''.decode("base64"),
+
+ ec_pkcs8 = '''
+ MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgVZpk6xDEvBmYgrhg
+ C5/V3xL0+fOe/NyX1OOIym9eQJihRANCAAQL/y8flsv7co00X80v5WZQplC2MDn1
+ Swa0o/oU/hmoVzdjsaJ7vv+UbX8v6z/qKtwHUmQjzNoTcph3uz7shggR
+ '''.decode("base64"),
+
+ rsa_rfc2313 = '''
+ MIIEpAIBAAKCAQEAx/N9ee3u6Z6qjw5waPhuUBYy7m6+kRfNYB8KSERGd5K2xD96
+ IeyvEv+xMDA2BQ3xOummL2yAjtMZ2N7Le37nfpvtzwVWqrOHzq7OWaw/pPl1N9Lq
+ VSQLPoxHw3TVe69QNPVu5SeumaOGXmzTIs1pr2yVBZD/i2KYiif3BO2SgoDx7g4s
+ cFdg/6YiDpKYbY/yx4YN6KJxDGMM6DE0Ih8hE68flJMSIbUWIaJZo0b7XPeE9zYU
+ zf93VLvuYIqWYMuwTw5TSUnzeRq6ALJpf90nObduJsYEPu/i4RFlxdm5WsmOb2Tu
+ F7JFesEdGeT9lCxxd3CI5YTItQIBWsx0AzCS/QIDAQABAoIBAQCmC7Zvwv9cUr8g
+ /cSr52L0bvrstlra8wFCiYRobwp10gilAHHUKlFZXa0vb1ns6J8jZVT0nQ5FjVkx
+ mBMzAzgLFEJwYOaP63ckVFZYcYqI3gBR0312JvCPiL8vuZ5vkC7zS75D3qhIPlwf
+ ng/YHu1dGLbIYJlWjxJN6NJh7Uh1xlZcm0WAJYhJpmMIZJv2abTS4GXw4SVOyMnd
+ tPEgEfrK/y2PsNUPwnby6LR1cE2rxOQtb6gNCov0AAiE0BsJeE7jXa2IEl6lKoBR
+ ChDMAeU53pJPlcYt7ZmAgyezuEfnr4kY5Rk/nTcwTxTpzQi7Dcth8QCRqfu4wXXj
+ QEN7b9cBAoGBAPNjRPc/Z89jYp1IDR+R5oi7YTsLzNAIlS/t9wgrujnVdsm00xos
+ dd+NwvjTi7wE0fV+7u5/W9ni3077JaBGBa9+nD0iB0PgAJW+tb8HUJXABQKoTA4m
+ yyiAHNHgarwc1uwr+yAYqvSj7aAvIcZeXgi3qxDXEOSKuk8n57/TpPMVAoGBANJP
+ /9/6zxd3PdogiP0nC+piJHstexk+l4WRqGWWuRG0VTIEfBk4dQfj/UwfmTcCQxAe
+ D0e9EoHeVOfsv4nfOfDhGC7jHLkLpNJbc5ttgr2sZ6qIouBJ8suMDte/zZze27aU
+ 7epFqw0w9Y58fwRyP2u5ILYFcm+cWeplg9lY4rpJAoGBALzLs1Krn4YzDOr+Whe0
+ IITN/XVFCQIStk8wo2B2MwXrvTJoDx0Ngf4AxE4qIwmdH5T0erkMmB5jK1/j12MF
+ DiH874tIWyRenXWLMwZU0UDoa7qM/Do6A3uOLUzsbT8wi9M1pp5WJD6S7qBED0oG
+ J6FRf+QXQCZYKn9+b/nQXfKlAoGADwFuPEjk5cO4Qgv4OjfC/eIqwC8qjU6N+RW/
+ ciAi7ER1n6/6OsJwdzOpKvlGMUqUBl5esLuoymNWo4Wc1PV8aNdmplHGBt3x0KB9
+ yyUxIt9eNiixllcwX52KoZIp1XuBKbHOl9yIq9RGcPgpB+Qu6jy3PMV+uL/rGnJL
+ ygIxiyECgYAwSLLuxHKGcy+oZo1vxSQlY2gOkprqK9VwjAhxGuGlJftXrL/Dkbxs
+ GatW0bGAyQ+VPivPl8YYhs34NCA1t1pJKczcNbHEJFN57x6AtCbHFLWB91wY96yA
+ 6y/Bgd45PoXryQl7+GdOAPyEYY3mq3R5vaozTraPrnD+61kpKVLJ/Q==
+ '''.decode("base64"),
+
+ rsa_pkcs8 = '''
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDH83157e7pnqqP
+ DnBo+G5QFjLubr6RF81gHwpIREZ3krbEP3oh7K8S/7EwMDYFDfE66aYvbICO0xnY
+ 3st7fud+m+3PBVaqs4fOrs5ZrD+k+XU30upVJAs+jEfDdNV7r1A09W7lJ66Zo4Ze
+ bNMizWmvbJUFkP+LYpiKJ/cE7ZKCgPHuDixwV2D/piIOkphtj/LHhg3oonEMYwzo
+ MTQiHyETrx+UkxIhtRYholmjRvtc94T3NhTN/3dUu+5gipZgy7BPDlNJSfN5GroA
+ sml/3Sc5t24mxgQ+7+LhEWXF2blayY5vZO4XskV6wR0Z5P2ULHF3cIjlhMi1AgFa
+ zHQDMJL9AgMBAAECggEBAKYLtm/C/1xSvyD9xKvnYvRu+uy2WtrzAUKJhGhvCnXS
+ CKUAcdQqUVldrS9vWezonyNlVPSdDkWNWTGYEzMDOAsUQnBg5o/rdyRUVlhxioje
+ AFHTfXYm8I+Ivy+5nm+QLvNLvkPeqEg+XB+eD9ge7V0YtshgmVaPEk3o0mHtSHXG
+ VlybRYAliEmmYwhkm/ZptNLgZfDhJU7Iyd208SAR+sr/LY+w1Q/CdvLotHVwTavE
+ 5C1vqA0Ki/QACITQGwl4TuNdrYgSXqUqgFEKEMwB5Tnekk+Vxi3tmYCDJ7O4R+ev
+ iRjlGT+dNzBPFOnNCLsNy2HxAJGp+7jBdeNAQ3tv1wECgYEA82NE9z9nz2NinUgN
+ H5HmiLthOwvM0AiVL+33CCu6OdV2ybTTGix1343C+NOLvATR9X7u7n9b2eLfTvsl
+ oEYFr36cPSIHQ+AAlb61vwdQlcAFAqhMDibLKIAc0eBqvBzW7Cv7IBiq9KPtoC8h
+ xl5eCLerENcQ5Iq6Tyfnv9Ok8xUCgYEA0k//3/rPF3c92iCI/ScL6mIkey17GT6X
+ hZGoZZa5EbRVMgR8GTh1B+P9TB+ZNwJDEB4PR70Sgd5U5+y/id858OEYLuMcuQuk
+ 0ltzm22Cvaxnqoii4Enyy4wO17/NnN7btpTt6kWrDTD1jnx/BHI/a7kgtgVyb5xZ
+ 6mWD2VjiukkCgYEAvMuzUqufhjMM6v5aF7QghM39dUUJAhK2TzCjYHYzBeu9MmgP
+ HQ2B/gDETiojCZ0flPR6uQyYHmMrX+PXYwUOIfzvi0hbJF6ddYszBlTRQOhruoz8
+ OjoDe44tTOxtPzCL0zWmnlYkPpLuoEQPSgYnoVF/5BdAJlgqf35v+dBd8qUCgYAP
+ AW48SOTlw7hCC/g6N8L94irALyqNTo35Fb9yICLsRHWfr/o6wnB3M6kq+UYxSpQG
+ Xl6wu6jKY1ajhZzU9Xxo12amUcYG3fHQoH3LJTEi3142KLGWVzBfnYqhkinVe4Ep
+ sc6X3Iir1EZw+CkH5C7qPLc8xX64v+sackvKAjGLIQKBgDBIsu7EcoZzL6hmjW/F
+ JCVjaA6Smuor1XCMCHEa4aUl+1esv8ORvGwZq1bRsYDJD5U+K8+XxhiGzfg0IDW3
+ WkkpzNw1scQkU3nvHoC0JscUtYH3XBj3rIDrL8GB3jk+hevJCXv4Z04A/IRhjear
+ dHm9qjNOto+ucP7rWSkpUsn9
+ '''.decode("base64"))
+
+
+def decode_ecpoint(ecpoint):
+ return { "\x02": "compressed", "\x04": "uncompressed" }[ecpoint[0]], ecpoint[1:1+len(ecpoint)/2], ecpoint[1+len(ecpoint)/2:]
+
+def dumpasn1(der):
+ from subprocess import call
+ from tempfile import NamedTemporaryFile
+ with NamedTemporaryFile() as f:
+ f.write(der)
+ f.flush()
+ call(("dumpasn1", "-aop", f.name))
+
+if __name__ == "__main__":
+
+ show_manual_decode = False
+
+ ec_rfc5915 = DER_Decode(der_test_keys["ec_rfc5915"], ECPrivateKey() )[0]
+ ec_pkcs8 = DER_Decode(der_test_keys["ec_pkcs8"], PrivateKeyInfo())[0]
+ ec_pkcs8_privateKey = DER_Decode(str(ec_pkcs8["privateKey"]), ECPrivateKey() )[0]
+ rsa_rfc2313 = DER_Decode(der_test_keys["rsa_rfc2313"], RSAPrivateKey() )[0]
+ rsa_pkcs8 = DER_Decode(der_test_keys["rsa_pkcs8"], PrivateKeyInfo())[0]
+ rsa_pkcs8_privateKey = DER_Decode(str(rsa_pkcs8["privateKey"]), RSAPrivateKey() )[0]
+
+ print
+ print "EC RFC 5915"
+ print ec_rfc5915.prettyPrint()
+
+ if show_manual_decode:
+ print
+ compressed, Qx, Qy = decode_ecpoint(ec_rfc5915["publicKey"].asOctets())
+ print "version: ", ec_rfc5915["version"]
+ print "privateKey:", str(ec_rfc5915["privateKey"]).encode("hex")
+ print "parameters:", ec_rfc5915["parameters"]
+ print "publicKey: ", compressed
+ print " Qx: ", Qx.encode("hex")
+ print " Qy: ", Qy.encode("hex")
+
+ # This works, and lets .prettyPrint() display the ANY content properly,
+ # but it breaks some of the key hackery we do after all this display stuff.
+ #ec_pkcs8["privateKeyAlgorithm"]["parameters"] = DER_Decode(ec_pkcs8["privateKeyAlgorithm"]["parameters"])[0]
+
+ print
+ print "EC PKCS #8"
+ print ec_pkcs8.prettyPrint()
+ print ec_pkcs8_privateKey.prettyPrint()
+
+ if show_manual_decode:
+ print
+ compressed, Qx, Qy = decode_ecpoint(ec_pkcs8_privateKey["publicKey"].asOctets())
+ print "version: ", ec_pkcs8["version"]
+ print "privateKeyAlgorithm:", ec_pkcs8["privateKeyAlgorithm"][0]
+ print " ", DER_Decode(ec_pkcs8["privateKeyAlgorithm"]["parameters"])[0]
+ print "privateKey:"
+ print " version: ", ec_pkcs8_privateKey["version"]
+ print " privateKey:", str(ec_pkcs8_privateKey["privateKey"]).encode("hex")
+ print " parameters:", ec_pkcs8_privateKey["parameters"]
+ print " publicKey: ", compressed
+ print " Qx: ", Qx.encode("hex")
+ print " Qy: ", Qy.encode("hex")
+
+ print
+ print "RSA RFC 2313"
+ print rsa_rfc2313.prettyPrint()
+
+ if show_manual_decode:
+ print
+ print "version:", rsa_rfc2313["version"]
+ print " n:", rsa_rfc2313["n"]
+ print " e:", rsa_rfc2313["e"]
+ print " d:", rsa_rfc2313["d"]
+ print " p:", rsa_rfc2313["p"]
+ print " q:", rsa_rfc2313["q"]
+ print " dP:", rsa_rfc2313["dP"]
+ print " dQ:", rsa_rfc2313["dQ"]
+ print " u:", rsa_rfc2313["u"]
+
+ #rsa_pkcs8["privateKeyAlgorithm"]["parameters"] = DER_Decode(rsa_pkcs8["privateKeyAlgorithm"]["parameters"])[0]
+
+ print
+ print "RSA PKCS #8"
+ print rsa_pkcs8.prettyPrint()
+ print rsa_pkcs8_privateKey.prettyPrint()
+
+ if show_manual_decode:
+ print
+ print "version: ", rsa_pkcs8["version"]
+ print "privateKeyAlgorithm:", rsa_pkcs8["privateKeyAlgorithm"][0]
+ print "privateKey:"
+ print " version:", rsa_pkcs8_privateKey["version"]
+ print " n:", rsa_pkcs8_privateKey["n"]
+ print " e:", rsa_pkcs8_privateKey["e"]
+ print " d:", rsa_pkcs8_privateKey["d"]
+ print " p:", rsa_pkcs8_privateKey["p"]
+ print " q:", rsa_pkcs8_privateKey["q"]
+ print " dP:", rsa_pkcs8_privateKey["dP"]
+ print " dQ:", rsa_pkcs8_privateKey["dQ"]
+ print " u:", rsa_pkcs8_privateKey["u"]
+
+ # Generate PKCS #8 from ECPrivateKey and check against static data
+ p8 = PrivateKeyInfo()
+ ec = ECPrivateKey()
+ ec["version"] = ec_rfc5915["version"]
+ ec["privateKey"] = ec_rfc5915["privateKey"]
+ ec["publicKey"] = ec_rfc5915["publicKey"]
+ p8["version"] = 0
+ p8["privateKeyAlgorithm"] = AlgorithmIdentifier()
+ p8["privateKeyAlgorithm"]["algorithm"] = "1.2.840.10045.2.1"
+ p8["privateKeyAlgorithm"]["parameters"] = ObjectIdentifier(ec_rfc5915["parameters"])
+ p8["privateKey"] = DER_Encode(ec)
+ der = DER_Encode(p8)
+ #print; dumpasn1(der)
+ #print; dumpasn1(der_test_keys["ec_pkcs8"])
+ print; print "Reencoded PKCS #8 {} static data".format("matches" if der == der_test_keys["ec_pkcs8"] else "doesn't match")
+
+ # Generate ECPrivateKey from PKCS #8 and check against static data
+ ec = ECPrivateKey()
+ ec["version"] = ec_pkcs8_privateKey["version"]
+ ec["privateKey"] = ec_pkcs8_privateKey["privateKey"]
+ ec["parameters"] = str(DER_Decode(ec_pkcs8["privateKeyAlgorithm"]["parameters"])[0])
+ ec["publicKey"] = ec_pkcs8_privateKey["publicKey"]
+ der = DER_Encode(ec)
+ #print; dumpasn1(der)
+ #print; dumpasn1(der_test_keys["ec_rfc5915"])
+ print; print "Reencoded PKCS #8 {} static data".format("matches" if der == der_test_keys["ec_rfc5915"] else "doesn't match")
+
+ # Paranoia: Make sure we really can load the RFC 5915 we just generated.
+ from ecdsa.keys import SigningKey
+ sk = SigningKey.from_der(der)
+ print; print "ECDSA Python library parse of reencoded PKCS #8 data: {!r}".format(sk)