zoukankan      html  css  js  c++  java
  • RSA签名验签学习笔记

      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 41

    私钥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 05

    私钥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 B1

    私钥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.2.840.113549.2.5转换成字数组 {42, 840, 113549, 2, 5}(因为前两部分定义为1.2,那么合成一个字40*1+2=42)

    二、将每个字分割为带有最高位的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}}

    三、加上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.

  • 相关阅读:
    Linux-C基础知识学习:C语言作业-将5个学生成绩保存在一个数组中,单独实现一个计算平均成绩的average函数, 在main函数中获取该函数返回的平均值,并打印。
    Linux-C基础知识学习:C语言作业-输入两个数,将两个数交换,按升序输出。
    C语言学习:结构体(笔记)--未完待续
    C语言学习:结构体(笔记)
    PHP之函数
    PHP之流程控制
    PHP之常量和变量
    PHP之数据类型
    PHP之标记风格和注释
    VMware虚拟机中各类文件作用详解
  • 原文地址:https://www.cnblogs.com/jintianhu/p/5051169.html
Copyright © 2011-2022 走看看