前言

數字簽名,就是隻有信息的發送者才能產生的別人無法偽造的一段數字串,這段數字串同時也是對信息的發送者發送信息真實性的一個有效證明。 不清楚的請自行科普數字簽名。本篇主要來講簽名值具體是怎麼計算出來的~

一、動手解密簽名值

1、測試密鑰

//隨機產生RSA私鑰。因1024位的RSA密鑰已不安全,本次測試使用2048長度的RSA密鑰。
> openssl genrsa  2048
Generating RSA private key, 2048 bit long modulus
.....................+++
............................................................................................................................+++
e is 65537 (0x10001)
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqmk7bbqdfdDWgQZ/srfpBurN40Rw8QqBjoE8cujbF8zHrNJc
RlhHVRQ9HRwHAkG0xM5OzZjfzxHseJ+D3v84xEwHrd5l0t/iMVZxIOoyHk0gHDKv
kFAaSk+Rlo61bqxgjcRTNkz9mpA8hmG55fYsl1ktJ05YA4rHSv33Dp5OsPGIlO+H
RZRjdsu8iYfFUrPmfjoVZKPhiRHEeYtDxfyu3rUOlfdkLQyFmlrVJAMdatpM2paz
eSY3ypqR0mClQ3+t4GpGrCjXqkFYOXP/02YpDKrO04zHtOxZNnZz7r3YedCgzG4Z
uBvZGin3ig/X2fxZLJmmERhv9FizGvM541kA7wIDAQABAoIBACS/FGWQ/C0JP3gL
IrYzGji3oTYEqCYSEeXc0GAm/jefnN8TbXptxtP9zT/dr1U5PfXCVxPWh0xrnQZV
v2Xyuxb5Hh7L+kECrg/dh/+FANGv5+CsvVbtLv4fMlG47D61RQzM7PSknXsa5zJD
GIcSEoOAY1A6gJgi8N6m7QNl96oH0TiTbs4xIo8wTgwPR2fkXFB/XySqweOZBeNW
W6cGFb382lM2N51G72ElQe27+O214J/bHKDukoGgJ5GE2uI2r0xkIXYLsy+zeOdG
gi1UEqjLng596wvH55hog+4b2aPQP2yxnzvtzQELb6XY/oApgQQd5HH5JpWa8ec3
YJBmp0ECgYEA2Y9YyGp6zsRRTKFuqt1ISQltlFg6pxSAGJYbUPcLD2x67hXdfnhB
s1fvNvG+hYh65e80/HZca8JKTB4ETU0oOPQfzoGRqa5pIxv37QIruskyu2Skevlo
CMAT7EXO1CT5ewce1We1R3vUVIlBI3/JibsI89VyBlyDK2QnUKltBtkCgYEAyIVA
PWnYt0EsA22LdFLcgOQ/W/LMYRssaxpbde9yXqp7MKhhAGFq33IWWYXaGhBVbslC
Co5cLPrcPiNvB+lFPfFfmBTncU9FGnkDCiAGCufWuqHGSc2mmVnn/pdUdwqI+jSW
tqCYQl5YO5R39sbjTPyilWWAuS0lUS9rLm3juQcCgYEAr6p8II9Bi/SeRIbQqew0
sqyHK1G2QjReXfvOIKjo6FJKTDWhe720JxBome/GS2HxAfoMyZD0fRoLDbzq8lPL
l3keuYqLR/wI7o1luZyYHKDacs8HtDfv1ajqLUwMfeVBACK2tc+gYxDMWFnfG7/R
xoEb8G43PIW0b/PVft7eprkCgYAZK/kTfI0S/CBtUbwW3ywFFiIKBeG4MvQRgd6H
YIan8ZjDU+/RX2lOIYFCvbXSXciLvsIGlzZlAxzQxBv1D0h87ScF7WHcbIoNN7G0
/K4lglMHXLWKoEFQsOOZpx+YTf9CAYYF6QUUF8nVuN6SYQc5q+ExBevx0wQDPAOl
cXAL0wKBgENcM4Gc0yAn8dRP803/I90s9zgHapKTjDCw6o3q331VNq90orWQj4hp
bj2BovL6jH2OatpMEv3lLxHXT+pfG9VMGXV79h6AGo5VrLBAMPzAua1xr3nZTWiB
EQd+4FF41ku94XsCbOwEdNgxtIw33m7OZmgYzajSPILvvI35DNnH
-----END RSA PRIVATE KEY-----

