用 Golang 實(shí)現(xiàn) RSA 加密和簽名(有示例)
本文介紹 RSA 干了什么,以及我們?cè)鯓佑?Go 實(shí)現(xiàn)它。

RSA(Rivest–Shamir–Adleman)加密是使用最廣的安全數(shù)據(jù)加密算法之一。
它是一種非對(duì)稱加密算法,也叫”單向加密“。用這種方式,任何人都可以很容易地對(duì)數(shù)據(jù)進(jìn)行加密,而只有用正確的”秘鑰“才能解密。
如果你想跳過(guò)解釋直接看源碼,點(diǎn)擊這里[1]。
RSA 加密,一言以蔽之
RSA 是通過(guò)生成一個(gè)公鑰和一個(gè)私鑰進(jìn)行加/解密的。公鑰和私鑰是一起生成的,組成一對(duì)秘鑰對(duì)。

這意味著我們可以把我們的公鑰給任何想給的人。之后他們可以把想發(fā)送給我們的信息進(jìn)行加密,唯一能訪問(wèn)這些信息的方式就是用我們的私鑰進(jìn)行解密。

秘鑰的生成過(guò)程,以及信息的加密解密過(guò)程不在本文討論范圍內(nèi),但是如果你想研究詳細(xì)信息,這里有一個(gè)關(guān)于此主題的強(qiáng)大視頻[2]。
秘鑰的生成
我們要做的第一件事就是生成公鑰私鑰對(duì)。這些秘鑰是隨機(jī)生成的,在后面所有的處理中都會(huì)用到。
我們用標(biāo)準(zhǔn)庫(kù) crypto/rsa[3] 來(lái)生成秘鑰,用 crypto/rand[4] 庫(kù)來(lái)生成隨機(jī)數(shù)。
// The GenerateKey method takes in a reader that returns random bits, and
// the number of bits
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
// The public key is a part of the *rsa.PrivateKey struct
publicKey := privateKey.PublicKey
// use the public and private keys
// ...
publicKey 和 privateKey 變量分別用于加密和解密。
加密
我們用 EncryptOEAP[5] 函數(shù)來(lái)加密一串隨機(jī)的信息。我們需要為這個(gè)函數(shù)提供一些輸入:
一個(gè)哈希函數(shù),用了它之后要能保證即使輸入做了微小的改變,輸出哈希也會(huì)變化很大。SHA256 適合于此。 一個(gè)用來(lái)生成隨機(jī)位的 random reader,這樣相同的內(nèi)容重復(fù)輸入時(shí)就不會(huì)有相同的輸出 之前生成的公鑰 我們想加密的信息 可選的標(biāo)簽參數(shù)(本文中我們忽略)
encryptedBytes, err := rsa.EncryptOAEP(
sha256.New(),
rand.Reader,
&publicKey,
[]byte("super secret message"),
nil)
if err != nil {
panic(err)
}
fmt.Println("encrypted bytes: ", encryptedBytes)
這段代碼會(huì)打印加密后的字節(jié),看起來(lái)有點(diǎn)像無(wú)用的信息。
解密
如果想訪問(wèn)加密字節(jié)承載的信息,就需要對(duì)它們進(jìn)行解密。
解密它們的唯一方法就是使用與加密時(shí)的公鑰對(duì)應(yīng)的私鑰。
*rsa.PrivateKey 結(jié)構(gòu)體有一個(gè)方法 Decrypt[6],我們使用這個(gè)方法從加密數(shù)據(jù)中解出原始的信息。
解密時(shí)我們需要輸入的參數(shù)有:1. 被加密的數(shù)據(jù)(稱為密文)2. 加密數(shù)據(jù)用的哈希
// The first argument is an optional random data generator (the rand.Reader we used before)
// we can set this value as nil
// The OEAPOptions in the end signify that we encrypted the data using OEAP, and that we used
// SHA256 to hash the input.
decryptedBytes, err := privateKey.Decrypt(nil, encryptedBytes, &rsa.OAEPOptions{Hash: crypto.SHA256})
if err != nil {
panic(err)
}
// We get back the original information in the form of bytes, which we
// the cast to a string and print
fmt.Println("decrypted message: ", string(decryptedBytes))
簽名和校驗(yàn)
RSA 秘鑰也用于簽名和校驗(yàn)。簽名不同于加密,簽名可以讓你宣示真實(shí)性,而不是機(jī)密性。
也就是說(shuō),由原始信息生成一段數(shù)據(jù),稱為“簽名”,而不是偽裝原始信息的內(nèi)容(像加密[7]中做的那樣)。

