Go 常用包:crypto 之 RSA 非對稱加解密

1. 維基百科釋義

RSA 加密算法是一種非對稱加密算法,在公開密鑰加密和電子商業中被廣泛使用。RSA 是由羅納德 · 李維斯特(Ron Rivest)、阿迪 · 薩莫爾(Adi Shamir)和倫納德 · 阿德曼(Leonard Adleman)在 1977 年一起提出的。當時他們三人都在麻省理工學院工作。RSA 就是他們三人姓氏開頭字母拼在一起組成的。

2. 生成密鑰

RSA私鑰存在PKCS1PKCS8兩種格式,通過openssl生成的私鑰格式爲PKCS1, 公鑰格式爲PKCS8

2.1 使用openssl命令

1. 生成私鑰 (PKCS1格式)
# 生成私鑰
➜ openssl genrsa -out private_ssl.pem 1024
Generating RSA private key, 1024 bit long modulus
.......++++++
.............................................++++++
e is 65537 (0x10001)
# 查看私鑰
➜ cat private_ssl.pem
-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDSgkiwisLr7yTuKnn6jANvRXRfnA9PINojpPiegMkv/mnScEvM
czP8OZjpFrsjSKFnlc6OX04O+4G3GuBMjc75wQw79auM4WMJtSh3PAWiBGu6Woto
AqZFbWRnIzk2Wjw1xSJxpOUa0ed1plU/jut1dKgDTi4q+BDqGizI2JeJdQIDAQAB
AoGAFij24/bHjDSxi4zXKGPi3KzQElyIVAkeTZBJR85A35eFpkyB/jTGbS/XA/qL
mqxDqXbgtqYbvoIFZrQilox7FCi5AzxhQDB3wD67y5OjuSWhTKypq5UOj/6y9zkC
Gbzi4zIBlOZIskfj81a+WBg7vi6FCaCg105nDJOCDs0IYiECQQDpZNBoolQGgsmR
M/x699WXhG1xYV9bTDhBKpsW/O0mRjiK/6pCbDjRWGxeB+BmFmRofYwWRj2stzkg
We1if8jNAkEA5uYFWBqHpKpFUULM7fqoyixrTXz+3h22vcKc4s/xhSCfM8yCS/ad
7S4tzpEGAenLPWYERhm6Cm/0yVavtYBjSQJBAN0MBG71P0ujVzDU4c29KGiGnfkC
VgPsHmNp7NVK23iijS7okeKzCOUNMCWmpBtMHfDw51q5T/Ri3BIN2cyuZgkCQQCV
Hz2YVxn/mRBHmRLtJ5PXbrSmSPH51crt50CXo6DiT91CAPStxsrcIZTn8fWlSq8+
KiLce0UR9JhtaBT27cIxAkEArozPsVt273/UkAru4LgBURK5ZEaHful/oK0xRDdg
fq+Ij4rs24CVPlNXfycZEDrKYMS9ScZrraf8JsTSEBx++w==
-----END RSA PRIVATE KEY-----
2. 生成公鑰 (PKCS8格式)
# 生成公鑰
➜ openssl rsa -in private_ssl.pem -pubout -out public_ssl.pem
writing RSA key
# 查看公鑰
➜ cat public_ssl.pem
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSgkiwisLr7yTuKnn6jANvRXRf
nA9PINojpPiegMkv/mnScEvMczP8OZjpFrsjSKFnlc6OX04O+4G3GuBMjc75wQw7
9auM4WMJtSh3PAWiBGu6WotoAqZFbWRnIzk2Wjw1xSJxpOUa0ed1plU/jut1dKgD
Ti4q+BDqGizI2JeJdQIDAQAB
-----END PUBLIC KEY-----
3. 私鑰 PKCS1 轉 PKCS8
➜ openssl pkcs8 -topk8 -inform pem -in private_ssl.pem -outform PEM -nocrypt
-----BEGIN PRIVATE KEY-----
MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBANKCSLCKwuvvJO4q
efqMA29FdF+cD08g2iOk+J6AyS/+adJwS8xzM/w5mOkWuyNIoWeVzo5fTg77gbca
4EyNzvnBDDv1q4zhYwm1KHc8BaIEa7pai2gCpkVtZGcjOTZaPDXFInGk5RrR53Wm
VT+O63V0qANOLir4EOoaLMjYl4l1AgMBAAECgYAWKPbj9seMNLGLjNcoY+LcrNAS
XIhUCR5NkElHzkDfl4WmTIH+NMZtL9cD+ouarEOpduC2phu+ggVmtCKWjHsUKLkD
PGFAMHfAPrvLk6O5JaFMrKmrlQ6P/rL3OQIZvOLjMgGU5kiyR+PzVr5YGDu+LoUJ
oKDXTmcMk4IOzQhiIQJBAOlk0GiiVAaCyZEz/Hr31ZeEbXFhX1tMOEEqmxb87SZG
OIr/qkJsONFYbF4H4GYWZGh9jBZGPay3OSBZ7WJ/yM0CQQDm5gVYGoekqkVRQszt
+qjKLGtNfP7eHba9wpziz/GFIJ8zzIJL9p3tLi3OkQYB6cs9ZgRGGboKb/TJVq+1
gGNJAkEA3QwEbvU/S6NXMNThzb0oaIad+QJWA+weY2ns1UrbeKKNLuiR4rMI5Q0w
JaakG0wd8PDnWrlP9GLcEg3ZzK5mCQJBAJUfPZhXGf+ZEEeZEu0nk9dutKZI8fnV
yu3nQJejoOJP3UIA9K3GytwhlOfx9aVKrz4qItx7RRH0mG1oFPbtwjECQQCujM+x
W3bvf9SQCu7guAFRErlkRod+6X+grTFEN2B+r4iPiuzbgJU+U1d/JxkQOspgxL1J
xmutp/wmxNIQHH77
-----END PRIVATE KEY-----

