Go语言实现椭圆曲线加密解密

本文借鉴了SM2的加密解密过程,这对我起了极大的帮助作用。本文基于Go语言实现了椭圆曲线加密解密。加密、解密、签名和验证的完整代码可以参考:https://github.com/lwqt99/myInterest,同时里面也包含了各类相关算法。

  1. 生成密钥对
type Ecdsa struct {
	randKey string
	priKey *ecdsa.PrivateKey
	pubKey *ecdsa.PublicKey
}
type Cipher struct {
	XCoordinate *big.Int
	YCoordinate *big.Int
	HASH        []byte
	CipherText  []byte
}
var ECDSA Ecdsa
//用于生成自定义的参数(如果使用官方自带参数则不需要使用下面的函数)
func initPX() elliptic.Curve {
	var p *elliptic.CurveParams
	p = &elliptic.CurveParams{Name: "P-X"}
	p.P, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319", 10)
	p.N, _ = new(big.Int).SetString("39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643", 10)
	p.B, _ = new(big.Int).SetString("b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef", 16)
	p.Gx, _ = new(big.Int).SetString("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7", 16)
	p.Gy, _ = new(big.Int).SetString("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", 16)
	p.BitSize = 384

	return p
}

//也可以使用自定义的曲线 具体可修改上面的函数
func (*Ecdsa)generateKey(priFile, pubFile *os.File) error {
	lenth := len(ECDSA.randKey)
	if lenth < 224/8 + 8 {
		return errors.New("randKey长度太短,至少为36位!")
	}
	// 根据随机密匙的长度创建私匙
	var curve elliptic.Curve
	if lenth > 521/8+8 {
		curve = elliptic.P521()
	} else if lenth > 384/8+8 {
		curve = elliptic.P384()
	} else if lenth > 256/8+8 {
		curve = elliptic.P256()
	} else if lenth > 224/8+8 {
		curve = elliptic.P224()
	}
	// 生成私匙
	priKey, err := ecdsa.GenerateKey(curve, strings.NewReader(ECDSA.randKey))
	if err != nil {
		return err
	}
	// 序列化私匙
	priBytes, err := x509.MarshalECPrivateKey(priKey)
	if err != nil {
		return err
	}
	priBlock := pem.Block{
		Type:  "ECD PRIVATE KEY",
		Bytes: priBytes,
	}
	// 编码私匙,写入文件
	if err := pem.Encode(priFile, &priBlock); err != nil {
		return err
	}
	// 序列化公匙
	pubBytes, err := x509.MarshalPKIXPublicKey(&priKey.PublicKey)
	if err != nil {
		return err
	}
	pubBlock := pem.Block{
		Type:  "ECD PUBLIC KEY",
		Bytes: pubBytes,
	}
	// 编码公匙,写入文件
	if err := pem.Encode(pubFile, &pubBlock); err != nil {
		return err
	}
	return nil
}
func (*Ecdsa)initECDSA() error {
	ECDSA.randKey = "ljz abc 123456 random choose some helpful id"
	// 初始化生成私匙公匙
	priFile, _ := os.Create("./gitgit/certificate/ecdsa-prikey.pem")
	pubFile, _ := os.Create("./gitgit/certificate/ecdsa-pubkey.pem")
	if err := ECDSA.generateKey(priFile, pubFile); err != nil {
		return err
	}
	return nil
}
func (*Ecdsa)GenerateKeys() error {
	if err := ECDSA.initECDSA(); err != nil{
		return err
	}
	return nil
}
// 加载私匙公匙
func (*Ecdsa)LoadKey() error {
	// 读取密匙
	pri, _ := ioutil.ReadFile("./gitgit/certificate/ecdsa-prikey.pem")
	pub, _ := ioutil.ReadFile("./gitgit/certificate/ecdsa-pubkey.pem")
	// 解码私匙
	block, _ := pem.Decode(pri)
	var err error
	// 反序列化私匙
	ECDSA.priKey, err = x509.ParseECPrivateKey(block.Bytes)
	if err != nil {
		return err
	}
	// 解码公匙
	block, _ = pem.Decode(pub)
	// 反序列化公匙
	var t interface{}
	t, err = x509.ParsePKIXPublicKey(block.Bytes)
	if err != nil {
		return err
	}
	var ok bool
	ECDSA.pubKey, ok = t.(*ecdsa.PublicKey)
	if !ok {
		return errors.New("公钥interface转换失败")
	}
	return nil
}
  • 注意事项
    • randKey不重要,可以直接选用需要的曲线参数
    • 使用时需要先生成Key,然后每次使用前使用(指重新run代码)LoadKey()来加载密钥对
  1. 加密
