椭圆曲线加密漫谈(填坑结束)

椭圆曲线加密漫谈

最近做一个分析,碰到了采用椭圆曲线的情况,顺路学习一下这种加密方式的用法和基本原理。这里作为记录学习的过程和想法。RSA见得多了,比较熟。ECC见得少,还没有符号,看起来是真的痛苦。

​ 相对于传统的对称加密(DES/AES),非对称加密拥有一个更优的品质,那就是不用传输密钥本身来实现两端的加解密,同时还可以胜任身份认证的过程。本期漫谈,我们会简单的涉及到椭圆曲线的意义和如何使用openssl来实现一次加密(C语言实现)。

对称与非对称

​ 我们常说对称与非对称,它们的意思究竟是什么?本质来看,就是加解密是否应用了同一个密钥k[1]。对称加密的数学表达很简单,形式如下:

E = f(M,k) \\ M = f^{-1}(E,k)

​ 假如我们用一段简单的代码来表示,可以写成:

for(int i=0;i<src_len;i++)
    src[i] ^= key[i];
for(int i=0;i<enc_len;i++)
    enc[i] ^= key[i];

​ 上述就是一个最简单的加密,各字段意义如下:

	1. 加密函数`f`和它的逆`f^-1`
	2. 原文`M`与密文`E`
	3. 密钥`k`

​ 现实中常见的对称加密一般是AES,这一部分很多代码片段,这里不再赘述。考察上式可以注意到,k在这里面起到了关键作用,它依赖于双方的约定,要么是提前商量好,要么是加密时传输。这就会导致密钥泄露的风险。

​ 那么非对称加密又是什么呢?非对称加密的可靠性通常依赖一个数学问题,比如RSA算法和bitcoin所用的secp256k1两者的数学本质可以分别表示为:

n = p*q \\ b = a^i(mod\quad p) (0\leq i\leq p)

​ 两者的可靠性分别建立在大数分解问题和离散对数问题[2]之上(计算的复杂度不可达)。相对于对称加密,二者采用了另一种思路,把加密的可靠性通过数学来保证,而非密钥隐藏。

​ 通过上述的描述,我们知道了两者的区别,但是我们必须承认,对称加密的速度是很快的,这一点非对称还存在差距。

非对称加密-椭圆曲线

​ 相对于上一个公式,下面这一个公式可能更容易理解,其分别的代表意义如下:

Q = d*G \\ E = \{rG, M+rQ\}
  1. 私钥d
  2. 基点G
  3. 公钥Q
  4. 密文E,原文M
  5. 随机数r

​ 在椭圆曲线中,我们可以简单的理解成,已知d、G很容易计算Q。但是已知Q、G很难计算出d。这就是我们使用非对称加密的优势所在,同时也可以被用作身份认证。(联想到什么了吗?证书)

​ 发行者持有d、G而公开Q、G。采用Q加密,只有d才能解密。而身份验证相反,d加密,采用Q进行验证。下面给出一个简单的采用椭圆曲线加解密流程。

​ 注意上方的E是采用了一个点对的形式表达,这意味我们同时给出了rQ和M+rG,这里的+代表一种运算方式,可以简单的理解成上述对称加密中的f。在接收到E之后,下列公式将会恢复M,这就是解密的基础数学原理。

M+rQ-d(rG) = M+rQ-r(dG) = M

ECC示例代码

代码将会极度简化,不包含任何错误处理并且不包含任何可用公私钥。

int do_enc(char *src, size_t src_len, char *enc, size_t *enc_len, const EC_KEY *pub_ec_key)
{
    const EC_GROUP *group = EC_KEY_get0_group(pub_ec_key);
    BN_CTX *ctx = BN_CTX_new();
    BIGNUM *kx = BN_new();
    BIGNUM *ky = BN_new();
    EC_POINT *calc_point = EC_POINT_new(group);
    EC_POINT *rG = EC_POINT_new(group);
    EC_POINT *rQ = EC_POINT_new(group);
    
    const EC_POINT *pubk = EC_KEY_get0_public_key(pub_ec_key);
    
    /*选取一个随机数,并且计算rG,rQ,最终将rQ转化成坐标形式参与后续运算*/
    BIGNUM *generate_rnd = BN_new();
    BIGNUM *bn_range = BN_new();
    ret = BN_rand_range(generate_rnd, bn_range);
    EC_POINT_mul(group, rG, generate_rnd, 0, 0, ctx);
    EC_POINT_get_affine_coordinates(group, rQ, kx, ky, ctx);
    EC_POINT_mul(group, rQ, 0, pubk, generate_rnd, ctx);
    
    /*操作kx ky进行加密即可*/
    ......
}

