Go-RSA加密解密詳解與代碼

目錄git

對稱密碼的缺點算法

RSA概述安全

RSA密鑰對生成算法網絡

RSA加解密過程dom

RSA小結ide

RSA的Go實現函數

encoding/pem包測試

crypto/x509包編碼

crypto/rsa包加密

生成RSA密鑰對

加解密

截圖

參考


對稱密碼的缺點

保密通訊進入計算機網絡時代,傳統密碼體制逐漸暴露其固有的弱點,體如今密鑰分發。經過前面的對稱密碼文章,能夠看到須要雙方知道密鑰。問題在於我要給你發送祕密信息,沒有安全的通道,發送信息前須要找到安全的通道告訴你密鑰,而後再在不安全通道進行通訊。問題是我既然找到了安全的通道,直接發送消息不就好了嗎? 固然,古代大部分是在線下約定好的,相似《懸崖之上》的密碼本。

對稱密碼解決了密鑰分發問題,能夠在不安全通道發送密鑰。固然,對稱密碼還有數字簽名問題,後面數字簽名相關的文章再說。

RSA概述

W.Diffie和Hellman發表了著名的文章《密碼學的新方向》首次提出了公鑰密碼算法的思想。

公鑰密碼體制爲密碼學的發展提供了新的理論和技術思想,一方面公鑰密碼算法是創建在數學函數基礎上的,而不是創建在字符或位方式的操做上的;另外一方面公鑰密碼算法是以非對稱的形式使用加密密鑰和解密密鑰,這兩個密鑰的使用對密鑰管理、認證等都有着深入的實際意義。能夠說,公鑰密碼體制的出如今密碼學發展史上是一次質的飛躍。

1978年,Rivest,Shamir和Adleman提出的RSA算法體現了公鑰算法的思想,被認爲是第一個安全的、實用的公鑰密碼算法。

RSA的理論基礎是數論的歐拉定理,它的安全性依賴於大整數的素因子分解的困難性。

RSA密鑰對生成算法
  1. 選取兩個大素數p和q,兩個數長度接近且相差較大。
  2. 計算n=p*q,9(n)=(p-1)(q-1)。
  3. 隨機選取整數e,知足gcd(ee(n))=1。
  4. 計算d,知足d*e=1(mode(n))。

注:p和q保密。e和n爲公鑰,d爲私鑰。

RSA加解密過程

加密算法:c=E(m)=m^e(mod n)

解密算法:m=D(c)=c^d(mod n)

RSA小結
  • 第一個實用的公開密鑰算法。
  • 目前使用最多的一種公鑰密碼算法。
  • RSA的理論基礎是數論的歐拉定理
  • RSA的安全性依賴於大數的素因子分解的困難性。
  • 密碼分析者既不能證實也不可否定RSA的安全性。
  • 既能用於加密也能用於數字簽名
  • 目前密鑰長度1024位是安全的。
RSA的Go實現

encoding/pem包

func Decode(data []byte) (p *Block, rest []byte)

Decode函數會從輸入裏查找到下一個PEM格式的塊(證書、私鑰等)。它返回解碼獲得的Block和剩餘未解碼的數據。若是未發現PEM數據,返回(nil, data)。 

crypto/x509包

func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte

MarshalPKCS1PrivateKey將rsa私鑰序列化爲ASN.1 PKCS#1 DER編碼。 

func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)

MarshalPKIXPublicKey將公鑰序列化爲PKIX格式DER編碼。

func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)

ParsePKIXPublicKey解析一個DER編碼的公鑰。這些公鑰通常在以"BEGIN PUBLIC KEY"出現的PEM塊中。

func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)

ParsePKCS1PrivateKey解析ASN.1 PKCS#1 DER編碼的rsa私鑰。

crypto/rsa包

func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) (out []byte, err error)

EncryptPKCS1v15使用PKCS#1 v1.5規定的填充方案和RSA算法加密msg。信息不能超過((公共模數的長度)-11)字節。注意:使用本函數加密明文(而不是會話密鑰)是危險的,請儘可能在新協議中使用RSA OAEP。

