RSA加密——go語言版

源起

在做rsa加密驗籤的過程中遇到了一些問題,在對整個rsa體系不夠了解的情況下花了很多的時間去嘗試,但總是各種不通、各種頭疼。

在嘗試和搜索方案的過程中,不斷的發現原來平時對rsa的瞭解是如此的少,rsa的體系是如此的龐大和龐雜。

不知道大家對rsa的瞭解程度如何,作爲一個幾年經驗的後端程序員來說,對加密的大致體系有所瞭解,知道簡單異或、md5、對稱、非對稱、用過幾種對稱加密算法。非對稱加密在做支付對接的時候也有所接觸。對rsa加密的原理有所瞭解,知道如何配置ssh免密、如何配置git的ssh、如何配置nginx的單向、雙向ssl證書。

然而,這些只是外圍知識而已!!!

當然,由於不是密碼學專業,也不是安全方向,這裏不對非對稱加密的體系和算法進行分析,我個人也沒有去學習和分析。這裏僅包含在處理問題和後續梳理過程中涉及的知識要點。

公鑰和私鑰格式關係

這個格式關係很重要。對於理解祕鑰加載的步驟,對於拿到手上的各種樣式的祕鑰的理解能有一個心理準備,有一個總體的可能性判斷。
在這裏插入圖片描述

公鑰

一般來說,我們遇到的公鑰是PEM格式,PEM格式是對DER格式公鑰的一種封裝,封裝的內容包括以下幾塊:

塊名稱 塊內容示例
-----BEGIN PUBLIC KEY-----
公鑰 base64.encode(public_key_string)
-----END PUBLIC KEY-----

以下是一個PEM格式公鑰的示例:

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA+xGZ/wcz9ugFpP07Nspo6U17l0YhFiFpxxU4pTk3Lifz9R3zsIsu
ERwta7+fWIfxOo208ett/jhskiVodSEt3QBGh4XBipyWopKwZ93HHaDVZAALi/2A
+xTBtWdEo7XGUujKDvC2/aZKukfjpOiUI8AhLAfjmlcD/UZ1QPh0mHsglRNCmpCw
mwSXA9VNmhz+PiB+Dml4WWnKW/VHo2ujTXxq7+efMU4H2fny3Se3KYOsFPFGZ1TN
QSYlFuShWrHPtiLmUdPoP6CV2mML1tk+l7DIIqXrQhLUKDACeM5roMx0kLhUWB8P
+0uj1CNlNN4JRZlC7xFfqiMbFRU9Z4N6YwIDAQAB
-----END RSA PUBLIC KEY-----

HEX是十六進制編碼格式,用的比較少,以下是一個十六進制格式的公鑰示例:

00 00 00 07 73 73 68 2d 72 73 61 00 00 00 01 25 00 00 01 00 7f 9c 09
8e 8d 39 9e cc d5 03 29 8b c4 78 84 5f d9 89 f0 33 df ee 50 6d 5d d0 
16 2c 73 cf ed 46 dc 7e 44 68 bb 37 69 54 6e 9e f6 f0 c5 c6 c1 d9 cb
f6 87 78 70 8b 73 93 2f f3 55 d2 d9 13 67 32 70 e6 b5 f3 10 4a f5 c3 
96 99 c2 92 d0 0f 05 60 1c 44 41 62 7f ab d6 15 52 06 5b 14 a7 d8 19 
a1 90 c6 c1 11 f8 0d 30 fd f5 fc 00 bb a4 ef c9 2d 3f 7d 4a eb d2 dc 
42 0c 48 b2 5e eb 37 3c 6c a0 e4 0a 27 f0 88 c4 e1 8c 33 17 33 61 38 
84 a0 bb d0 85 aa 45 40 cb 37 14 bf 7a 76 27 4a af f4 1b ad f0 75 59 
3e ac df cd fc 48 46 97 7e 06 6f 2d e7 f5 60 1d b1 99 f8 5b 4f d3 97 
14 4d c5 5e f8 76 50 f0 5f 37 e7 df 13 b8 a2 6b 24 1f ff 65 d1 fb c8 
f8 37 86 d6 df 40 e2 3e d3 90 2c 65 2b 1f 5c b9 5f fa e9 35 93 65 59 
6d be 8c 62 31 a9 9b 60 5a 0e e5 4f 2d e6 5f 2e 71 f3 7e 92 8f fe 8b

ssh生成的公鑰,有自己的格式,由3部分組成“ssh-rsa”字符+公鑰+備註。以下是一個ssh公鑰的示例:

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3O26p3AlNy5JalOon2E2q62fygTBQZkVv4LJP1v7UxMH8pMNbsP1YG2keMotLtakHiqfHDd/s6eUDQYxU2nVdwruwPmP65mfhD0xTXsFFOdn+B80EVQdkm12h9Mcd9pVzMTplvB9v2j/8RX8UH45KsCgB5GcZJwdexJWys64Q+5eP386xZ7ow3XkKDS+XCEQTZaz+vN97aOQontwp+Dj0lB0VKhpnCWI5cadFRCNVitWaLLK6STRrYKV9CTUWOIuIV+2EC85UGIGmD3M+z6S3RZepoOZPDCRgaXdiNNsAAWa2ULZ9Wep9Qtly0NEsQ9EHrmeHadnDaWQy9w/KLsYj [email protected]

私鑰

