RSA私鑰簽名時要基於某個HASH算法,好比MD5或者SHA1等。以前我一直認爲簽名的過程是:先對明文作HASH計算,而後用私鑰直接對HASH值加密。最近才發現不是那麼簡單,須要對HASH後的數據進行BER編碼再加密。算法
先看一個例子。數組
公鑰模:89 54 E6 61 C1 52 DB ED 07 57 50 04 AD B3 D2 A7 A9 8F E8 D8 20 5B 01 B2 E5 E4 7A 7B EE 80 E3 C0 13 11 D2 F9 AD C3 CC 5F 1D 96 AC B2 AB BE 9C 14 9E 76 31 06 B2 E6 FA 01 52 A7 2E 53 C2 1D 3B 7B 9B 68 05 D2 5E 35 31 98 0E 02 93 E0 D9 0C 38 2D 3D EE 10 E6 87 53 79 DF B2 1E 12 D9 9E EF 89 6D 01 59 0D 13 94 DB 05 B7 09 34 D3 5B AB ED 7C FE 0E BE 87 EE E8 DD 01 39 3A CA 3A A7 17 B8 AA E3編碼
公鑰指數:01 00 01加密
私鑰指數:01 FE B1 BA 09 CC E2 54 F7 1E 55 93 3B D2 B8 E4 A6 99 E8 8F FB 28 57 45 FA 00 EF A6 8D 38 62 16 90 30 5A 18 36 65 F9 BA 07 FC 00 56 38 18 74 BB F7 F1 4F 95 01 54 49 9D 6B 4D F2 66 55 13 87 A1 A6 95 74 72 6A D8 3A EA 34 A8 F8 40 5F 27 11 30 4F 96 3A 2E 7B E6 B6 47 3C 3B 4D 24 E8 FA 51 19 59 FB 52 E0 9B D2 24 B3 B5 8A 36 BF 34 20 E9 2A AB 5D 55 9B 60 01 D5 04 81 E8 E7 EC B2 5F 81 41spa
私鑰P:BF 36 08 66 63 74 6A 79 D0 77 64 21 73 6D 1A B9 13 BB 35 13 BE A6 73 84 C8 7D 83 67 BE C2 F5 0C 3A 7F 5F EF 6E 73 E2 BC 31 D2 0C 78 06 D7 38 85 7E F5 06 40 62 A6 1D 53 CC 97 34 30 58 EE E2 05對象
私鑰Q:B7 DD 46 99 58 B2 52 4B 87 FB E1 F1 09 44 AB 9A AD D1 93 90 9C 40 E0 2F 36 63 F4 7F 49 CB 36 E3 2C DA 85 5C 6E CE 41 AC CB 09 6C 27 B6 44 2B D8 26 5F D5 63 DF 2A C8 60 57 3B 23 13 2B 5F 65 C7接口
私鑰DP:A6 EF C4 9B A7 9E DE CA E5 2F 27 33 71 33 C3 0D EC 65 18 2C D9 D9 36 A7 A9 E6 B2 CF E3 A3 10 10 12 0E 5C B2 8C 2B 0E BC 21 7E F2 35 E4 3B 08 74 BC 67 AD 82 8E DD DA 62 EC 0E E2 98 87 3C 60 05md5
私鑰DQ:B6 A0 8B A7 75 7A 6A 53 AB D6 7D 2E 35 CE 87 C5 34 31 9F 29 5C 8A F4 22 F1 1B 87 97 87 6C DA 2F FC 35 71 91 C6 5E 08 CD E1 3E 92 B7 3F 4B A7 61 23 7C BD 30 5E 52 D8 85 19 20 1C 4E C6 1E 13 B1ci
私鑰InvQ:B4 12 D6 05 1C 2C 2B 6F B5 73 99 F3 B7 A7 08 6F A3 E8 2D 6F 33 A6 AE E5 BE 7B 89 86 7F 48 3B DD BC 4A 07 BF A4 A1 BB 96 BD 0E 46 F1 43 FA FB DE A0 1B AB 38 7D 49 59 45 EE 8C F9 3D 89 CF EB AC字符串
明文:11 22 33 44 55
經過調用.NET的RSA簽名接口,產生基於MD5的簽名後數據:56 E1 5E 29 84 D6 BC FB 87 7F 55 93 B4 E1 F3 75 2C 64 A5 BC 04 3A D7 0A DB 84 AD 8B 9C 4D D8 E6 8A 56 85 7B 2C 5E 50 E5 81 EB DC 40 D8 9A 29 64 54 19 5B F0 2B 77 D3 DB CF A2 17 BF 33 3F 19 19 B0 FF 36 53 D3 C2 36 1D 90 43 27 2C 0F 54 34 54 F7 E8 D2 09 75 E4 F1 A0 8B F5 38 EA 66 D6 53 14 E4 C5 B6 5A C7 74 52 6E 0A 16 C6 9B B7 81 0B 06 61 8A E7 41 BB 97 E6 EE 3E 6A 1C 7A E6 32 18 60
用公鑰對上面的數據解密後獲得:30 20 30 0C 06 08 2A 86 48 86 F7 0D 02 05 05 00 04 10 28 3D 4F EA 5D DE D5 9C F8 37 D3 04 73 28 F5 AF
這是一段TLV格式的數據,解析後
TAG |
名稱 |
長度 |
值 |
||
30 |
Sequence組合類型 |
20 |
|||
30 |
Sequence組合類型 |
0C |
|||
06 |
對象標識ObjectID |
08 |
2A 86 48 86 F7 0D 02 05 |
||
05 |
空類型 |
00 |
|||
04 |
字符串類型 |
10 |
28 3D 4F EA 5D DE D5 9C F8 37 D3 04 73 28 F5 AF |
能夠看到28 3D 4F EA 5D DE D5 9C F8 37 D3 04 73 28 F5 AF正好就是明文數據11 22 33 44 55的MD5值。
那麼上面這段數據的其它內容表示什麼意思呢?
這裏使用的編碼方法是BER(Basic Encoding Rule),BER的數據都是TLV格式的,每種TAG的定義以下:
0x01:BOOL
0x02:INT,整型
0x04:OCTSTR,字符串類型
0x05:NULL,空類型
0x06:OBJID,對象標識ObjectID(在這裏就是對應的HASH算法的OID編碼)
0x0A:ENUM
0x30:SEQ,Sequence組合類型
0x31:SETOF
0x40:IPADDR
0x41:COUNTER
0x42:GAUGE
0x43:TIMETICKS
0x44:OPAQUE
也就是說,每次基於不一樣的HASH算法對不一樣的數據進行簽名時,構造的這一段BER數據的基本格式是固定不變的,只是HASH算法的OID和哈希值會變而已。
下面講一下HASH算法的OID是怎麼編碼的。
每一個算法的OID都是固定的一串十進制數據,是國際權威組織定的。好比MD5的OID 是 1.2.840.113549.2.5 ,表示爲"iso(1) member-body (2) US (840) rsadsi(113549) digestAlgorithm (2) md5 (5)", 因此當解碼程序看到這個OID時,就知道是MD5散列.
對OID的編碼規則以下:前兩部分若是定義爲x.y, 那麼它們將合成一個字40*x + y, 其他部分單獨做爲一個字節進行編碼。每一個字首先被分割爲最少數量的沒有頭零數字的7位數字.這些數字以big-endian格式進行組織,而且一個接一個地組合成字節. 除了編碼的最後一個字節外,其餘全部字節的最高位(位8)都爲1。舉例: 30331 = 1 * 128^2 + 108 * 128 + 123 分割成7位數字(0x80)後爲{1,108,123}設置最高位後變成{129,236,123}.若是該字只有一個7位數字,那麼最高爲0。
規則不太好懂,仍是以MD5舉例:
1、將1.2.840.113549.2.5轉換成字數組 {42, 840, 113549, 2, 5}(由於前兩部分定義爲1.2,那麼合成一個字40*1+2=42)
2、將每一個字分割爲帶有最高位的7位數字。
42=42,只有一個7位數字,那麼最高爲0,結果爲{0x2A}
840= 6*128^1+72,除最後一個字節外,其餘字節的BIT8都置1,結果爲{0x86,0x48}
113549=6*128^2+119*128^1+13,除最後一個字節外,其餘字節的BIT8都置1,結果爲{0x86,0xF7,0x0D}
2=2, 只有一個7位數字,那麼最高爲0,結果爲{0x02}
5=5, 只有一個7位數字,那麼最高爲0,結果爲{0x05}
最終結果爲{{0x2A},{0x86,0x48},{0x86,0xF7,0x0D},{0x02},{0x05}}
3、加上TAG和LEN,獲得OID編碼爲 0x06 08 2A 86 48 86 F7 0D 02 05
RSA驗籤時的步驟:先用公鑰解密,解析TLV數據從中獲得HASH算法的OID和HASH值,根據OID選擇相應的HASH算法對明文進行計算,最後比對HASH值。
常見的HASH算法在用於RSA簽名時的BER數據編碼格式爲:
MD2 |
1.2.840.113549.2.2 |
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 02 05 00 04 10 || H. |
MD4 |
1.2.840.113549.2.4 |
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 04 05 00 04 10 || H. |
MD5 |
1.2.840.113549.2.5 |
30 20 30 0c 06 08 2a 86 48 86 f7 0d 02 05 05 00 04 10 || H |
SHA1 |
1.3.14.3.2.26 |
30 21 30 09 06 05 2b 0e 03 02 1a 05 00 04 14 || H |
SHA224 |
2.16.840.1.101.3.4.2.4 不肯定是否這個OID |
30 2D 30 0d 06 09 60 86 48 01 65 03 04 02 04 05 00 04 1C || H |
SHA256 |
2.16.840.1.101.3.4.2.1 |
30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 || H |
SHA384 |
2.16.840.1.101.3.4.2.2 |
30 41 30 0d 06 09 60 86 48 01 65 03 04 02 02 05 00 04 30 || H |
SHA512 |
2.16.840.1.101.3.4.2.3 |
30 51 30 0d 06 09 60 86 48 01 65 03 04 02 03 05 00 04 40 || H |
SM3 |
1.2.156.197.1.504 不肯定是否這個OID |
30 30 30 0c 06 08 2a 81 1C 81 45 01 83 78 05 00 04 20 || H. |