區塊鏈背後的資訊保安(4)RSA加解密及簽名演算法的技術原理及其Go語言實現
# RSA加解密及簽名演算法的技術原理及其Go語言實現
對稱加密中,加密和解密使用相同的金鑰,因此必須向解密者配送金鑰,即金鑰配送問題。
而非對稱加密中,由於加密和解密分別使用公鑰和私鑰,而公鑰是公開的,因此可以規避金鑰配送問題。
非對稱加密演算法,也稱公鑰加密演算法。
1977年,Ron Rivest、Adi Shamir、Leonard Adleman三人在美國公佈了一種公鑰加密演算法,即RSA公鑰加密演算法。
RSA是目前最有影響力和最常用的公鑰加密演算法,可以說是公鑰加密演算法的事實標準。
### RSA加密原理
使用M和C分別表示明文和密文,則RSA加密、解密過程如下:
其中e、n的組合(e, n)即為公鑰,d、n的組合(d, n)即為私鑰。
當然e、d、n並非任意取值,需要符合一定條件,如下即為e、d、n的求解過程。
### 生成金鑰對
e、d、n的求解過程,也即生成金鑰對的過程。涉及如下步驟:
* 1、取兩個大質數(也稱素數)p、q,n = pq。
* 2、取正整數e、d,使得ed mod (p-1)(q-1) = 1,也即:ed ≡ 1 mod (p-1)(q-1)。
e和d是模(p-1)(q-1)的乘法逆元,僅當e與(p-1)(q-1)互質時,存在d。
舉例驗證:
* 1、取p、q分別為13、17,n = pq = 221。
* 2、而(p-1)(q-1) = 12x16 = 192,取e、d分別為13、133,有13x133 mod 192 = 1
取明文M = 60,公鑰加密、私鑰解密,加密和解密過程分別如下:
### RSA加密原理證明過程
### 手動求解金鑰對中的d
ed mod (p-1)(q-1) = 1,已知e和(p-1)(q-1)求d,即求e對模(p-1)(q-1)的乘法逆元。
如上面例子中,p、q為13、17,(p-1)(q-1)=192,取e=13,求13d mod 192 = 1中的d。
13d ≡ 1 (mod 192),在右側新增192的倍數,使計算結果可以被13整除。
13d ≡ 1 + 192x9 ≡ 13x133 (mod 192),因此d = 133
其他計算方法有:費馬小定律、擴充套件歐幾里得演算法、尤拉定理。
### RSA安全性
由於公鑰公開,即e、n公開。
因此破解RSA私鑰,即為已知e、n情況下求d。
因ed mod (p-1)(q-1) = 1,且n=pq,因此該問題演變為:對n質因數分解求p、q。
目前已被證明,已知e、n求d和對n質因數分解求p、q兩者是等價的。
實際中n長度為2048位以上,而當n>200位時分解n是非常困難的,因此RSA演算法目前仍被認為是安全實用的。
### RSA計時攻擊和防範
RSA解密的本質是模冪運算,即:
其中C為密文,(d,n)為私鑰,均為超過1024位的大數運算,直接計算並不可行,因此最經典的演算法為蒙哥馬利演算法。
而這種計算是比較是耗時的,因此攻擊者可以觀察不同的輸入對應的解密時間,通過分析推斷私鑰,稱為計時攻擊。
而防範RSA計時攻擊的辦法,即在解密時加入隨機因素,使得攻擊者無法準確獲取解密時間。
具體實現步驟如下:
### go標準庫中的RSA加解密實現
go標準庫中解密即實現了對計時攻擊的防範,程式碼如下:
```go
//加密
//m為明文
//(pub.E, pub.N)為公鑰
//c為密文
func encrypt(c *big.Int, pub *PublicKey, m *big.Int) *big.Int {
e := big.NewInt(int64(pub.E))
c.Exp(m, e, pub.N)
return c
}
//解密
//傳入random支援防範計時攻擊
func decrypt(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int, err error) {
if c.Cmp(priv.N) > 0 {
err = ErrDecryption
return
}
if priv.N.Sign() == 0 {
return nil, ErrDecryption
}
var ir *big.Int
if random != nil {
var r *big.Int
for {
//步驟1產生0至n-1之間隨機數r
r, err = rand.Int(random, priv.N)
if err != nil {
return
}
if r.Cmp(bigZero) == 0 {
r = bigOne
}
var ok bool
//r的模n的乘法逆元ir,步驟4中使用
ir, ok = modInverse(r, priv.N)
if ok {
break
}
}
bigE := big.NewInt(int64(priv.E))
//計算步驟2中C'
rpowe := new(big.Int).Exp(r, bigE, priv.N) // N != 0
cCopy := new(big.Int).Set(c)
cCopy.Mul(cCopy, rpowe)
cCopy.Mod(cCopy, priv.N)
c = cCopy
}
if priv.Precomputed.Dp == nil {
//步驟3,使用C'計算對應的M'
m = new(big.Int).Exp(c, priv.D, priv.N)
} else {
//略
}
if ir != nil {
//步驟4計算實際的M
m.Mul(m, ir)
m.Mod(m, priv.N)
}
return
}
//程式碼位置src/crypto/rsa/rsa.go
```
### RSA簽名和驗籤的原理
非對稱加密演算法,除支援加密外,還可以實現簽名。原理如下:
簽名:
* 1、提取訊息摘要,使用傳送方私鑰對訊息摘要加密,生成訊息簽名。
* 2、將訊息簽名和訊息一起,使用接收方公鑰加密,獲得密文。
驗籤:
* 1、使用接收方私鑰對密文解密,獲得訊息和訊息簽名。
* 2、使用傳送方公鑰解密訊息簽名,獲得訊息摘要。
* 3、使用相同辦法重新提取訊息摘要,與上一步中訊息摘要對比,如相同則驗籤成功。
附示意圖如下:
### go標準庫中的RSA簽名和驗籤實現
程式碼如下:
```go
//簽名
func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
//雜湊提取訊息摘要
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
if err != nil {
return nil, err
}
tLen := len(prefix) + hashLen
k := (priv.N.BitLen() + 7) / 8
if k < tLen+11 {
return nil, ErrMessageTooLong
}
// EM = 0x00 || 0x01 || PS || 0x00 || T
em := make([]byte, k)
em[1] = 1
for i := 2; i < k-tLen-1; i++ {
em[i] = 0xff
}
//整合訊息摘要和訊息體
copy(em[k-tLen:k-hashLen], prefix)
copy(em[k-hashLen:k], hashed)
m := new(big.Int).SetBytes(em)
//使用傳送方私鑰加密訊息摘要和訊息體,即為簽名
c, err := decryptAndCheck(rand, priv, m)
if err != nil {
return nil, err
}
copyWithLeftPad(em, c.Bytes())
return em, nil
}
//驗證簽名
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
//雜湊提取訊息摘要
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed))
if err != nil {
return err
}
tLen := len(prefix) + hashLen
k := (pub.N.BitLen() + 7) / 8
if k < tLen+11 {
return ErrVerification
}
c := new(big.Int).SetBytes(sig)
//使用傳送方公鑰解密,提取訊息體和訊息簽名
m := encrypt(new(big.Int), pub, c)
em := leftPad(m.Bytes(), k)
// EM = 0x00 || 0x01 || PS || 0x00 || T
//對比傳送方和接收方訊息體、以及訊息簽名
ok := subtle.ConstantTimeByteEq(em[0], 0)
ok &= subtle.ConstantTimeByteEq(em[1], 1)
ok &= subtle.ConstantTimeCompare(em[k-hashLen:k], hashed)
ok &= subtle.ConstantTimeCompare(em[k-tLen:k-hashLen], prefix)
ok &= subtle.ConstantTimeByteEq(em[k-tLen-1], 0)
for i := 2; i < k-tLen-1; i++ {
ok &= subtle.ConstantTimeByteEq(em[i], 0xff)
}
if ok != 1 {
return ErrVerification
}
return nil
}
//程式碼位置src/crypto/rsa/pkcs1v15.go
```
### 後記
RSA演算法中使用了大量數論知識,有關數論知識還有待學習。
待續。
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- 區塊鏈背後的資訊保安(3)橢圓曲線加解密及簽名演算法的技術原理及其Go語言實現區塊鏈解密演算法Go
- 區塊鏈背後的資訊保安(1)AES加密演算法原理及其GO語言實現區塊鏈加密演算法Go
- 區塊鏈背後的資訊保安(2) DES、3DES加密演算法原理及其GO語言實現區塊鏈3D加密演算法Go
- 兄弟連區塊鏈教程區塊鏈資訊保安3橢圓曲線加解密及簽名演算法的技術原理二區塊鏈解密演算法
- 區塊鏈背後的資訊保安(5) 對稱加密演算法的分組模式及其Go語言實現區塊鏈加密演算法模式Go
- 兄弟連區塊鏈教程區塊鏈背後的資訊保安2DES、3DES加密演算法原理二區塊鏈3D加密演算法
- 實戰區塊鏈技術培訓之Go語言區塊鏈Go
- GO語言實現區塊鏈Part4 Transactions 1Go區塊鏈
- 前後端java+vue 實現rsa 加解密與摘要簽名演算法後端JavaVue解密演算法
- GIFTO背後區塊鏈技術的分類區塊鏈
- 區塊鏈,中心去,何曾著眼看君王?用Go語言實現區塊鏈技術,透過Golang秒懂區塊鏈區塊鏈Golang
- 【區塊鏈技術實現】區塊鏈
- GO語言實現區塊鏈Part7 NetworkGo區塊鏈
- GO語言實現區塊鏈Part6 Transactions 2Go區塊鏈
- GO語言實現區塊鏈Part3 Persistence and CLIGo區塊鏈
- GO語言實現區塊鏈Part1 Basic PrototypeGo區塊鏈
- 雲+區塊鏈 實現區塊鏈技術的普惠應用區塊鏈
- GO語言實現區塊鏈Part5 AddressesGo區塊鏈
- GO語言實現區塊鏈Part2 Proof-of-WorkGo區塊鏈
- 蘋果超級簽名的實現過程、技術原理蘋果
- 區塊鏈-NFT 的實現原理區塊鏈
- 一圖看懂區塊鏈的工作原理、技術及用例區塊鏈
- 使用 Go 語言打造區塊鏈(二)Go區塊鏈
- RSA加密解密及數字簽名Java實現加密解密Java
- RSA演算法原理——(3)RSA加解密過程及公式論證演算法解密公式
- 區塊鏈不談技術的都是韭菜——區塊鏈技術組成及架構區塊鏈架構
- 區塊鏈名詞術語彙總區塊鏈
- 【區塊鏈技術】區塊鏈的一些相關演算法區塊鏈演算法
- 區塊鏈基本原理及其技術實現 - 姜家志 | JTalk 第五期區塊鏈
- 區塊鏈的原理與golang實現例子區塊鏈Golang
- 區塊鏈的核心技術區塊鏈
- go 語言與區塊鏈基礎講解Go區塊鏈
- 區塊鏈開發之Go語言—IO操作區塊鏈Go
- (二)區塊鏈的共識演算法:PoS 及其 例子 程式碼 實現區塊鏈演算法
- 用Java程式碼實現區塊鏈技術Java區塊鏈
- 區塊鏈技術起源及應用區塊鏈
- Go語言的 10 個實用技術Go
- 區塊鏈技術應用及發展 區塊鏈技術開發公司哪家好區塊鏈