msg := []byte("verifiable message")
// Before signing, we need to hash our message
// The hash is what we actually sign
msgHash := sha256.New()
_, err = msgHash.Write(msg)
if err != nil {
panic(err)
}
msgHashSum := msgHash.Sum(nil)
// In order to generate the signature, we provide a random number generator,
// our private key, the hashing algorithm that we used, and the hash sum
// of our message
signature, err := rsa.SignPSS(rand.Reader, privateKey, crypto.SHA256, msgHashSum, nil)
if err != nil {
panic(err)
}
// To verify the signature, we provide the public key, the hashing algorithm
// the hash sum of our message and the signature we generated previously
// there is an optional "options" parameter which can omit for now
err = rsa.VerifyPSS(&publicKey, crypto.SHA256, msgHashSum, signature, nil)
if err != nil {
fmt.Println("could not verify signature: ", err)
return
}
// If we don't get any error from the `VerifyPSS` method, that means our
// signature is valid
fmt.Println("signature verified")
總結(jié)
本文中我們看到了如何生成 RSA 公鑰和私鑰,以及怎樣使用它們進(jìn)行加密、解密、簽名和驗(yàn)證任意數(shù)據(jù)。
在將它們用于你的數(shù)據(jù)之前,你需要了解一些使用限制。首先,你要加密的數(shù)據(jù)必須比你的秘鑰短。例如,EncryptOAEP 文檔[8] 中說(shuō)“(要加密的)信息不能比公布的模數(shù)減去哈希長(zhǎng)度的兩倍后再減去 2 長(zhǎng)”。
使用的哈希算法要適合你的需求。SHA256(在本例中用的就是 SHA256)可以用于大部分案例,但是如果是對(duì)數(shù)據(jù)要求更高的應(yīng)用,你可能需要用 SHA512。
你可以在這里[9]找到所有示例的源碼。
via: https://www.sohamkamani.com/golang/rsa-encryption/
作者:Soham Kamani[10]譯者:lxbwolf[11]校對(duì):polaris1119[12]
本文由 GCTT[13] 原創(chuàng)編譯,Go 中文網(wǎng)[14] 榮譽(yù)推出
參考資料
這里: https://gist.github.com/sohamkamani/08377222d5e3e6bc130827f83b0c073e
[2]強(qiáng)大視頻: https://www.youtube.com/watch?v=wXB-V_Keiu8
[3]crypto/rsa: https://pkg.go.dev/crypto/rsa?tab=doc
[4]crypto/rand: https://pkg.go.dev/crypto/rand?tab=doc
[5]EncryptOEAP: https://pkg.go.dev/crypto/rsa?tab=doc#EncryptOAEP
[6]Decrypt: https://pkg.go.dev/crypto/rsa?tab=doc#PrivateKey.Decrypt
[7]加密: https://www.sohamkamani.com/golang/rsa-encryption/#encryption
[8]EncryptOAEP 文檔: https://pkg.go.dev/crypto/rsa?tab=doc#EncryptOAEP
[9]這里: https://gist.github.com/sohamkamani/08377222d5e3e6bc130827f83b0c073e
[10]Soham Kamani: https://twitter.com/sohamkamani
[11]lxbwolf: https://github.com/lxbwolf
[12]polaris1119: https://github.com/polaris1119
[13]GCTT: https://github.com/studygolang/GCTT
[14]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
