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

目錄git

Ecc概述github

歷史golang

密鑰對生成算法

加密算法安全

解密算法ide

小結函數

Ecc的Go實現性能

crypto/ecdsa 包測試

crypto/elliptic 包編碼

crypto/rand 包

crypto/x509包

代碼

生成密鑰對

加密

解密

測試代碼

截圖

參考


​​​​​​​

Ecc概述

ECC的主要優點是在某些狀況下它比其餘的算法(好比RSA加密算法)使用更小的密鑰並提供至關的或更高等級的安全。ECC的另外一個優點是能夠定義羣之間的雙線性映射,基於Weil對或是Tate對;雙線性映射已經在密碼學中發現了大量的應用,例如基於身份的加密。
不過一個缺點是加密和解密操做的實現比其餘機制花費的時間長。

比特幣也是用的這個。

歷史

  • 橢圓曲線在代數學和幾何學上已普遍研究了150多年之久,有豐富而深厚的理論積累。
  • 1985年,Koblitz和Miller提出橢圓曲線密碼體制(EllipticCurve Cryptosystem,簡稱ECC)橢圓曲線並非橢圓,之因此稱爲橢圓曲線是由於它們是用三次方程來表示的,它的通常形式:
  • y^2+axy+by=x^3+cx^2+dx+e,其中a,b,c,d和e是知足某些條件的實數。
  • 大多數的橢圓曲線密碼系統是在模p或F2下運算。
  • 橢圓曲線密碼已經逐漸被採用,已成爲公鑰密碼的一個重要發展方向。

密鑰對生成

  •  選擇一個橢圓曲線E:y^2=x^3+ax+b(modp)),構造一個橢圓羣Ep(a,b)。
  •  在Ep(a,b)中挑選生成元點G=(x1,y1),G應使得知足nG=O最小的n是一個很是大的素數。
  •  選擇一個小於n的整數n_A做爲其私鑰,而後產生其公鑰PA=n_AG。

注:公開的信息(E,G,n,PA)

IEpI表示橢圓羣Ep(a,b)的元素個數,n是IEpI的素因子。知足:

p+1-2\sqrt{p}\leq |E_p|\leq p+1+2\sqrt{p}

加密算法

  • 將m編碼成一個數m<p,在橢圓羣Ep(a,b)中隨機選擇一點P_t=(x_t,y_t)
  • 在區間[1,n-1]內選取一個隨機數k,計算點(x_1,y_1)=kG
  • 依據接受方的公鑰PB計算點(2,y)=kPB;
  • 計算密文C=mx_t+y_t
  • 傳送加密數據{kG,P_t+kP_B,C}給接受方。

解密算法

  • 接受方接受加密數據{kG,P_t+kP_B,C}。
  • 接受方使用本身的私鑰n_B做以下計算:P_t+kP_B-nB(kG)=P_t+k(n_BG)-n_B(kG)=P_t
  • 計算m=(C-y_t)/x_t,得明文m

小結

  • 計算量小,處理速度快
  • 存儲空間佔用小
  • 帶寬要求低
  • 應用前景很是好,特別在移動通訊、無線設備上的應用
  • 安全性能更高(160位等同RSA的1024位)​​​​​​​

在保持同等安全的條件下所需的密鑰長度(單位爲比特)

RSA 1024 2048 3072 7680 15360
ECC 160 224 256 384 512
Ecc的Go實現

crypto/ecdsa

func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)

公鑰/私鑰。由於

type PrivateKey struct {
     PublicKey     
     D   *big.Int 
}

公鑰在私鑰的結構體裏面

crypto/elliptic 包

func P256() Curve

返回一個實現了P-256的曲線。

crypto/rand

func Read(b []byte) (n int, err error)

本函數是一個使用io.ReadFull調用Reader.Read的輔助性函數。當且僅當err == nil時,返回值n == len(b)。

crypto/x509包

func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error)

MarshalECPrivateKey將ecdsa私鑰序列化爲ASN.1 DER編碼。 

代碼

生成密鑰對

  • 使用ecdsa.GenerateKey生成私鑰
  • 使用x509.MarshalECPrivateKey對生成的私鑰序列化
  • 使用pem.Block轉爲塊,使用pem.Encode編碼
  • 保存私鑰到文件
  • 公鑰從私鑰結構體中取出,其餘相似,x509序列化使用MarshalECPrivateKey函數便可
