asn1crypto
asn1crypto copied to clipboard
PublicKeyInfo['public_key'] returns ParsableOctetBitString on RSA keys instead of RSAPublicKey
With a PublicKeyInfo object pk_info, pk_info['public_key'] returns a ParsableOctetBitString for an RSA key(i.e, the spec override is not used).
For EC keys however a ECPointBitString is returned
Is this asymmetry intended?
Naively, I would expect pk_info['public_key'] to return either ECPointBitString or RSAPublicKey.
from asn1crypto.keys import PublicKeyInfo
info1 = PublicKeyInfo.load(b'0Y0\x13\x06\x07*\x86H\xce=\x02\x01\x06\x08*\x86H\xce=\x03\x01\x07\x03B\x00\x04`\xd0-\x95#\x19\x0e\xfb\x9ds\xc6_\xc2}\xc4\xf9\xf3E\x0e\r`{\x92\xff\xddw\xb2\xda\x87l\xd5)UM\xda\xbc\xb4\\\xeb\xff\x96c\x10\xd5\xc9\xc2v{\xdf\x98\xc1\xe25\xb8\x81\xc8\xd0%:\x1d\x16\xe0\xb2\xf9')
info1['public_key']
# <asn1crypto.keys.ECPointBitString 140004862275032 b'\x03B\x00\x04`\xd0-\x95#\x19\x0e\xfb\x9ds\xc6_\xc2}\xc4\xf9\xf3E\x0e\r`{\x92\xff\xddw\xb2\xda\x87l\xd5)UM\xda\xbc\xb4\\\xeb\xff\x96c\x10\xd5\xc9\xc2v{\xdf\x98\xc1\xe25\xb8\x81\xc8\xd0%:\x1d\x16\xe0\xb2\xf9'>
info2 = PublicKeyInfo.load(b'0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xb9?\xce\x00.C\xb4\t\x94\xc2\xbb\x89>{\xa1\xa6V\xff\x80\x04S,o\xfdKO\xe9\xf9\xfd\x96\x94\x8b\xef\x81=FK\xd6%8\xa1\xf1/\xb1\x81\xdf\x0c-\xfffC\xbb\xe6\x90\xb5\xbf0i)!\x0f\xa9 \xacQ\x95v7\xbb\xab9\xd6c\xe3I\x95Z#V\xac\x8d\x10Q\x06\x84\xb0\xb6\xac\xe9D\x9e\x9f+\xd4\xbc\xb1\xb8{/\xc5\xbf7STtG\x94\xd8\x14B.\x8c\x0e\xd5\xc2\xaa\x08\xf1\x83\xbe\xcc\x08\x92\xd0\xe5\x9a\'4N\xda\xd1\xbd\n^\xc9\xd7R(\xe8~\xf5\xb2\xe1x\xc1\xd8W\x83\x9a\xf57\xe6\r\xa5\x13l\xd5\xe9%h\x1a!\xa0\xbf\xaf\x81,I\xd9\xce\x15\x96**\x16\x7f1\x8d\x8e\r\xb5\xbb\x8cxvW1y{\x05\x8a\xcc\xcb\xb0\x16\x95\xcb0\x1b\xda\x99\xfe6K\xa3\xb8\xcb|\xfd,\xa4f\xf1\xc7G\xa4\x96S%*!\x8d(G\xe9\x80\x866\xa1\x8d,B\x1a\xfa\xab\xa5\x89\xe7\xce\xd06u\x01\xaew\xc7X\x01\x9c\x88\x81\xeay\x19V\xcf\x02\x03\x01\x00\x01')
info2['public_key']
# <asn1crypto.core.ParsableOctetBitString 140004853169792 b"\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xb9?\xce\x00.C\xb4\t\x94\xc2\xbb\x89>{\xa1\xa6V\xff\x80\x04S,o\xfdKO\xe9\xf9\xfd\x96\x94\x8b\xef\x81=FK\xd6%8\xa1\xf1/\xb1\x81\xdf\x0c-\xfffC\xbb\xe6\x90\xb5\xbf0i)!\x0f\xa9 \xacQ\x95v7\xbb\xab9\xd6c\xe3I\x95Z#V\xac\x8d\x10Q\x06\x84\xb0\xb6\xac\xe9D\x9e\x9f+\xd4\xbc\xb1\xb8{/\xc5\xbf7STtG\x94\xd8\x14B.\x8c\x0e\xd5\xc2\xaa\x08\xf1\x83\xbe\xcc\x08\x92\xd0\xe5\x9a'4N\xda\xd1\xbd\n^\xc9\xd7R(\xe8~\xf5\xb2\xe1x\xc1\xd8W\x83\x9a\xf57\xe6\r\xa5\x13l\xd5\xe9%h\x1a!\xa0\xbf\xaf\x81,I\xd9\xce\x15\x96**\x16\x7f1\x8d\x8e\r\xb5\xbb\x8cxvW1y{\x05\x8a\xcc\xcb\xb0\x16\x95\xcb0\x1b\xda\x99\xfe6K\xa3\xb8\xcb|\xfd,\xa4f\xf1\xc7G\xa4\x96S%*!\x8d(G\xe9\x80\x866\xa1\x8d,B\x1a\xfa\xab\xa5\x89\xe7\xce\xd06u\x01\xaew\xc7X\x01\x9c\x88\x81\xeay\x19V\xcf\x02\x03\x01\x00\x01">
The setup of PublicKeyInfo._public_key_spec for EC and RSA keys is different, (returning tuple and class respectively). This probably accounts for the difference in behaviour of __getitem__.
This is surprising to me and wonder whether this is intentional, i.e., RSA keys are not auto-parsed to RSAPublicKey when calling _['public_key'].
$ dumpasn1 -ade info1.der
0 89: SEQUENCE {
2 19: . SEQUENCE {
4 7: . . OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
13 8: . . OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7)
: . . }
23 66: . BIT STRING
: . . 04 60 D0 2D 95 23 19 0E FB 9D 73 C6 5F C2 7D C4
: . . F9 F3 45 0E 0D 60 7B 92 FF DD 77 B2 DA 87 6C D5
: . . 29 55 4D DA BC B4 5C EB FF 96 63 10 D5 C9 C2 76
: . . 7B DF 98 C1 E2 35 B8 81 C8 D0 25 3A 1D 16 E0 B2
: . . F9
: . }
$ dumpasn1 -ade info2.der
0 290: SEQUENCE {
4 13: . SEQUENCE {
6 9: . . OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
17 0: . . NULL
: . . }
19 271: . BIT STRING
: . . 30 82 01 0A 02 82 01 01 00 B9 3F CE 00 2E 43 B4
: . . 09 94 C2 BB 89 3E 7B A1 A6 56 FF 80 04 53 2C 6F
: . . FD 4B 4F E9 F9 FD 96 94 8B EF 81 3D 46 4B D6 25
: . . 38 A1 F1 2F B1 81 DF 0C 2D FF 66 43 BB E6 90 B5
: . . BF 30 69 29 21 0F A9 20 AC 51 95 76 37 BB AB 39
: . . D6 63 E3 49 95 5A 23 56 AC 8D 10 51 06 84 B0 B6
: . . AC E9 44 9E 9F 2B D4 BC B1 B8 7B 2F C5 BF 37 53
: . . 54 74 47 94 D8 14 42 2E 8C 0E D5 C2 AA 08 F1 83
: . . BE CC 08 92 D0 E5 9A 27 34 4E DA D1 BD 0A 5E C9
: . . D7 52 28 E8 7E F5 B2 E1 78 C1 D8 57 83 9A F5 37
: . . E6 0D A5 13 6C D5 E9 25 68 1A 21 A0 BF AF 81 2C
: . . 49 D9 CE 15 96 2A 2A 16 7F 31 8D 8E 0D B5 BB 8C
: . . 78 76 57 31 79 7B 05 8A CC CB B0 16 95 CB 30 1B
: . . DA 99 FE 36 4B A3 B8 CB 7C FD 2C A4 66 F1 C7 47
: . . A4 96 53 25 2A 21 8D 28 47 E9 80 86 36 A1 8D 2C
: . . 42 1A FA AB A5 89 E7 CE D0 36 75 01 AE 77 C7 58
: . . 01 9C 88 81 EA 79 19 56 CF 02 03 01 00 01
: . }
As you can see, both structures contain a BIT STRING. For your ec point, it's in a special non-asn.1 format 04 x y. For the RSA key, it's actually another ASN.1 sequence:
$ dumpasn1 -ade info2payload.der
0 266: SEQUENCE {
4 257: . INTEGER
: . . 00 B9 3F CE 00 2E 43 B4 09 94 C2 BB 89 3E 7B A1
: . . A6 56 FF 80 04 53 2C 6F FD 4B 4F E9 F9 FD 96 94
: . . 8B EF 81 3D 46 4B D6 25 38 A1 F1 2F B1 81 DF 0C
: . . 2D FF 66 43 BB E6 90 B5 BF 30 69 29 21 0F A9 20
: . . AC 51 95 76 37 BB AB 39 D6 63 E3 49 95 5A 23 56
: . . AC 8D 10 51 06 84 B0 B6 AC E9 44 9E 9F 2B D4 BC
: . . B1 B8 7B 2F C5 BF 37 53 54 74 47 94 D8 14 42 2E
: . . 8C 0E D5 C2 AA 08 F1 83 BE CC 08 92 D0 E5 9A 27
: . . 34 4E DA D1 BD 0A 5E C9 D7 52 28 E8 7E F5 B2 E1
: . . 78 C1 D8 57 83 9A F5 37 E6 0D A5 13 6C D5 E9 25
: . . 68 1A 21 A0 BF AF 81 2C 49 D9 CE 15 96 2A 2A 16
: . . 7F 31 8D 8E 0D B5 BB 8C 78 76 57 31 79 7B 05 8A
: . . CC CB B0 16 95 CB 30 1B DA 99 FE 36 4B A3 B8 CB
: . . 7C FD 2C A4 66 F1 C7 47 A4 96 53 25 2A 21 8D 28
: . . 47 E9 80 86 36 A1 8D 2C 42 1A FA AB A5 89 E7 CE
: . . D0 36 75 01 AE 77 C7 58 01 9C 88 81 EA 79 19 56
: . . CF
265 3: . INTEGER 65537
: . }
So it kind of makes sense to reflect this difference in asn1crypto. It returns a sub class of BitString (or very close) in both cases, so there isn't really an asymmetry.
You could take the reverse approach: Assume you do get an RSAPublicKey from info2['public_key']. How should the ASN.1 structure of PublicKeyInfo look like? It would need to be a Sequence where the public_key is another Sequence (or RSAPublicKey). But it's not. The field is a BitString.
It would certainly be more convenient if you didn't have to call .parsed for the RSA key. But I think this way it's closer to reality.
So in asn1crypto, the item access syntax performs direct ASN.1 structure access. As @joernheissler explained, the way that RSA is encoded is a double encoding, thus it takes two steps to get the value out.
Ultimately a property along the lines of https://github.com/wbond/asn1crypto/blob/master/asn1crypto/keys.py#L800-L810, but for PublicKeyInfo is probably in order so that every user doesn't need to rewrite these conditions.