ECC 椭圆曲线加解密算法

随着计算机性能的提高,部分算法已经不再安全,但是道高一尺魔高一丈,加密算法也在不断的进步和演化,通常的方法是增加密钥长度,越长越安全,确实也管用,但是性能同样也有损失,本篇要介绍的 ECC 椭圆曲线加密算法就是一种密钥长度增加不多,但是密码强度增加很多的算法,比 RSA 强多了,像比特币,以太坊中使用的就是 ECC,咱们每个人的身份证中使用的也是 ECC。

一、为什么叫椭圆曲线

首先回忆一下直线方程式 y=ax+b ,在坐标系中表示一条直线,是一次方程,圆锥曲线可以用二次方程表示。椭圆曲线是用三次方程表示,如下:
《ECC 椭圆曲线加解密算法》
其中,a 和 b 的取值不同,椭圆曲线的形状会有所改变,经典的形状如下图所示:
《ECC 椭圆曲线加解密算法》

这时有读者会有疑问了,“上图中不是一个椭圆的形状啊,为什么叫椭圆曲线啊?”,原因是椭圆曲线的 3 次方程式和椭圆周长公式中的一部分很像,下面是椭圆周长公式:
《ECC 椭圆曲线加解密算法》

可以看出积分公式中分母的平方恰恰就是椭圆曲线的公式。

椭圆曲线有以下两个特点:

  • 画一条直线跟椭圆曲线相交,它们最多有三个交点;
  • 关于 X 轴对称。

二、比特币使用的椭圆曲线

比特币中使用的椭圆曲线是 secp256k1,上文图中的椭圆曲线是连续的,但在像比特币这些实际应用中,都是有限域上定义的离散曲线。

椭圆曲线三次方程式中参数 a, b,基点 G 的不同,椭圆曲线的加密性能也不一样。一些常用的使用特定参数的椭圆曲线都有特定的标识,现在世界上比较主流的有:

  • 美国国家标准与技术研究院(NIST)和美国国家安全局(NSA)的 secp256r1、secp521r1 等椭圆曲线
  • 中国国家密码局认定的 SM2 国产密码算法。
  • 比特币和以太坊使用的 secp256k1。

下面来看一下 secp256k1 的所有参数和方程式: y^2 = x^3 + 7 ( a = 0,b = 7)

选择的基点是:
《ECC 椭圆曲线加解密算法》

可以看出方程式和参数并不复杂,反而简单,那么有读者要问了“这么简单的方程式和参数,那这个加密算法还安全吗?”,这个问题非常好,下面会讲到它的安全性其实并不是由方程式和参数决定的,而是由椭圆曲线上的运算决定的,请继续往下看。

三、椭圆曲线运算法则

在椭圆曲线上会定义两种运算,加法和乘法,但是其实它们和通常意义上的加法和乘法不一样的,只是为了方便理解所以这么叫,怎么不一样呢,下面看看具体的定义。

1. 椭圆曲线加法

根据上面介绍的椭圆曲线的特性“画一条直线跟椭圆曲线相交,它们最多有三个交点”,可以进行以下定义:

  • 假设椭圆曲线上有 P、Q 两个点,经过这两个点做一条直线和椭圆曲线相交于第三点 R,然后做关于 x 轴的对称点 -R,-R 即是 R 的逆元,根据阿贝尔群的定义,-R 也一定在椭圆曲线上。
  • 定义 P+Q = -R,也就是说椭圆曲线上任意两点的和也在椭圆曲线上,同样可以引申出椭圆曲线上任意三点的和为 0 即 P+Q+R = 0。如图:
  • 《ECC 椭圆曲线加解密算法》
  • 假如 P=Q,则作椭圆曲线在 P 点的切线,与曲线相交于 R,则 R = P+P = 2P
    《ECC 椭圆曲线加解密算法》

2. 椭圆曲线乘法

根据上面椭圆曲线的加法可以得出下列等式:

  • P+P = 2P(过点 P 切线作一条直线)
  • P+2P = 3P(过点 P 和 2P 作一条直线)
  • P+3P = 4P(过点 P 和 3P 作一条直线)

假设 P 是椭圆曲线上的一个点,正整数 K 乘以 P 可以总结成公式为:(k-1) * P + P = k * P

如果把 k 看作是两个数相乘即 k = m * n,则可以得出满足以下性质(在椭圆曲线密钥交换中会用到):(m * n) * P = m * (n * P) = (n * m)p = n * (m*P)

四、椭圆曲线的难题

