ecc算法的代码实现

4 篇文章 0 订阅
1 篇文章 0 订阅

ecc算法的代码实现


什么是ecc算法

Elliptic curve cryptography,椭圆曲线密码学,即ECC。是一种建立公开密钥加密的算法,基于椭圆曲线数学。椭圆曲线在密码学中的使用是在1985年由Neal Koblitz和Victor Miller分别独立提出的。详情链接https://www.cnblogs.com/Kalafinaian/p/7392505.html

如果你能够坚持看完上面的博客,并且能够看懂里面的内容,我十分佩服,因为我实在是没看下去。不过或多或少了解了一些基本的概念比如椭圆曲线函数并不是说真的就是一个函数来生成椭圆上的两个点这么简单,函数的几何形状也并不真的是一个椭圆。(真的就看懂了这么点东西…)所以有机会还是希望多多阅读一下大神的博客。

有关与go语言的ecc包

在go语言中crypto/elliptic包是声明椭圆曲线模型的包
注意:对于go语言自带的ECC函数来说,数字越大对应的ECC的公私钥的长度就越长,对应的加密等级就越高,当然也就越安全,那么对应的执行效率也就会相对降低。
在这里插入图片描述
crypto/ecdsa包则是go中用于椭圆曲线数字签名的包。
在这里插入图片描述

具体实现流程

一、生成公私钥
生成密钥–>将生成的私钥进行x509序列化为ASN.1标准的DER二进制编码–> 构建pem.Block数据块–>pem编码【公钥同理】

二、ecdsa私钥生成数字签名
读取本地私钥pem文件–>pem解析pem数据块–>x509解析der字符串,获得私钥–>计算hash–>ecdsa签名

三、ecdsa签名验证
读取本地公钥pem文件–>pem解析pem数据块–>x509解析der字符串,获得公钥–>计算hash–>ecdsa签名验证

代码实现

package main

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"fmt"
	"math/big"
	"os"
)

// 初始化创建ecc密钥
func generateECDSAKey() {
	// 生成ecc算法的密钥
	privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
	if err != nil {
		panic(err)
	}
	// 将私钥本地化,使用x509进行序列化
	privateDerBytes, err := x509.MarshalECPrivateKey(privateKey)
	if err != nil {
		panic(err)
	}
	// 再将它转换成pem的格式编码
	privatePemBlock := pem.Block{
		Type:  "Ecc PrivateKey", // 简介使用算法类型rsa/ecc
		Bytes: privateDerBytes,
	}
	// 在本地创建pem文件
	privateFile, err := os.Create("ECDSAPrivateKey.pem")
	if err != nil {
		panic(err)
	}
	defer privateFile.Close()
	// 进行pem编码
	err = pem.Encode(privateFile, &privatePemBlock)
	if err != nil {
		panic(err)
	}
	// 公钥同理
	publicKey := privateKey.PublicKey
	publicDerBytes, err := x509.MarshalPKIXPublicKey(&publicKey)
	if err != nil {
		panic(err)
	}
	publicPemBlock := pem.Block{
		Type:  "Ecc PublicKey",
		Bytes: publicDerBytes,
	}
	publicFile, err := os.Create("ECDSAPublicKey.pem")
	if err != nil {
		panic(err)
	}
	defer publicFile.Close()
	pem.Encode(publicFile, &publicPemBlock)
}

// 私钥签名
func privateKeySignature(data []byte, privateKeyPemFileName string) (rText, sText []byte) {
	// 读取私钥
	privateFile, err := os.Open(privateKeyPemFileName)
	if err != nil {
		panic(err)
	}
	defer privateFile.Close()
	// 读取文件源信息
	fileInfo, err := privateFile.Stat()
	if err != nil {
		panic(err)
	}
	buffer := make([]byte, fileInfo.Size())
	privateFile.Read(buffer)
	// pem解码
	block, _ := pem.Decode(buffer)
	// x509 DER解码
	privateKey, err := x509.ParseECPrivateKey(block.Bytes)
	if err != nil {
		panic(err)
	}
	// 进行签名
	r, s, err := ecdsa.Sign(rand.Reader, privateKey, getHash(data))
	// 将big的数据转换成[]byte
	rText, _ = r.MarshalText()
	sText, _ = s.MarshalText()
	return
}

// 获取哈希
func getHash(data []byte) []byte {
	hash256 := sha256.New()
	hash256.Write(data)
	return hash256.Sum(nil)
}

// 公钥验证
func publicKeyVerify(data, rText, sText []byte, publicKeyPemFileName string) bool {
	// 读取公钥
	publicFile, err := os.Open(publicKeyPemFileName)
	if err != nil {
		panic(err)
	}
	defer publicFile.Close()
	fileInfo, err := publicFile.Stat()
	if err != nil {
		panic(err)
	}

	buffer := make([]byte, fileInfo.Size())
	publicFile.Read(buffer)
	block, _ := pem.Decode(buffer)
	publicKeyType, err := x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		panic(err)
	}
	// 获取到publicKey的类型,要进行断言判断rsa/ecc
	publicKey := publicKeyType.(*ecdsa.PublicKey)
	// 对[]byte签名数据转换成big数据
	var r, s big.Int
	r.UnmarshalJSON(rText)
	s.UnmarshalJSON(sText)
	if !ecdsa.Verify(publicKey, getHash(data), &r, &s) {
		return false
	} else {
		return true
	}
}