私鑰常遇到的是pkcs1和pkcs8兩種標準,有時候還會遇到加密後的私鑰,需要用密碼加載或者去掉祕鑰後再使用。後續會說到格式轉換和去掉私鑰密碼的方法。

go語言生成公私鑰

由於不同語言對rsa公私鑰支持程度不同,所以很可能遇到在java項目用的很好的祕鑰,在go語言沒有辦法用。但是又不確定是祕鑰的問題,還是代碼寫法的問題。這時候如果能夠生成一套所使用語言支持的祕鑰對,對解決問題和建立信心都有很大幫助。

func GenRsaKey(bits int) error {
	// 生成私鑰文件
	privateKey, err := rsa.GenerateKey(rand.Reader, bits)
	if err != nil {
		return err
	}
	derStream := x509.MarshalPKCS1PrivateKey(privateKey)
	block := &pem.Block{
		Type:  "RSA PRIVATE KEY",
		Bytes: derStream,
	}
	file, err := os.Create("private.pem")
	if err != nil {
		return err
	}
	err = pem.Encode(file, block)
	if err != nil {
		return err
	}
	// 生成公鑰文件
	publicKey := &privateKey.PublicKey
	derPkix, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {
		return err
	}
	block = &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: derPkix,
	}
	file, err = os.Create("public.pem")
	if err != nil {
		return err
	}
	err = pem.Encode(file, block)
	if err != nil {
		return err
	}
	return nil
}

go加載私鑰

pkcs1

package main

import ("fmt"
   "crypto/x509"
   "encoding/pem"
		"crypto/rsa"
	   )

func main () {
	privateKey := `your key`
	blockPri, _ := pem.Decode([]byte(privateKey))
   if blockPri == nil {
	   fmt.Println("err1")
      return
   }       
	priKey, err := x509.ParsePKCS1PrivateKey([]byte(blockPri.Bytes))
	if err != nil {
			 fmt.Println(err)
			 fmt.Println("err2")
            return 
         }
	
	fmt.Println("ok")
	fmt.Println(priKey)
	
}

pkcs8

package main

import ("fmt"
   "crypto/x509"
   "encoding/pem"
		"crypto/rsa"
	   )

func main () {
	privateKey := `your key`
	blockPri, _ := pem.Decode([]byte(privateKey))
   if blockPri == nil {
	   fmt.Println("err1")
      return
   }
	
	prkI, err := x509.ParsePKCS8PrivateKey([]byte(blockPri.Bytes))
         if err != nil {
			 fmt.Println(err)
			 fmt.Println("err2")
            return 
         }
	priKey := prkI.(*rsa.PrivateKey)

	
	fmt.Println("ok")
	fmt.Println(priKey)
	
}

go加載公鑰

	blockPub, _ := pem.Decode([]byte(publicKey))
   if blockPub == nil {
      fmt.Println("err1")
      return
   }
   pubKey, err := x509.ParsePKIXPublicKey(publicKey)
   if err != nil {
      fmt.Println(err)
			 fmt.Println("err2")
            return 
   }

	fmt.Println("ok")
	fmt.Println(pubKey)

go進行rsa加密和驗籤

以下是hash摘要方式的rsa加密驗籤,也可以使用md5摘要方式的rsa加密:

//your_private_key和your_public_key都是上述《go加載rsa公、私鑰》部分創建的結構體類型
func Sign(src []byte, hash crypto.Hash) ([]byte, error) {
   h := hash.New()
   h.Write(src)
   hashed := h.Sum(nil)
   return rsa.SignPKCS1v15(rand.Reader, your_private_key, hash, hashed)
}

func Verify(src []byte, sign []byte, hash crypto.Hash) error {
   h := hash.New()
   h.Write(src)
   hashed := h.Sum(nil)
   return rsa.VerifyPKCS1v15(your_public_key, hash, hashoed, sign)
}

這裏說一下爲什麼要先摘要,再加密和驗籤,這裏主要考慮的是性能,因爲用rsa加密方式對性能的損耗比較大,如果待加密字串長的話,加密過程對性能的影響更大。所以,一般會先對字符串進行hash或者md5摘要。

openssl生成的公私鑰

//生成私鑰,默認生成的是pkcs1格式私鑰
genrsa -out rsa_private_key.pem 1024
//轉成pkcs8格式
pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM –nocrypt
//生成公鑰
rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

去掉私鑰的密碼

//執行會後,會要求你輸入私鑰使用的密碼
openssl rsa -in my_private_key.pem -out new_private_key.pem

openssl公鑰和openssh公鑰互轉

兩者的私鑰是同樣的格式,但是兩者的公鑰是不同的格式,所以轉換的基礎就是私鑰。通過私鑰生成另一種格式的公鑰。

比如,如果已經有了openssl生成的私鑰,可以用如下命令生成openssh的公鑰:

ssh-keygen  -y -f openssh_private.pem > public_key.pub

參考

本文參考了下列文章,有的代碼片段直接取自文章:

  1. Golang加密系列之RSA
  2. GO加密解密RSA番外篇:生成RSA密鑰
  3. GO加密解密之RSA
  4. PKCS標準
  5. openssl數字證書常見格式與協議介紹
  6. Generating an SSH Key Pair
  7. Generate Private key with OpenSSL and Public key ssh-keygen for SSH
  8. Converting keys between openssl and openssh
  9. How are the files in ~/.ssh related to the theory?
  10. OpenSSH RSA Public Key format
  11. openssl 生成公私鑰對
  12. 去掉ssh key的密碼
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章