func DecryptPKCS1v15(rand io.Reader, priv *PrivateKey, ciphertext []byte) (out []byte, err error)

DecryptPKCS1v15使用PKCS#1 v1.5規定的填充方案和RSA算法解密密文。若是random不是nil,函數會注意規避時間側信道***。

生成RSA密鑰對

  1. 使用rsa.GenerateKey生成私鑰
  2. 使用x509.MarshalPKCS1PrivateKey序列化私鑰爲derText
  3. 使用pem.Block轉爲Block
  4. 使用pem.Encode寫入文件
  5. 從私鑰中獲取公鑰
  6. 使用x509.MarshalPKIXPublicKey序列化公鑰爲derStream
  7. 使用pem.Block轉爲Block
  8. 使用pem.Encode寫入文件
func GenerateRsaKey(keySize int, dirPath string) error {
	privateKey,err := rsa.GenerateKey(rand.Reader,keySize)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	// x509
	derText :=x509.MarshalPKCS1PrivateKey(privateKey)
	// pem Block
	block := &pem.Block{
		Type:"rsa private key",
		Bytes:derText,
	}
	// just joint, caller must let dirPath right
	file,err := os.Create(dirPath+"private.pem")
	defer file.Close()
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	err = pem.Encode(file,block)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	// get PublicKey from privateKey
	publicKey := privateKey.PublicKey
	derStream,err := x509.MarshalPKIXPublicKey(&publicKey)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	block = &pem.Block{
		Type:"rsa public key",
		Bytes:derStream,
	}
	file,err = os.Create(dirPath+"public.pem")
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	err = pem.Encode(file, block)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	return nil
}

加解密

加密

  1. 獲取公鑰
  2. 使用x509.ParsePKIXPublicKey解析公鑰
  3. 使用rsa.EncryptPKCS1v15加密
func RsaEncrypt(plainText []byte,filePath string) ([]byte, error) {
	// get pem.Block
	block,err := util.GetKey(filePath)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err.Error())
	}
	// X509
	publicInterface,err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err.Error())
	}
	publicKey,flag := publicInterface.(*rsa.PublicKey)
	if flag == false{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,errors.RsatransError)
	}
	// encrypt
	cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err.Error())
	}
	return cipherText,nil
}

解密

  1. 獲取私鑰
  2. 使用x509.ParsePKCS1PrivateKey解析私鑰
  3. 使用rsa.DecryptPKCS1v15解密
func RsaDecrypt(cipherText []byte,filePath string) (plainText []byte,err error) {
	// get pem.Block
	block,err := util.GetKey(filePath)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err.Error())
	}
	// get privateKey
	privateKey, _ := x509.ParsePKCS1PrivateKey(block.Bytes)
	defer func() {
		if err2 := recover();err2 != nil{
			_, file, line, _ := runtime.Caller(0)
			err = util.Error(file,line,errors.RsaNilError)
		}
	}()
	// get plainText use privateKey
	plainText, err3 := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText)
	if err3 != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err3.Error())
	}
	return plainText,err
}

測試代碼

func TestRsa(t *testing.T) {
	// 生成密鑰對
	err := GenerateRsaKey(1024, "./")
	if err!=nil{
		fmt.Println(err)
	}
	// 測試加密
	plainText := []byte("hi, I'm lady_killer9")
	cipherText,err := RsaEncrypt(plainText,"./public.pem")
	if err!=nil{
		fmt.Println(err)
	}
	fmt.Printf("加密後爲:%s\n",cipherText)
	// 測試解密
	plainText,err = RsaDecrypt(cipherText,"./private.pem")
	if err!=nil{
		fmt.Println(err)
	}
	fmt.Printf("解密後爲:%s\n",plainText)
}

截圖

源代碼地址:https://gitee.com/frankyu365/gocrypto

目前是v1.0.5-alpha版本,測試中,碰見了個空指針panic,不太肯定直接recover的思路是否正確。

參考

《現代密碼學教程 谷利澤,楊義先等》

Go標準庫-crypto/rsa

Go標準庫-crypto/x509

Go標準庫-encoding/pem

相關文章
相關標籤/搜索