// 通過生成的私鑰,得到公鑰
> openssl  rsa -pubout
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqmk7bbqdfdDWgQZ/srfpBurN40Rw8QqBjoE8cujbF8zHrNJc
RlhHVRQ9HRwHAkG0xM5OzZjfzxHseJ+D3v84xEwHrd5l0t/iMVZxIOoyHk0gHDKv
kFAaSk+Rlo61bqxgjcRTNkz9mpA8hmG55fYsl1ktJ05YA4rHSv33Dp5OsPGIlO+H
RZRjdsu8iYfFUrPmfjoVZKPhiRHEeYtDxfyu3rUOlfdkLQyFmlrVJAMdatpM2paz
eSY3ypqR0mClQ3+t4GpGrCjXqkFYOXP/02YpDKrO04zHtOxZNnZz7r3YedCgzG4Z
uBvZGin3ig/X2fxZLJmmERhv9FizGvM541kA7wIDAQABAoIBACS/FGWQ/C0JP3gL
IrYzGji3oTYEqCYSEeXc0GAm/jefnN8TbXptxtP9zT/dr1U5PfXCVxPWh0xrnQZV
v2Xyuxb5Hh7L+kECrg/dh/+FANGv5+CsvVbtLv4fMlG47D61RQzM7PSknXsa5zJD
GIcSEoOAY1A6gJgi8N6m7QNl96oH0TiTbs4xIo8wTgwPR2fkXFB/XySqweOZBeNW
W6cGFb382lM2N51G72ElQe27+O214J/bHKDukoGgJ5GE2uI2r0xkIXYLsy+zeOdG
gi1UEqjLng596wvH55hog+4b2aPQP2yxnzvtzQELb6XY/oApgQQd5HH5JpWa8ec3
YJBmp0ECgYEA2Y9YyGp6zsRRTKFuqt1ISQltlFg6pxSAGJYbUPcLD2x67hXdfnhB
s1fvNvG+hYh65e80/HZca8JKTB4ETU0oOPQfzoGRqa5pIxv37QIruskyu2Skevlo
CMAT7EXO1CT5ewce1We1R3vUVIlBI3/JibsI89VyBlyDK2QnUKltBtkCgYEAyIVA
PWnYt0EsA22LdFLcgOQ/W/LMYRssaxpbde9yXqp7MKhhAGFq33IWWYXaGhBVbslC
Co5cLPrcPiNvB+lFPfFfmBTncU9FGnkDCiAGCufWuqHGSc2mmVnn/pdUdwqI+jSW
tqCYQl5YO5R39sbjTPyilWWAuS0lUS9rLm3juQcCgYEAr6p8II9Bi/SeRIbQqew0
sqyHK1G2QjReXfvOIKjo6FJKTDWhe720JxBome/GS2HxAfoMyZD0fRoLDbzq8lPL
l3keuYqLR/wI7o1luZyYHKDacs8HtDfv1ajqLUwMfeVBACK2tc+gYxDMWFnfG7/R
xoEb8G43PIW0b/PVft7eprkCgYAZK/kTfI0S/CBtUbwW3ywFFiIKBeG4MvQRgd6H
YIan8ZjDU+/RX2lOIYFCvbXSXciLvsIGlzZlAxzQxBv1D0h87ScF7WHcbIoNN7G0
/K4lglMHXLWKoEFQsOOZpx+YTf9CAYYF6QUUF8nVuN6SYQc5q+ExBevx0wQDPAOl
cXAL0wKBgENcM4Gc0yAn8dRP803/I90s9zgHapKTjDCw6o3q331VNq90orWQj4hp
bj2BovL6jH2OatpMEv3lLxHXT+pfG9VMGXV79h6AGo5VrLBAMPzAua1xr3nZTWiB
EQd+4FF41ku94XsCbOwEdNgxtIw33m7OZmgYzajSPILvvI35DNnH
-----END RSA PRIVATE KEY-----
writing RSA key
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqmk7bbqdfdDWgQZ/srfp
BurN40Rw8QqBjoE8cujbF8zHrNJcRlhHVRQ9HRwHAkG0xM5OzZjfzxHseJ+D3v84
xEwHrd5l0t/iMVZxIOoyHk0gHDKvkFAaSk+Rlo61bqxgjcRTNkz9mpA8hmG55fYs
l1ktJ05YA4rHSv33Dp5OsPGIlO+HRZRjdsu8iYfFUrPmfjoVZKPhiRHEeYtDxfyu
3rUOlfdkLQyFmlrVJAMdatpM2pazeSY3ypqR0mClQ3+t4GpGrCjXqkFYOXP/02Yp
DKrO04zHtOxZNnZz7r3YedCgzG4ZuBvZGin3ig/X2fxZLJmmERhv9FizGvM541kA
7wIDAQAB
-----END PUBLIC KEY-----