func main() {
	data := []byte("我向某用户转账10元")
	generateECDSAKey()
	rText, sText := privateKeySignature(data, "ECDSAPrivateKey.pem")
	fmt.Println(publicKeyVerify(data, rText, sText, "ECDSAPublicKey.pem"))
}

使用以太坊的ecc加密算法实现

以太坊的crypto模块

该模块分为两个部分一个是实现sha3,一个是实现secp256k1(这也是比特币中使用的签名算法). 需要说明的是secp256k1有两种实现方式,一种是依赖libsecp256k1,需要cgo,另外一种是依赖github.com/btcsuite/btcd,这是一个使用go语言实现的比特币的客户端.

sha3模块

这个模块实际上可以认为就是一个功能计算sha3-256,用法也很简单,就是调用crypto中的Keccak256,输出是一个32字节的hash结果

hash := crypto.Keccak256Hash([]byte("hello"))
//hash值:4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45

secp256k1模块

这个模块比较复杂,如果要细度源码,需要对密码学有比较深入的理解,但是使用起来其实比较简单.
主要就是签名,验证,以及公钥与以太坊地址转换
1.签名
secp256k1的私钥地址长度是32字节256位,公钥地址长度是65字节,而以太坊的地址长度是20字节.
2.验证
验证签名是否正确,需要公钥,hash(对message进行hash的结果),以及签名. 这里面真正校验的是第三步,也就是公钥是否和我的相同,而不像普通工RSA签名验证一样.当然我们可以封装成和RSA签名验证一样形式的func VerifySignature(pubKey,msg,sig []byte) error
3.公钥与地址的转换
以太坊中并没有直接拿公钥当做账户地址,而是进行了一个简单的转换,具体来说就是hash(公钥)的后20位,这里的hash算法是sha3-256,可以用一行代码来表示

crypto.Keccak256(pubKey)[12:]

以太坊ecc的简单模式实现

package main

import (
	"bytes"
	"crypto/ecdsa"
	"crypto/rand"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/crypto/ecies"
	"strconv"
)

// 获取私钥
func getKey() (*ecdsa.PrivateKey, error) {
	privateKey, err := ecdsa.GenerateKey(crypto.S256(), rand.Reader)
	if err != nil {
		return privateKey, err
	}
	return privateKey, nil
}

// 加密
func ECCEncrypt(publicKey ecies.PublicKey, data []byte) ([]byte, error) {
	ct, err := ecies.Encrypt(rand.Reader, &publicKey, data, nil, nil)
	return ct, err
}

// 解密
func ECCDecrypt(privateKey ecies.PrivateKey, ct []byte) ([]byte, error) {
	m, err := privateKey.Decrypt(ct, nil, nil)
	return m, err
}

// 获取哈希
func getHash2(data []byte, nonce int) string {
	hashBytes := sha256.Sum256([]byte(string(data) + strconv.Itoa(nonce)))
	return hex.EncodeToString(hashBytes[:])
}

//  获取挖矿等级需求来进行判断hash是否满足
func getMineDiff(diff int) (str string) {
	for i := 0; i < diff; i++ {
		str = str + "0"
	}
	return
}

// 开始挖矿[链的挖矿难度]
func calculationHash(diff int, data []byte) string {
	strDiff := getMineDiff(diff)
	var nonce int
	for {
		if getHash2(data, nonce)[:diff] == strDiff {
			return getHash2(data, nonce)
		}
		nonce++
	}
}

// 签名
func signature(hash []byte, privateKey *ecdsa.PrivateKey) ([]byte, error) {
	signature, err := crypto.Sign(hash, privateKey)
	if err != nil {
		return signature, err
	}
	return signature, nil
}

// 验证
func validate(recoveredPub, recoveredPubBytes []byte) bool {
	if !bytes.Equal(recoveredPubBytes, recoveredPub) {
		return false
	}
	return true
}

func main() {
	privateKeyECDSA, _ := getKey()
	// 将ecdsa的私钥转换成以太坊的私钥
	privateKey := ecies.ImportECDSA(privateKeyECDSA)
	publicKey := privateKey.PublicKey
	// 公钥加密
	data := []byte("我向某用户转账10元")
	hash := calculationHash(4, data)

	fmt.Println("哈希散列为", hash)
	encryptData, err := ECCEncrypt(publicKey, []byte(hash))
	if err != nil {
		panic(err)
	}
	fmt.Println("公钥加密后为", hex.EncodeToString(encryptData))

	// 私钥解密
	decryptData, err := ECCDecrypt(*privateKey, encryptData)
	if err != nil {
		panic(err)
	}
	fmt.Println("私钥解密后为", string(decryptData))

	// 进行签名
	signData, _ := signature(crypto.Keccak256([]byte(hash)), privateKey.ExportECDSA())
	fmt.Println("签名为", hex.EncodeToString(signData))

	// 验证
	recoveredPub, _ := crypto.Ecrecover(crypto.Keccak256([]byte(hash)), signData)
	recoveredPubBytes := crypto.FromECDSAPub(&privateKeyECDSA.PublicKey)

	fmt.Println(validate(recoveredPub, recoveredPubBytes))
}

ecc密钥对与[]byte类型转换

https://blog.csdn.net/reigns_/article/details/83110066

借鉴参考:
https://blog.csdn.net/u013792921/article/details/85057646#1.ECC 密码学07–数字签名之go中的椭圆曲线数字签名
https://www.cnblogs.com/baizx/p/6936258.html 以太坊系列之三: 以太坊的crypto模块–以太坊源码学习
https://blog.csdn.net/reigns_/article/details/83069118 golang实现ecc加密解密

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值