3. 讀取密鑰

3.1 代碼

// 讀取PKCS1格式私鑰
func ReadRSAPKCS1PrivateKey(path string) (*rsa.PrivateKey, error) {
 // 讀取文件
 context, err := ioutil.ReadFile(path)
 if err != nil {
  return nil, err
 }
 // pem解碼
 pemBlock, _ := pem.Decode(context)
 // x509解碼
 privateKey, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
 return privateKey, err
}
// 讀取公鑰(包含PKCS1和PKCS8)
func ReadRSAPublicKey(path string) (*rsa.PublicKey, error) {
 var  err error
 // 讀取文件
 readFile, err := ioutil.ReadFile(path)
 if err != nil {
  return nil, err
 }
 // 使用pem解碼
 pemBlock, _ := pem.Decode(readFile)
 var pkixPublicKey interface{}
 if pemBlock.Type == "RSA PUBLIC KEY" {
  // -----BEGIN RSA PUBLIC KEY-----
  pkixPublicKey, err = x509.ParsePKCS1PublicKey(pemBlock.Bytes)
 } else if pemBlock.Type == "PUBLIC KEY" {
  // -----BEGIN PUBLIC KEY-----
  pkixPublicKey, err = x509.ParsePKIXPublicKey(pemBlock.Bytes)
 }
 if err != nil {
  return nil,err
 }
 publicKey := pkixPublicKey.(*rsa.PublicKey)
 return publicKey, nil
}

3.2 測試

// 讀取密鑰
func TestReadKey(t *testing.T) {
 // pkcs1格式-私鑰
 privatePKCS1KeyPath := "../../tmp/private_ssl.pem"
 privatePKCS1Key, err := crypto.ReadRSAPKCS1PrivateKey(privatePKCS1KeyPath)
 if err != nil {
  t.Error(err)
 }
 fmt.Printf("PKCS1私鑰: %#v\n",privatePKCS1Key)
 // pkcs8格式-公鑰
 publicPKCS8KeyPath := "../../tmp/public_ssl.pem"
 publicPKCS8Key, err := crypto.ReadRSAPublicKey(publicPKCS8KeyPath)
 if err != nil {
  t.Error(err)
 }
 fmt.Printf("PKCS8公鑰: %#v\n",publicPKCS8Key)
}

4. 加密

4.1 代碼

// 加密(使用公鑰加密)
func RSAEncrypt(data, publicKeyPath string) (string, error) {
 // 獲取公鑰
  // ReadRSAPublicKey代碼在 【3.讀取密鑰】
 rsaPublicKey, err := ReadRSAPublicKey(publicKeyPath)
 if err != nil {
  return "", err
 }
 // 加密
 encryptPKCS1v15, err := rsa.EncryptPKCS1v15(rand.Reader, rsaPublicKey, []byte(data))
 if err != nil {
  return "",err
 }
 // 把加密結果轉成Base64
 encryptString := base64.StdEncoding.EncodeToString(encryptPKCS1v15)
 return encryptString, err
}

4.2 測試

// 加密測試
func TestRsaEncrypt(t *testing.T) {
 publicKeyPath := "../../tmp/public_ssl.pem"
 data := "123456"
 encrypt, err := crypto.RSAEncrypt(data, publicKeyPath)
 if err != nil {
  t.Error(err)
 }
 fmt.Printf("加密結果:%v \n",encrypt)
}
/** 輸出
=== RUN   TestRsaEncrypt
加密結果:SRYyBXd4p+wUeTZ5478g+hW2P3OvqhYMyPwW/j91SappgxMWC/O3vCG2aVTcAHknUkK2oEs6e28deKuOvOkjSWl/jnXFDCkXklgbgnXJtfu2FjP9jXhG2b6/Eo3okxLvLXZtkaRAgZKNbbKkeiNASUO4IidkoNrnI4aOuuuVIOY= 
--- PASS: TestRsaEncrypt (0.00s)
PASS
*/