2、編寫代碼解密

眾所周知:

RSA加密解密:私鑰解密,公鑰加密。

RSA數字簽名-俗稱加簽驗簽:私鑰加簽,公鑰驗簽。

其實:

也是有私鑰加密,公鑰解密的。只是因為公鑰是公開的,私鑰加密後所有人都可以解密,沒有意義,所以常用簽名,而不是加密。

私鑰加簽的本質也是私鑰加密數據的Hash值。

這裡有個小技巧:我們用公鑰對簽名值解密,使用RSANOPADDING,這樣就能得到 簽名時私鑰加密的數據。

鑒於篇幅長度,代碼只貼出關鍵部分。

代碼之PHP:

/**
    * 創建簽名
    * @param string $data 數據
    * @return null|string
    */
   public function createSign($data = "")
   {
       if (!is_string($data)) {
           return null;
       }
       openssl_sign($data, $sign, self::getPrivateKey(),OPENSSL_ALGO_SHA256);
       return  $sign;
   }

   /**
    * 公鑰解密數據
    * @param string $data 數據
    * @return null|string
    */
   public function decData($data = "")
   {
       if (!is_string($data)) {
           return null;
       }
       openssl_public_decrypt($data, $encData, self::getPublicKey(),OPENSSL_NO_PADDING);
       return  $encData;
   }

   /**
    * 公鑰解密數據
    * @param string $data 數據
    * @return null|string
    */
   public function decDataPKCS1Padding($data = "")
   {
       if (!is_string($data)) {
           return null;
       }
       openssl_public_decrypt($data, $encData, self::getPublicKey(),OPENSSL_PKCS1_PADDING);
       return  $encData;
   }

其他語言代碼整理ing...

本次測試 java、js、C#、PHP。結果均一致,如下:

使用示例私鑰進行簽名,得到如下結果
簽名值Base64: UjV/9zDoYcXTKKKDlWhFshQ08ikknKWqhCig8J7VhVrFCF+tFUQ85xnCUA0KR/t9CVFcf7SDA6ntr/T2xJ4T9TiAHEmNIhghZTlOsp+ieyvB5N4jQ6fuK6DjdtK/icklK5fmMozbKKiHwjr33lWY8NTfXydtKgd/5fPIVhUB26TyOKiO7JY3iAlylqfIpEy4g3fsxiiPGe022jIt400re1UCyZWbhXqdr2JUE7hAZEcMcbxvZff4Ilh6hHZ9rAkWBU1E2vsQUnnvdDK3BOQpZQvTtfIHbX+lhT5UcsFJrwOuONo0nG0s7MSjCsjQEf7iucEZpZeUYwOeMexVHsU9rw==
簽名值Hex: 52357ff730e861c5d328a283956845b21434f229249ca5aa8428a0f09ed5855ac5085fad15443ce719c2500d0a47fb7d09515c7fb48303a9edaff4f6c49e13f538801c498d22182165394eb29fa27b2bc1e4de2343a7ee2ba0e376d2bf89c9252b97e6328cdb28a887c23af7de5598f0d4df5f276d2a077fe5f3c8561501dba4f238a88eec963788097296a7c8a44cb88377ecc6288f19ed36da322de34d2b7b5502c9959b857a9daf625413b84064470c71bc6f65f7f822587a84767dac0916054d44dafb105279ef7432b704e429650bd3b5f2076d7fa5853e5472c149af03ae38da349c6d2cecc4a30ac8d011fee2b9c119a5979463039e31ec551ec53daf