/*
	用于生成随机数
 */
func randFieldElement(c elliptic.Curve, random io.Reader) (r *big.Int, err error) {
	one := new(big.Int).SetInt64(1)
	if random == nil {
		random = rand.Reader //If there is no external trusted random source,please use rand.Reader to instead of it.
	}
	params := c.Params()
	b := make([]byte, params.BitSize/8+8)
	_, err = io.ReadFull(random, b)
	if err != nil {
		return
	}
	r = new(big.Int).SetBytes(b)
	n := new(big.Int).Sub(params.N, one)
	r.Mod(r, n)
	r.Add(r, one)
	return
}

func intToBytes(x int) []byte {
	var buf = make([]byte, 4)

	binary.BigEndian.PutUint32(buf, uint32(x))
	return buf
}

// 填充
func zeroByteSlice(n int) []byte {
	return make([]byte,n)
}

/*
	加密
 */
func kdf(length, bitsize int, x ...[]byte) ([]byte, bool) {
	var c []byte

	ct := 1
	h := sm3.New()
	for i, j := 0, (length+bitsize-1)/bitsize; i < j; i++ {
		h.Reset()
		for _, xx := range x {
			h.Write(xx)
		}
		h.Write(intToBytes(ct))
		hash := h.Sum(nil)
		if i+1 == j && length%bitsize != 0 {
			c = append(c, hash[:length%bitsize]...)
		} else {
			c = append(c, hash...)
		}
		ct++
	}
	for i := 0; i < length; i++ {
		if c[i] != 0 {
			return c, true
		}
	}
	return c, false
}

func encrypt(message string, random io.Reader) ([]byte, error) {
	data := []byte(message)
	length := len(data)

	for {
		curve := ECDSA.pubKey.Curve
		r, err := randFieldElement(curve, random)
		if err != nil {
			return nil, err
		}
		x1, y1 := curve.ScalarBaseMult(r.Bytes()) //计算rG(kG便于理解)
		x2, y2 := curve.ScalarMult(ECDSA.pubKey.X, ECDSA.pubKey.Y, r.Bytes())

		x1Buf := x1.Bytes()
		y1Buf := y1.Bytes()
		x2Buf := x2.Bytes()
		y2Buf := y2.Bytes()

		//fmt.Println(len(x1Buf))
		bitsize := curve.Params().BitSize
		bitsize_8 := bitsize / 8

		if n := len(x1Buf); n < bitsize_8 {
			x1Buf = append(zeroByteSlice(bitsize_8)[:bitsize_8-n], x1Buf...)
		}
		if n := len(y1Buf); n < bitsize_8 {
			y1Buf = append(zeroByteSlice(bitsize_8)[:bitsize_8-n], y1Buf...)
		}
		if n := len(x2Buf); n < bitsize_8 {
			x2Buf = append(zeroByteSlice(bitsize_8)[:bitsize_8-n], x2Buf...)
		}
		if n := len(y2Buf); n < bitsize_8 {
			y2Buf = append(zeroByteSlice(bitsize_8)[:bitsize_8-n], y2Buf...)
		}

		// 计算加密结果
		c := []byte{}
		c = append(c, x1Buf...) // x分量
		c = append(c, y1Buf...) // y分量
		tm := []byte{}
		tm = append(tm, x2Buf...)
		tm = append(tm, data...)
		tm = append(tm, y2Buf...)

		h := sm3.Sm3Sum(tm)
		c = append(c, h...)
		ct, ok := kdf(length, bitsize_8, x2Buf, y2Buf) // 密文
		if !ok {
			continue
		}
		c = append(c, ct...)
		//for i := 0; i < len(c); i++ {
		//	fmt.Println(c[i])
		//}
		//fmt.Println(len(c))
		for i := 0; i < length; i++ {
			c[bitsize_8*3+i] ^= data[i]
		}

		return append([]byte{0x04}, c...), nil
	}
}
func CipherMarshal(bitsize int, data []byte) ([]byte, error) {
	data = data[1:]
	x := new(big.Int).SetBytes(data[:bitsize])
	y := new(big.Int).SetBytes(data[bitsize:bitsize*2])
	hash := data[bitsize*2:bitsize*3]
	cipherText := data[bitsize*3:]
	return asn1.Marshal(Cipher{x, y, hash, cipherText})
}

