用 Golang 實現 RSA 加密和簽名

RSA(Rivest–Shamir–Adleman)加密是使用最廣的安全數據加密算法之一。

它是一種非對稱加密算法,也叫”單向加密 “。用這種方式,任何人都可以很容易地對數據進行加密,而只有用正確的” 祕鑰“才能解密。

RSA 加密,一言以蔽之

RSA 是通過生成一個公鑰和一個私鑰進行加 / 解密的。公鑰和私鑰是一起生成的,組成一對祕鑰對。

這意味着我們可以把我們的公鑰給任何想給的人。之後他們可以把想發送給我們的信息進行加密,唯一能訪問這些信息的方式就是用我們的私鑰進行解密。

祕鑰的生成過程,以及信息的加密解密過程不在本文討論範圍內,但是如果你想研究詳細信息,這裏有一個關於此主題的強大視頻 [2]。

祕鑰的生成

我們要做的第一件事就是生成公鑰私鑰對。這些祕鑰是隨機生成的,在後面所有的處理中都會用到。

我們用標準庫 crypto/rsa[3] 來生成祕鑰,用 crypto/rand[4] 庫來生成隨機數。

// 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
// ...

publicKeyprivateKey 變量分別用於加密和解密。

加密

我們用 EncryptOEAP[5] 函數來加密一串隨機的信息。我們需要爲這個函數提供一些輸入:

  1. 一個哈希函數,用了它之後要能保證即使輸入做了微小的改變,輸出哈希也會變化很大。SHA256 適合於此。

  2. 一個用來生成隨機位的 random reader,這樣相同的內容重複輸入時就不會有相同的輸出

  3. 之前生成的公鑰

  4. 我們想加密的信息

  5. 可選的標籤參數(本文中我們忽略)

encryptedBytes, err := rsa.EncryptOAEP(
 sha256.New(),
 rand.Reader,
 &publicKey,
 []byte("super secret message"),
 nil)
if err != nil {
 panic(err)
}

fmt.Println("encrypted bytes: ", encryptedBytes)

這段代碼會打印加密後的字節,看起來有點像無用的信息。

解密

如果想訪問加密字節承載的信息,就需要對它們進行解密。

解密它們的唯一方法就是使用與加密時的公鑰對應的私鑰。

*rsa.PrivateKey 結構體有一個方法 Decrypt[6],我們使用這個方法從加密數據中解出原始的信息。

解密時我們需要輸入的參數有:1. 被加密的數據(稱爲_密文_)2. 加密數據用的哈希

// 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))

簽名和校驗

RSA 祕鑰也用於簽名和校驗。簽名不同於加密,簽名可以讓你宣示真實性,而不是機密性。

也就是說,由原始信息生成一段數據,稱爲 “簽名”,而不是僞裝原始信息的內容(像加密 [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")

總結

本文中我們看到了如何生成 RSA 公鑰和私鑰,以及怎樣使用它們進行加密、解密、簽名和驗證任意數據。

在將它們用於你的數據之前,你需要了解一些使用限制。首先,你要加密的數據必須比你的祕鑰短。例如,EncryptOAEP 文檔 [8] 中說 “(要加密的)信息不能比公佈的模數減去哈希長度的兩倍後再減去 2 長”。

使用的哈希算法要適合你的需求。SHA256(在本例中用的就是 SHA256)可以用於大部分案例,但是如果是對數據要求更高的應用,你可能需要用 SHA512。

你可以在這裏 [9] 找到所有示例的源碼。

via: https://www.sohamkamani.com/golang/rsa-encryption/

作者:Soham Kamani[10] 譯者:lxbwolf[11] 校對:polaris1119[12]

本文由 GCTT[13] 原創編譯,Go 中文網 [14] 榮譽推出

參考資料

[1] 這裏: https://gist.github.com/sohamkamani/08377222d5e3e6bc130827f83b0c073e

[2] 強大視頻: 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 中文網: https://studygolang.com/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/w976zzchogZy8xkWGN--Ww