5. 解密

5.1 代碼

// 解密(使用私鑰解密)
func RSADecrypt(base64data,privateKeyPath string) (string,error) {
 // data反解base64
 decodeString, err := base64.StdEncoding.DecodeString(base64data)
 if err != nil {
  return "", err
 }
 // 讀取密鑰
 rsaPrivateKey, err := ReadRSAPKCS1PrivateKey(privateKeyPath)
 if err != nil {
  return "", err
 }
 // 解密
 decryptPKCS1v15, err := rsa.DecryptPKCS1v15(rand.Reader, rsaPrivateKey, decodeString)
 return string(decryptPKCS1v15),err
}

5.2 測試

// 解密測試
func TestRsaDecrypt(t *testing.T) {
 privateKeyPath := "../../tmp/private_ssl.pem"
 data := "pUYa4set6XkBshfio5g2hzPx1tA67sxEvJBpJiuK3McJ9cPJAXzuRkWIy4s6cDQOhrPUaNXhr3M3WLHH19/eaqcNZz1yOFZwgGKmkWtdmygtLB/wrDant9uRfXrvzlV9iMq+cUlqsrwuCa0wcGEBNHRhIJOQSTs+SxaRTeoRCbU="
 encrypt, err := crypto.RSADecrypt(data, privateKeyPath)
 if err != nil {
  t.Error(err)
 }
 fmt.Printf("解密結果:%v \n",encrypt)
}
/** 輸出
=== RUN   TestRsaDecrypt
解密結果:123456 
--- PASS: TestRsaDecrypt (0.00s)
PASS
*/

6. 數據簽名

6.1 加簽

a. 代碼

// 對數據進行數字簽名
func GetRSASign(data, privateKeyPath string) (string, error) {
 // 讀取私鑰
 privateKey, err := ReadRSAPKCS1PrivateKey(privateKeyPath)
 if err != nil {
  return "", err
 }
 // 計算Sha1散列值
 hash := sha256.New()
 hash.Write([]byte(data))
 sum := hash.Sum(nil)
 // 從1.5版本規定,使用RSASSA-PKCS1-V1_5-SIGN 方案計算簽名
 signPKCS1v15, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, sum)
 // 結果轉成base64
 toString := base64.StdEncoding.EncodeToString(signPKCS1v15)
 return toString, err
}

b. 測試

// 數據加簽
func TestAddSign(t *testing.T) {
 privateKeyPath := "../../tmp/private_ssl.pem"
 data := "123456"
 sign, err := crypto.GetRSASign(data,privateKeyPath)
 if err != nil {
  t.Error(err)
 }
 fmt.Printf("數據簽名: %v \n",sign)
}
/** 輸出
=== RUN   TestAddSign
數據簽名: QnGqGbIqoHjJG1l+JiaOKWBdX+h00lnKCoO2rTYKIro9hoaDj7nqmu+Mxsuo+2jumicvCNBZNOpMzYryjZf0x7Q4ycLBtqtCWuFRasiInUO7Avy19LRTjdMf2xw9968vilB/xEAQ53JXIDUVvCsMxTfpHI9oRiWEGXWNkhfkjkQ= 
--- PASS: TestAddSign (0.00s)
PASS
*/

6.2 驗籤

a. 代碼

// 驗證簽名
func VerifyRsaSign(data, publicKeyPath, base64Sign string) (bool, error) {
 // 反解base64
 sign,err := base64.StdEncoding.DecodeString(base64Sign)
 if err != nil {
  return false, err
 }
 // 獲取公鑰
 publicKey, err := ReadRSAPublicKey(publicKeyPath)
 if err != nil {
  return false, err
 }
 // 計算Sha1散列值
 hash := sha256.New()
 hash.Write([]byte(data))
 bytes := hash.Sum(nil)
 err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, bytes, sign)
 return err == nil, err
}

b. 測試

// 數據簽名驗證
func TestVaSign(t *testing.T) {
 publicKeyPath := "../../tmp/public_ssl.pem"
 data := "123456"
 sign := "QnGqGbIqoHjJG1l+JiaOKWBdX+h00lnKCoO2rTYKIro9hoaDj7nqmu+Mxsuo+2jumicvCNBZNOpMzYryjZf0x7Q4ycLBtqtCWuFRasiInUO7Avy19LRTjdMf2xw9968vilB/xEAQ53JXIDUVvCsMxTfpHI9oRiWEGXWNkhfkjkQ="
 verifyRsaSign,err := crypto.VerifyRsaSign(data, publicKeyPath, sign)
 if err != nil {
  fmt.Printf("驗籤失敗: %v \n",err)
 }
 fmt.Printf("驗簽結果: %v \n",verifyRsaSign)
}
/** 輸出
=== RUN   TestVaSign
驗簽結果: true 
--- PASS: TestVaSign (0.00s)
PASS
*/
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/D0tGz59PDc3hLyyEdGVozg