非对称加密之所以难破解,根本原理就是基于一个数学上的难题,像 RSA 加密就是基于大质数因子分解困难的特性来支撑的,椭圆曲线的难题则是:椭圆曲线上的离散对数问题。

看过 RSA 加密算法原理那篇文章的读者可能会记得它是基于取模运算,椭圆曲线也是一样的,满足下面公式的曲线,其中 p 是质数,x、y、a、b 都是小于 p 的非负整数:

y^2 = x^3 + ax + b (mod p) { (4a^3 + 27b^2!)=0 }

来看一下 y^2 = x^3 – x 这个公式取模后的的图像(p=71):
《ECC 椭圆曲线加解密算法》

可以看出,虽然很散乱,但是仔细看这些点都是关于一条直线对称的,这条直线就是 y=71/2 这条水平直线,并且原来椭圆曲线上定义的加法和乘法都可用。

假如选择一个点 P(4,8) 为基点,按照椭圆曲线的加法去运算 2P、3P… 这样的话,最后得到一个 k 次加法后的结果 kP(44,44),请问 k 是多少?

这时看一下上面的散点图,找到 (4,8) 和(44,44)这两个点,很难找出来通过几次椭圆曲线加法转变过去的,更何况这个是在公式中取模的那个质数等于 71 的情况下,如果把这个质数取得很大,难度就更大了,比特币中使用的 Secp256k1 这条曲线中取模的质数 p 等于:

p = 2^256 – 2^32 – 2^9 – 2^8 – 2^7 – 2^6 – 2^4 – 1

这样一个数,要逐一算出可能性取匹配几乎是不可能的。

总结一下椭圆曲线的数学依据:

K = kG

  • G 为椭圆曲线上的一个点,叫基点;
  • k 为正整数;
  • 如果给定小 k 和 G,通过椭圆曲线加法法则去计算 K 很容易;
  • 如果给定 K 和 G,求小 k 就非常困难。

一般规定大 K 为公开密钥,小 k 为私钥。

五、椭圆曲线的加密强度

这里我们那攻击分组对称加密 AES 算法的难度来做对比,如下表所示:

  AES	RSA  	ECC
  80	1024	163
  112	2240	233
  128	3072	283

这组数据是国际期刊和一些学术论文中公认的结果,具体的实现步骤这里就不介绍,重点是要明白 ECC 的强度高,像 AES-128 位的密码强度和 RSA 的 3072 位密码强度大约相同,同样和 ECC 的 283 位密码强度相同,密码强度其实就是攻击的难度,也就是说攻击 128 位密钥的 AES 的算法的难度和 ECC283 位密钥的难度相当。

从图表中可以观察到 RSA 的密钥长度增长的很多,ECC 的密钥长度增长并不大,考虑到实际使用过程中的性能和未来能持续的安全行,ECC 是更好的选择。

六、Go语言使用椭圆曲线签名认证实现

package main
import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "math/big"
    "fmt"
)
//通过椭圆曲线完成签名和验证
func main() {
    //声明明文
    message := []byte("hello world")
    //生成私钥
    privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    //生成公钥
    pub := privateKey.PublicKey
    //将明文散列
    digest := sha256.Sum256(message)
    //签名
    r, s, _ := ecdsa.Sign(rand.Reader, privateKey, digest[:])
    //设置私钥的参数类型为曲线类型
    param := privateKey.Curve.Params()
    //获得私钥byte长度
    curveOrderByteSize := param.P.BitLen() / 8
    //获得签名返回值的字节
    rByte, sByte := r.Bytes(), s.Bytes()
    //创建数组
    signature := make([]byte, curveOrderByteSize*2)
    //通过数组保存了签名结果的返回值
    copy(signature[curveOrderByteSize-len(rByte):], rByte)
    copy(signature[curveOrderByteSize*2-len(sByte):], sByte)
    //认证
    //将明文做hash散列,为了验证的内容对比
    digest = sha256.Sum256(message)
    curveOrderByteSize = pub.Curve.Params().P.BitLen() / 8
    //创建两个整形对象
    r, s = new(big.Int), new(big.Int)
    //设置证书值
    r.SetBytes(signature[:curveOrderByteSize])
    s.SetBytes(signature[curveOrderByteSize:])


​```
//认证
e := ecdsa.Verify(&pub, digest[:], r, s)

if e == true {
    fmt.Println("OK")
} else {
    fmt.Println("failed")
}
​```

}
    原文作者:码墨
    原文地址: https://blog.csdn.net/guidao13/article/details/84206770
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