// 生成ECC私鑰對
// keySize 密鑰大小, 224 256 384 521
// dirPath 密鑰文件生成後保存的目錄
// 返回 錯誤
func GenerateECCKey(keySize int,dirPath string) error {
	// generate private key
	var priKey *ecdsa.PrivateKey
	var err error
	switch keySize{
	case 224:priKey,err = ecdsa.GenerateKey(elliptic.P224(),rand.Reader)
	case 256:priKey,err = ecdsa.GenerateKey(elliptic.P256(),rand.Reader)
	case 384:priKey,err = ecdsa.GenerateKey(elliptic.P256(),rand.Reader)
	case 521:priKey,err = ecdsa.GenerateKey(elliptic.P521(),rand.Reader)
	default:priKey,err = nil,nil
	}
	if priKey == nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,errors.EcckeyError)
	}
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	// x509
	derText,err := x509.MarshalECPrivateKey(priKey)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return util.Error(file,line+1,err.Error())
	}
	// pem block
	block := &pem.Block{
		Type:"ecdsa private key",
		Bytes:derText,
	}
	file,err := os.Create(dirPath+"eccPrivate.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())
	}
	file.Close()
	// public key
	pubKey := priKey.PublicKey
	derText, err = x509.MarshalPKIXPublicKey(&pubKey)
	block = &pem.Block{
		Type:"ecdsa public key",
		Bytes:derText,
	}
	file, err = os.Create(dirPath+"eccPublic.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())
	}
	file.Close()
	return nil
}

加密

go包沒有ecc的加密,這裏採用的github上的一個項目的ecies包

  • 獲取block塊
  • 使用x509.ParsePKIXPublicKey解析爲公鑰
  • 斷言後轉爲ecies包的Public類型(轉換函數附在後面)
  • 使用ecies.Encrypt加密
// Ecc 加密
// plainText 明文
// filePath 公鑰文件路徑
// 返回 密文 錯誤
func EccEncrypt(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.(*ecdsa.PublicKey)
	if flag == false{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,errors.RsatransError)
	}
	cipherText,err := ecies.Encrypt(rand.Reader,util.PubEcdsaToEcies(publicKey),plainText,nil,nil)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err.Error())
	}
	return cipherText,err
}

解密

  • 獲取block塊
  • 使用x509.ParseECPrivateKey解析爲私鑰
  • 轉爲ecies的私鑰格式(轉換函數附在後面)
  • 使用ecies.Decrypt解密
// ECC 解密
// cipherText 密文
// filePath 私鑰文件路徑
// 返回 明文 錯誤
func EccDecrypt(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.ParseECPrivateKey(block.Bytes)
	priKey := util.PriEcdsaToEcies(privateKey)
	plainText,err = priKey.Decrypt(cipherText,nil,nil)
	if err != nil{
		_, file, line, _ := runtime.Caller(0)
		return nil,util.Error(file,line+1,err.Error())
	}
	return plainText,nil
}

附:ecdsa包的公鑰私鑰轉爲ecies對應的密鑰的轉換代碼,所有代碼看後面gitee連接 

// ecdsa public key to ecies public key
func PubEcdsaToEcies(pub *ecdsa.PublicKey) *ecies.PublicKey {
	return &ecies.PublicKey{
		X:      pub.X,
		Y:      pub.Y,
		Curve:  pub.Curve,
		Params: ecies.ParamsFromCurve(pub.Curve),
	}
}
// ecdsa private key to ecies private key
func PriEcdsaToEcies(prv *ecdsa.PrivateKey) *ecies.PrivateKey {
	pub := PubEcdsaToEcies(&prv.PublicKey)
	return &ecies.PrivateKey{*pub, prv.D}
}

測試代碼

plainText := []byte("hi, I'm lady_killer9")
	cipherText,err := EccEncrypt(plainText,"./eccPublic.pem")
	if err!=nil{
		fmt.Println(err)
	}
	fmt.Printf("加密後:%s\n",cipherText)
	plainText,err = EccDecrypt(cipherText,"./eccPrivate.pem")
	if err!=nil{
		fmt.Println(err)
	}
	fmt.Printf("解密後:%s\n",plainText)
截圖

所有源碼代碼放在:https://gitee.com/frankyu365/gocrypto

參考

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

Go標準庫-crypto/ecdsa

Go標準庫-crypto/elliptic

Go標準庫-crypto/rand

Go標準庫-crypto/hex

Go標準庫-crypto/sha256

Github-以太坊​​​​​​​

相關文章
相關標籤/搜索