1. 簡介
在對稱密碼中缠沈,由于加密和解密的密鑰是相同的,因此必須向接收者配送密鑰。用于解密的密鑰必須被配送給
接收者饲趋,這一問題稱為密鑰配送問題拐揭。如果使用非對稱加密也可以稱為公鑰密碼,則無需向接收者配送用于解
密的密鑰奕塑,這樣就解決了密鑰配送問題堂污。可以說非對稱加密是密碼學(xué)歷史上最偉大的發(fā)明爵川。
非對稱加密中敷鸦,密鑰分為加密密鑰和解密密鑰兩種。發(fā)送者用加密密鑰對消息進(jìn)行加密寝贡,接收者用解密密鑰對
密文進(jìn)行解密扒披。要理解公鑰密碼,清楚地區(qū)分加密密鑰和解密密鑰是非常重要的圃泡。加密密鑰是發(fā)送者加密時使
用的碟案,而解密密鑰則是接收者解密時使用的。
仔細(xì)思考一下加密密鑰和解密密鑰的區(qū)別颇蜡,我們可以發(fā)現(xiàn):
- 發(fā)送者只需要加密密鑰
- 接收者只需要解密密鑰
- 解密密鑰不可以被竊聽者獲取
- 加密密鑰被竊聽者獲取也沒問題
也就是說价说,解密密鑰從一開始就是由接收者自己保管的,因此只要將加密密鑰發(fā)給發(fā)送者就可以解決密鑰配送
問題了风秤,而根本不需要配送解密密鑰鳖目。
RSA是一種非對稱加密算法,它的名字是由它的三位開發(fā)者缤弦,即RonRivest领迈、AdiShamir和LeonardAdleman 的姓氏 的首字母組成的(Rivest-Shamir-Leonard)。
2. RSA加密
在RSA中碍沐,明文狸捅、密鑰和密文都是數(shù)字。RSA的 加密過程可以用下列公式來表達(dá)累提,如下尘喝。
密文 = 明文^E mod N
也就是說,RSA的密文是對代表明文的數(shù)字的E次方求modN的結(jié)果斋陪。換句話說朽褪,就是將明文自己做E次乘法,然 后將其結(jié)果除以N求余數(shù)无虚,這個余數(shù)就是密文鞍匾。
加密公式中出現(xiàn)的兩個數(shù)一一一E和N,到底都是什么數(shù)呢?RSA的加密是求明文的E次方modN骑科,因此只 要知道E和N這兩個數(shù)橡淑,任何人都可以完成加密的運(yùn)算。所以說咆爽,E和N是RSA加密的密鑰梁棠,也就是說置森, E和N的組 合就是公鑰 。
不過符糊,E和N并不是隨便什么數(shù)都可以的凫海,它們是經(jīng)過嚴(yán)密計算得出的。
有一個很容易引起誤解的地方需要大家注意一一E和N這兩個數(shù)并不是密鑰對(公鑰和私鑰的密鑰對)男娄。E和N兩 個數(shù)才組成了一個公鑰行贪,因此我們一般會寫成 “公鑰是(E,N)” 或者 “公鑰是{E, N}" 這樣的形式模闲,將E和N用括號 括起來建瘫。
3. RSA解密
RSA的解密和加密一樣簡單,可以用下面的公式來表達(dá)
明文 = 密文^D mod N
也就是說尸折,對表示密文的數(shù)字的D次方求modN就可以得到明文啰脚。換句話說,將密文自己做D次乘法实夹,再對其結(jié)果 除以N求余數(shù)橄浓,就可以得到明文。
這里所使用的數(shù)字N和加密時使用的數(shù)字N是相同的亮航。 數(shù)D和數(shù)N組合起來就是RSA的解密密鑰荸实,因此D和N的組合 就是私鑰 。只有知道D和N兩個數(shù)的人才能夠完成解密的運(yùn)算缴淋。
在RSA中准给,加密和解密的形式是相同的。加密是求 "E次方的mod N”宴猾,而解密則是求 "D次 方的modN” 。
當(dāng)然叼旋,D也并不是隨便什么數(shù)都可以的仇哆,作為解密密鑰的D,和數(shù)字E有著相當(dāng)緊密的聯(lián)系夫植。否則讹剔,用E加密的結(jié) 果可以用D來解密這樣的機(jī)制是無法實(shí)現(xiàn)的。
4. Go中生成公鑰和私鑰
- 生成私鑰操作流程概述:
- 使用rsa中的GenerateKey方法生成私鑰
- 通過x509標(biāo)準(zhǔn)將得到的ras私鑰序列化為ASN.1 的 DER編碼字符串
- 將私鑰字符串設(shè)置到pem格式塊中
- 通過pem將設(shè)置好的數(shù)據(jù)進(jìn)行編碼, 并寫入磁盤文件中
- 生成公鑰操作流程:
- 從得到的私鑰對象中將公鑰信息取出
- 通過x509標(biāo)準(zhǔn)將得到 的rsa公鑰序列化為字符串
- 將公鑰字符串設(shè)置到pem格式塊中
- 通過pem將設(shè)置好的數(shù)據(jù)進(jìn)行編碼, 并寫入磁盤文件
- 生成公鑰和私鑰的源代碼:
/*
* 生成RSA公鑰和私鑰并保存在對應(yīng)的目錄文件下
* 參數(shù)bits: 指定生成的秘鑰的長度, 單位: bit
*/
func RsaGenKey(bits int, privatePath,pubulicPath string) error {
// 1. 生成私鑰文件
// GenerateKey函數(shù)使用隨機(jī)數(shù)據(jù)生成器random生成一對具有指定字位數(shù)的RSA密鑰
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return err
}
// 2. MarshalPKCS1PrivateKey將rsa私鑰序列化為ASN.1 PKCS#1 DER編碼
derPrivateStream := x509.MarshalPKCS1PrivateKey(privateKey)
// 3. Block代表PEM編碼的結(jié)構(gòu), 對其進(jìn)行設(shè)置
block := pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: derPrivateStream,
}
// 4. 創(chuàng)建文件
privateFile, err := os.Create(privatePath)
defer privateFile.Close()
if err != nil {
return err
}
// 5. 使用pem編碼, 并將數(shù)據(jù)寫入文件中
err = pem.Encode(privateFile, &block)
if err != nil {
return err
}
// 1. 生成公鑰文件
publicKey := privateKey.PublicKey
derPublicStream, err := x509.MarshalPKIXPublicKey(&publicKey)
if err != nil {
return err
}
block = pem.Block{
Type: "RSA PUBLIC KEY",
Bytes: derPublicStream,
}
publicFile, err := os.Create(pubulicPath)
defer publicFile.Close()
if err != nil {
return err
}
// 2. 編碼公鑰, 寫入文件
err = pem.Encode(publicFile, &block)
if err != nil {
panic(err)
return err
}
return nil
}
測試代碼:
func testGenRSA() {
rsa.RsaGenKey(2048, "privateKey.pem","pubulicKey.pem")
}
5. Go中使用RSA
5.1. 操作步驟
- 公鑰加密
- 將公鑰文件中的公鑰讀出, 得到使用pem編碼的字符串
- 將得到的字符串解碼
- 使用x509將編碼之后的公鑰解析出來
- 使用得到的公鑰通過rsa進(jìn)行數(shù)據(jù)加密
- 公鑰解密
- 將私鑰文件中的私鑰讀出, 得到使用pem編碼的字符串
- 將得到的字符串解碼
- 使用x509將編碼之后的私鑰解析出來
- 使用得到的私鑰通過rsa進(jìn)行數(shù)據(jù)解密
5.2. 代碼實(shí)現(xiàn)
- RSA公鑰加密
/*
* RSA公鑰加密
*/
func RSAEncrypt(src []byte, filename string) ([]byte, error) {
// 根據(jù)文件名讀出文件內(nèi)容
file, err := os.Open(filename)
if err != nil {
return nil, err
}
defer file.Close()
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
// 從數(shù)據(jù)中找出pem格式的塊
block, _ := pem.Decode(buf)
if block == nil {
return nil, err
}
// 解析一個der編碼的公鑰
publicKey, err := x509.ParsePKCS1PublicKey(block.Bytes)
if err != nil {
return nil, err
}
// 公鑰加密
result, _ := rsa.EncryptPKCS1v15(rand.Reader, publicKey, src)
return result, nil
}
- RSA私鑰解密
/*
* RSA私鑰解密
*/
func RSADecrypt(src []byte, filename string) ([]byte, error) {
// 根據(jù)文件名讀出內(nèi)容
file, err := os.Open(filename)
if err != nil {
return nil,err
}
defer file.Close()
info, _ := file.Stat()
buf := make([]byte, info.Size())
file.Read(buf)
// 從數(shù)據(jù)中解析出pem塊
block, _ := pem.Decode(buf)
if block == nil {
return nil,err
}
// 解析出一個der編碼的私鑰
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
// 私鑰解密
result, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, src)
if err != nil {
return nil,err
}
return result,nil
}
- 測試代碼:
func testRSA() {
msg := "二愣子抗日"
cipherText, _:= rsa.RSAEncrypt([]byte(msg), "publicKey.pem")
fmt.Println(string(cipherText))
plainText, _:= rsa.RSADecrypt(cipherText, "privateKey.pem")
fmt.Println(string(plainText))
}