使用示例公鑰進行NOPADDING解密,得到如下結果
原數據Base64: AAH/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////ADAxMA0GCWCGSAFlAwQCAQUABCC5TSe5k00+CKUuUtfafav6xITv43pTgO6QiPes4u/N6Q==
原數據Hex: 0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d060960864801650304020105000420b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

使用示例公鑰進行PKCS1_PADDING解密,得到如下結果
原數據Base64: MDEwDQYJYIZIAWUDBAIBBQAEILlNJ7mTTT4IpS5S19p9q/rEhO/jelOA7pCI96zi783p
原數據Hex: 3031300d060960864801650304020105000420b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

簡單分析

1. 字元串"hello world"進行sha256運算得到hash: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

2. 在Hash結果前數據填充:3031300d060960864801650304020105000420

3. PKCS1 在上一步結果前填充:

0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00

二、結合資料分析RSA的補位

1、簽名時,對Hash值的數據填充方式  

對hash演算法id和hash值進行ASN.1的DER編碼。如下:

      DigestInfo ::= SEQUENCE {          digestAlgorithm AlgorithmIdentifier,          digest OCTET STRING      }

為方便理解,我們使用ASN1dump對示例中的數據做解析:

3031300d060960864801650304020105000420b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

直接上圖:

可以看到sha256的演算法id,2.16.840.1.101.3.4.2.1:

數據也在其中,另附上部分演算法id:

另因為各個hash演算法id是固定的,計算的結果也是固定的。所以不同的hash演算法的填充也是固定的。如下:

md2:"3020300c06082a864886f70d020205000410",
md5:"3020300c06082a864886f70d020505000410",
sha1:"3021300906052b0e03021a05000414",
sha224:"302d300d06096086480165030402040500041c",
sha256:"3031300d060960864801650304020105000420",
sha384:"3041300d060960864801650304020205000430",
sha512:"3051300d060960864801650304020305000440",
ripemd160: "3021300906052b2403020105000414"

2、pkcs1padding V1.5的填充模式,參考rfc2312

以下描述均以十六進位字元串來描述。

pkcs1padding V1.5的填充模式方式如下:

EB = 00+BT+PS +00 + D

即:加密塊=00+塊類型+填充字元+00+數據

1. 開頭00是為了確保塊轉換為整數的時候 小於模數

2. BT(Block Type):當使用私鑰操作,塊類型為00或01,公鑰操作,塊類型為02。塊類型為00,數據開頭必須不能是00,因為填充的也是00,將無法解析。塊類型為01或02,塊可以被準確解析,因為不會是00來填充。

3. PS(Padding String):k-3-||D|| 個位元組組組成,k表示密鑰的位元組長度, D表示明文數據D的位元組長度 。當BT為01時,填充位元組值為FF,BT為00時,填充位元組值為00,BT為02時填充隨機數(非00)。填充長度至少為8個位元組

4. 00,用於分開PS和D

5. D,數據原文(HEX)

注意:2048位的RSA密鑰,加密塊長度也必須是2048位,也就是256個位元組。所以示例中的加密塊需要填充202個FF才夠256個位元組。

故簽名的時候,  加密的塊:00+01+FF(202個) +00 + ">3031300d060960864801650304020105000420b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9

三、測試題

最後拋了兩個問題,看看大家有沒有理解上面所介紹的內容。 

1、RSA簽名的時候 值是固定, 公鑰加密的結果確實隨機的,為什麼?

2、分析如下代碼,是否有問題?

*

本文作者:liang亮,本文屬 FreeBuf 原創獎勵計劃,未經許可禁止轉載

推薦閱讀:

查看原文 >>
相關文章