func (*Ecdsa) EncryptAsn1(message string, random io.Reader) ([]byte, error) {
	cipher, err := encrypt(message, random)
	if err != nil {
		return nil, err
	}
	return CipherMarshal(ECDSA.pubKey.Curve.Params().BitSize/8,cipher)
}

  1. 解密
/*
sm2密文asn.1编码格式转C1|C3|C2拼接格式
*/
func CipherUnmarshal(bitsize int, data []byte) ([]byte, error) {
	var cipher Cipher
	_, err := asn1.Unmarshal(data, &cipher)
	if err != nil {
		return nil, err
	}
	x := cipher.XCoordinate.Bytes()
	y := cipher.YCoordinate.Bytes()
	hash := cipher.HASH
	if err != nil {
		return nil, err
	}
	cipherText := cipher.CipherText
	if err != nil {
		return nil, err
	}
	if n := len(x); n < bitsize {
		x = append(zeroByteSlice(bitsize)[:bitsize-n], x...)
	}
	if n := len(y); n < bitsize {
		y = append(zeroByteSlice(bitsize)[:bitsize-n], y...)
	}
	c := []byte{}
	c = append(c, x...)          // x分量
	c = append(c, y...)          // y分
	c = append(c, hash...)       // x分量
	c = append(c, cipherText...) // y分
	return append([]byte{0x04}, c...), nil
}


func decrypt(priv *ecdsa.PrivateKey, data []byte) ([]byte, error) {
	data = data[1:]
	curve := priv.Curve
	bitsize_8 := curve.Params().BitSize / 8
	length := len(data) - bitsize * 3

	x := new(big.Int).SetBytes(data[:bitsize_8])
	y := new(big.Int).SetBytes(data[bitsize_8:bitsize_8*2])
	x2, y2 := curve.ScalarMult(x, y, priv.D.Bytes())
	x2Buf := x2.Bytes()
	y2Buf := y2.Bytes()
	if n := len(x2Buf); n < bitsize_8 {
		x2Buf = append(zeroByteSlice(bitsize_8)[:bitsize_8-n], x2Buf...)
	}
	if n := len(y2Buf); n < bitsize_8 {
		y2Buf = append(zeroByteSlice(bitsize_8)[:bitsize_8-n], y2Buf...)
	}
	c, ok := kdf(length,bitsize_8, x2Buf, y2Buf)
	if !ok {
		return nil, errors.New("Decrypt: failed to decrypt")
	}
	for i := 0; i < length; i++ {
		c[i] ^= data[i+bitsize*3]
	}
	tm := []byte{}
	tm = append(tm, x2Buf...)
	tm = append(tm, c...)
	tm = append(tm, y2Buf...)
	h := sm3.Sm3Sum(tm)
	if bytes.Compare(h, data[bitsize_8*2:bitsize_8*3]) != 0 {
		return c, errors.New("Decrypt: failed to decrypt")
	}
	return c, nil
}


func (*Ecdsa) DecryptAsn1(ciphertxt []byte) ([]byte, error) {
	bitsize_8 := ECDSA.pubKey.Curve.Params().BitSize / 8
	cipher, err := CipherUnmarshal(bitsize_8,ciphertxt)
	if err != nil {
		return nil, err
	}
	return decrypt(ECDSA.priKey, cipher)
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值