Ecc概述
ECC的主要优势是在某些情况下它比其他的算法(比如RSA加密算法)使用更小的密钥并提供相当的或更高等级的安全。ECC的另一个优势是可以定义群之间的双线性映射,基于Weil对或是Tate对;双线性映射已经在密码学中发现了大量的应用,例如基于身份的加密。
比特币也是用的这个。
历史
椭圆曲线在代数学和几何学上已广泛研究了150多年之久,有丰富而深厚的理论积累。
1985年,Koblitz和Miller提出椭圆曲线密码体制(EllipticCurve Cryptosystem,简称ECC)椭圆曲线并不是椭圆,之所以称为椭圆曲线是因为它们是用三次方程来表示的,它的一般形式:
,其中a,b,c,d和e是满足某些条件的实数。
大多数的椭圆曲线密码系统是在模p或F2下运算。
椭圆曲线密码已经逐渐被采用,已成为公钥密码的一个重要发展方向。
密钥对生成
选择一个椭圆曲线E:,构造一个椭圆群Ep(a,b)。
在Ep(a,b)中挑选生成元点G=(x1,y1),G应使得满足nG=O最小的n是一个非常大的素数。
选择一个小于n的整数作为其私钥,然后产生其公钥PA=G。
注:公开的信息(E,G,n,PA)
IEpI表示椭圆群Ep(a,b)的元素个数,n是IEpI的素因子。满足:
加密算法
- 将m编码成一个数m<p,在椭圆群Ep(a,b)中随机选择一点;
- 在区间[1,n-1]内选取一个随机数k,计算点;
- 依据接受方的公钥PB计算点(2,y)=kPB;
- 计算密文C=;
- 传送加密数据{kG,,C}给接受方。
解密算法
- 接受方接受加密数据{kG,,C}。
- 接受方使用自己的私钥作如下计算:
- 计算m=,得明文m。
小结
- 计算量小,处理速度快
- 存储空间占用小
- 带宽要求低
- 应用前景非常好,特别在移动通信、无线设备上的应用
- 安全性能更高(160位等同RSA的1024位)
在保持同等安全的条件下所需的密钥长度(单位为比特)
RSA | 1024 | 2048 | 3072 | 7680 | 15360 |
ECC | 160 | 224 | 256 | 384 | 512 |
Ecc的Go实现
crypto/ecdsa
包
公钥/私钥。因为
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编码。
x509等其他包在https://blog.csdn.net/lady_killer9/article/details/118026802 中有提到。
代码
生成密钥对
使用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
补:ECC和RSA对比
数论基础
RSA:欧拉定理
ECC:离散对数
安全性基础
RSA:整数分解问题的困难性
ECC:椭圆曲线,离散对数问题的难解性
当前安全密钥长度
RSA:1024位
ECC:160位
消耗
RSA:CPU、内存消耗大
ECC:CPU、内存消耗小
安全等级与密钥长度
RSA:增长较快
ECC:成线性
参考
《现代密码学教程 谷利泽,杨义先等》
Go标准库-crypto/ecdsa
Go标准库-crypto/elliptic
Go标准库-crypto/rand
Go标准库-crypto/hex
Go标准库-crypto/sha256
Github-以太坊
更多Go相关内容:Go-Golang学习总结笔记
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。