int do_dec(char *src, size_t src_len, char *dec, size_t *dec_len, const EC_KEY *pri_ec_key)
{
    /*我们是获取的E = {rG,M+rQ}, 恢复rG,然后和私钥计算得到rQ*/
    const BIGNUM *pri_key_bn = EC_KEY_get0_private_key(pri_ec_key);
    const EC_GROUP *group = EC_KEY_get0_group(pri_ec_key);
    EC_POINT *rG = EC_POINT_new(group);
    ret = EC_POINT_set_affine_coordinates(group, rG, kx, ky, ctx);
    
    EC_POINT *rQ = EC_POINT_new(group);
    ret = EC_POINT_mul(group, rQ, 0, rG, pri_key_bn, ctx);
    
    /*还记得上面的rQ吗,它就是参与加密的部分,下面代码操作kx ky进行解密即可*/
    ......
    
}

int main()
{
    char *use_pubk = "THIS IS PUBLICK_KEY.GENERATED BY OPENSSL";
    char *use_prik = "THIS IS PRIVATE_KEY.GENERATED BY OPENSSL";
	EVP_PKEY *evp_key = EVP_PKEY_new();
	BIO *bio = BIO_new_mem_buf(use_pubk, strlen(use_pubk));
    BIO *prik_bio = BIO_new_mem_buf(www_prik, strlen(www_prik));
    const EC_KEY *pri_ec_key = PEM_read_bio_ECPrivateKey(prik_bio, NULL, NULL, NULL);
	PEM_ASN1_read_bio((d2i_of_void *)d2i_PUBKEY, "PUBLIC KEY", bio, (void **)&evp_key,  NULL, NULL);
	EC_KEY *pub_ec_key = EVP_PKEY_get1_EC_KEY(evp_key);
    char *src = "hello, ecc!";
    size_t src_len = strlen(src);
    char enc[256] = {0,};
    size_t enc_len = 255;
    char dec[256]  ={0,};
    size_t dec_len = 255;
    
    do_enc(src, src_len, enc, enc_len, pub_ec_key);
    do_dec(enc,enc_len, dec, dec_len, pri_ec_key);
}

好,到此我们就简单了解了ecc的原理和如何使用openssl进行一次加解密。同时,注意到由于随机数的存在,每一次的加密密文都是不同的,这一点相较于对称加密,也是一个很大的区别。

啥也不是,散会!:laughing:


  1. 對稱密鑰加密 - 维基百科,自由的百科全书 ↩︎

  2. 应用密码学 | 离散对数问题(Discrete logarithm Problem) - 知乎 ↩︎

14 个赞

mark

1 个赞

好家伙 :+1: :+1:

1 个赞

后仰马克

23 个赞

高端起来了

2 个赞

别人是渐渐的看不懂了,我就不一样,我直接开始就看不懂

1 个赞

既然都看到这里了,也发在玩咯安全区了,为啥不去研究一下一些特殊可解的情况,这些特殊情况都是一些安全问题,先看你继续学不,不继续后面我单独开一贴讲,最近比较忙,没空写这些文章

3 个赞

粗粗就看了一遍,希望有一个示例输入和输出结果,看看我用别的语言写是否能得出正确结果,感谢 :grin:

21 个赞

学习了哈哈

:joy:有随机数存在,你很难得到和我一致的结果的。只能自己同时计算出QG来检查。而且kx/ky的参与运算方式看你自己喜好,这里面也不太一样。

哦哦,我看看什么地方能验证一下结果

21 个赞

好文。内容过硬、格式优良,最主要居然还有TOC

1 个赞

你说的特殊可解点吗?没有研究呢,这个只是我工作时碰到的情况,要分析代码才去补课了一点ecc的知识,简化版也是为了提文件才写的。从数学原理上来看,如果这些特殊点存在,那意味着d的保密性无法得到保证了,它的安全性不是就很难保证吗?大佬如果有空发帖子,我就来学习:kissing_heart:

1 个赞

最简单的就是python调用openssl,你在网站自己生成一对公私钥,然后去计算QG。先算G,映射到kx ky,然后恢复G。再尝试用私钥恢复Q。对比一下恢复的Q和计算出来的Q。这两次Q如果是一致的,那就说明你成功啦。我记得有一个函数是用来对比两个点的,EC_POINT_cmp()。你得查一查,我记不清有点。
这样就相当于模拟一次公钥加密,然后你把点对E传输,在其他地方解密。

1 个赞

TOC怎么搞的,# 好像不能在右侧出现导航

22 个赞

感谢佬!我按照你说的研究一下 :smiling_face_with_three_hearts:

22 个赞

写文章的时候点小齿轮,插入目录

2 个赞

我试下编辑之前的文章

22 个赞

mark

1 个赞

楼主对这个公式全部理解了吗?