1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
|
# 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
from ecdsa import der as ECDSA_DER
from ecdsa.util import oid_ecPublicKey, encoded_oid_ecPublicKey
from ecdsa.keys import SigningKey
from ecdsa.curves import find_curve
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")
# Try doing same thing with ecdsa package ASN.1 utilities.
sk = SigningKey.from_der(der_test_keys["ec_rfc5915"])
vk = ECDSA_DER.encode_bitstring("\x00\x04" + sk.get_verifying_key().to_string())
ec = ECDSA_DER.encode_sequence(ECDSA_DER.encode_integer(1),
ECDSA_DER.encode_octet_string(sk.to_string()),
ECDSA_DER.encode_constructed(1, vk))
p8 = ECDSA_DER.encode_sequence(ECDSA_DER.encode_integer(0),
ECDSA_DER.encode_sequence(encoded_oid_ecPublicKey,
sk.curve.encoded_oid),
ECDSA_DER.encode_octet_string(ec))
print; print "ECDSA-library PKCS #8 encoding {} pyasn1 PKCS #8 encoding".format("matches" if p8 == 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.
sk = SigningKey.from_der(der)
print; print "ECDSA Python library parse of reencoded PKCS #8 data: {!r}".format(sk)
# Same thing with ecdsa package ASN.1 utilities.
car, cdr = ECDSA_DER.remove_sequence(der_test_keys["ec_pkcs8"])
assert cdr == ""
version, cdr = ECDSA_DER.remove_integer(car)
assert version == 0
car, ec = ECDSA_DER.remove_sequence(cdr)
oid, cdr = ECDSA_DER.remove_object(car)
assert oid == oid_ecPublicKey
oid, cdr = ECDSA_DER.remove_object(cdr)
curve = find_curve(oid)
assert cdr == ""
car, cdr = ECDSA_DER.remove_octet_string(ec)
assert cdr == ""
car, cdr = ECDSA_DER.remove_sequence(car)
assert cdr == ""
version, cdr = ECDSA_DER.remove_integer(car)
assert version == 1
privkey, cdr = ECDSA_DER.remove_octet_string(cdr)
tag, car, cdr = ECDSA_DER.remove_constructed(cdr)
assert tag == 1
assert cdr == ""
pubkey, cdr = ECDSA_DER.remove_bitstring(car)
assert cdr == ""
assert pubkey[:2] == "\x00\x04"
sk = SigningKey.from_string(privkey, curve)
print; print "ECDSA-library PKCS #8 decoding {} pyasn1 PKCS #8 decoding".format(
"matches" if der == sk.to_der() else "doesn't match")
|