【導讀】什么是對稱加密?Go 語言做對稱加密怎么做?本文作者從加密原理到代碼實現帶你上車。
對稱加密中,加密和解密使用相同的密鑰,因此必須向解密者配送密鑰,即密鑰配送問題。而非對稱加密中,由于加密和解密分別使用公鑰和私鑰,而公鑰是公開的,因此可以規避密鑰配送問題。非對稱加密算法,也稱公鑰加密算法。
1977 年,Ron Rivest、Adi Shamir、Leonard Adleman 三人在美國公布了一種公鑰加密算法,即 RSA 公鑰加密算法。RSA 是目前最有影響力和最常用的公鑰加密算法,可以說是公鑰加密算法的事實標準。
一、RSA 加密原理
使用 M 和 C 分別表示明文和密文,則 RSA 加密、解密過程如下:
img
其中 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 解密的本質是模冪運算
img
其中 C 為密文,(d,n) 為私鑰,均為超過 1024 位的大數運算,直接計算并不可行,因此最經典的算法為蒙哥馬利算法。而這種計算是比較是耗時的,因此者可以觀察不同的輸入對應的解密時間,通過分析推斷私鑰,稱為計時。而防范 RSA 計時的辦法,即在解密時加入隨機因素,使得***者無法準確獲取解密時間。
二、Go RSA 加密解密
1、rsa 加解密,必然會去查 crypto/ras 這個包
Package rsa implements RSA encryption as specified in PKCS#1.
這是該包的說明:實現 RSA 加密技術,基于 PKCS#1 規范。
對于什么是 PKCS#1,可以查閱相關資料。PKCS(公鑰密碼標準),而#1 就是 RSA 的標準。可以查看:PKCS 系列簡介
從該包中函數的名稱,可以看到有兩對加解密的函數。
EncryptOAEP 和 DecryptOAEPEncryptPKCS1v15 和 DecryptPKCS1v15
這稱作加密方案,詳細可以查看,PKCS #1 v2.1 RSA 算法標準
可見,當與其他語言交互時,需要確定好使用哪種方案。
PublicKey 和 PrivateKey 兩個類型分別代表公鑰和私鑰,關于這兩個類型中成員該怎么設置,這涉及到 RSA 加密算法,本文中,這兩個類型的實例通過解析文章開頭生成的密鑰得到。
2、解析密鑰得到 PublicKey 和 PrivateKey 的實例
這個過程,我也是花了好些時間(主要對各種加密的各種東東不熟):怎么將 openssl 生成的密鑰文件解析到公鑰和私鑰實例呢?
在 encoding/pem 包中,看到了—–BEGIN Type—–這樣的字樣,這正好和 openssl 生成的密鑰形式差不多,那就試試。
在該包中,一個 block 代表的是 PEM 編碼的結構,關于 PEM,請查閱相關資料。我們要解析密鑰,當然用 Decode 方法:
func Decode(data []byte) (p *Block, rest []byte)
這樣便得到了一個 Block 的實例(指針)。
解析來看 crypto/x509。為什么是 x509 呢?這又涉及到一堆概念。先不管這些,我也是看 encoding 和 crypto 這兩個包的子包摸索出來的。在 x509 包中,有一個函數:
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)
從該函數的說明:ParsePKIXPublicKey parses a DER encoded public key. These values are typically found in PEM blocks with “BEGIN PUBLIC KEY”??梢娺@就是解析 PublicKey 的。另外,這里說到了 PEM,可以上面的 encoding/pem 對了。(PKIX 是啥東東,查看這里 )
而解析私鑰的,有好幾個方法,從上面的介紹,我們知道,RSA 是 PKCS#1,剛好有一個方法:
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)
返回的就是 rsa.PrivateKey。
代碼實現:
package main
import (
“crypto/rsa”
“crypto/rand”
“crypto/x509”
“encoding/pem”
“os”
“fmt”
)
func RSAGenKey(bits int) error {
/*
生成私鑰
*/
//1、使用 RSA 中的 GenerateKey 方法生成私鑰
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return err
}
//2、通過 X509 標準將得到的 RAS 私鑰序列化為:ASN.1 的 DER 編碼字符串
privateStream := x509.MarshalPKCS1PrivateKey(privateKey)
//3、將私鑰字符串設置到 pem 格式塊中
block1 := pem.Block{
Type: “private key”,
Bytes: privateStream,
}
//4、通過 pem 將設置的數據進行編碼,并寫入磁盤文件
fPrivate, err := os.Create(“privateKey.pem”)
if err != nil {
return err
}
defer fPrivate.Close()
err = pem.Encode(fPrivate, &block1)
if err != nil {
return err
}
/*
生成公鑰
*/
publicKey:=privateKey.PublicKey
publicStream,err:=x509.MarshalPKIXPublicKey(&publicKey)
//publicStream:=x509.MarshalPKCS1PublicKey(&publicKey)
block2:=pem.Block{
Type:“public key”,
Bytes:publicStream,
}
fPublic,err:=os.Create(“publicKey.pem”)
if err!=nil {
return err
}
defer fPublic.Close()
pem.Encode(fPublic,&block2)
return nil
}
//對數據進行加密操作
func EncyptogRSA(src []byte,path string) (res []byte,err error) {
//1. 獲取秘鑰(從本地磁盤讀?。?/p>
f,err:=os.Open(path)
if err!=nil {
return
}
defer f.Close()
fileInfo,_:=f.Stat()
b:=make([]byte,fileInfo.Size())
f.Read(b)
// 2、將得到的字符串解碼
block,_:=pem.Decode(b)
// 使用 X509 將解碼之后的數據 解析出來
//x509.MarshalPKCS1PublicKey(block): 解析之后無法用,所以采用以下方法:ParsePKIXPublicKey
keyInit,err:=x509.ParsePKIXPublicKey(block.Bytes) //對應于生成秘鑰的 x509.MarshalPKIXPublicKey(&publicKey)
//keyInit1,err:=x509.ParsePKCS1PublicKey(block.Bytes)
if err!=nil {
return
}
//4. 使用公鑰加密數據
pubKey:=keyInit.(*rsa.PublicKey)
res,err=rsa.EncryptPKCS1v15(rand.Reader,pubKey,src)
return
}
//對數據進行解密操作
func DecrptogRSA(src []byte,path string)(res []byte,err error) {
//1. 獲取秘鑰(從本地磁盤讀取)
f,err:=os.Open(path)
if err!=nil {
return
}
defer f.Close()
fileInfo,_:=f.Stat()
b:=make([]byte,fileInfo.Size())
f.Read(b)
block,_:=pem.Decode(b)//解碼
privateKey,err:=x509.ParsePKCS1PrivateKey(block.Bytes)//還原數據
res,err=rsa.DecryptPKCS1v15(rand.Reader,privateKey,src)
return
}
func main() {
//rsa.GenerateKey()
err:=RSAGenKey(4096)
if err!=nil {
fmt.Println(err)
return
}
fmt.Println(“秘鑰生成成功!”)
str:=“山重水復疑無路,柳暗花明又一村!”
fmt.Println(“加密之前的數據為:”,string(str))
data,err:=EncyptogRSA([]byte(str),“publicKey.pem”)
data,err=DecrptogRSA(data,“privateKey.pem”)
fmt.Println(“加密之后的數據為:”,string(data))
}
責任編輯:haq
-
代碼
+關注
關注
30文章
4748瀏覽量
68355 -
RSA
+關注
關注
0文章
59瀏覽量
18863
原文標題:GO 語言 RSA 加密解密
文章出處:【微信號:gh_3980db2283cd,微信公眾號:開關電源芯片】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論