ECC算法实践

关于椭圆曲线加密算法 (ECC) ,前几天因为想算序列号,所以研究了一下,把知道的东西写一下,怕以后忘记。
关于 ECC 算法本身,请 Google 一篇文件《 ECC 加密算法入门介绍》。在此,只介绍几个概念。
所谓椭圆曲线,是指类似于 y^2+a1xy+a3y = x^3+a2x^2+a4x+a6 这个表达式的曲线。
为了简便起见,有的算法采用 y^2 = x^3+ax^2+b 的形式,而有的采用 y^2 + xy = x^3 + ax^2 + b 的形式。
椭圆曲线为了用于密码学,需要把以前的连续曲线变为有限域上的点。所谓有限域,就是由有限个元素组成的域,比如说有限域 Fp ,就是由 P 个元素组成的域。
在引入有限域后,原来的加减乘除法则都发生了变化,例如:
加法是 a+b≡c (mod p) ,即 (a+b)÷p 的余数 c÷p 的余数相同。
乘法是 a×b≡c (mod p) ,即 (a×b)÷p 的余数 c÷p 的余数相同。
b 的倒数 b^-1 的算法是用 b×b^-1≡1 (mod p) 来算的。
举例来说假定有限域是 20 ,那么 11+13≡4 3 的倒数是 7
有限域的算法对 ECC 来说非常关键,由此可以类推的是 b 的平方根 c 应该是 c×c≡b (mod p) ,所以相当难算,到现在我也想不出算法是什么。
还有一个很重要的概念就是阶,对于椭圆曲线上的某个点 G ,存在一个最小的数 n ,使得 nG=O∞ ,那么 n 就是 G 点的阶。
ECC 可以用户数据加密,签名等,也可以用户生成软件序列号,微软的软件序列号就是用的 ECC
ECC 生成软件序列号的方式是:
   1 、选择一条椭圆曲线 Ep(a,b) ,和基点 G
   2
、选择私有密钥 k k<n n G 的阶),利用基点 G 计算公开密钥 K=kG
   3
、产生一个随机整数 r r<n ),计算点 R=rG
   4
、将用户名和点 R 的坐标值 x,y 作为参数,计算 SHA Secure Hash Algorithm )值,即 Hash=SHA(username,x,y)
   5
、计算 sn≡r - Hash * k (mod n)
   6
、将 sn Hash 作为 用户名 username 的序列号
验证的算法为:
1 、从用户输入的序列号中,提取 sn 以及 Hash
2
、计算点 R≡sn*G+Hash*K ( mod p ) ,如果 sn Hash 正确,其值等于软件作者签名过程中点 R(x,y) 的坐标,因为
      sn≡r-Hash*k
mod n
     
所以
       sn*G + Hash*K
      =(r-Hash*k)*G+Hash*K
      =rG-Hash*kG+Hash*K
      =rG- Hash*K+ Hash*K
      =rG=R

 3
、将用户名和点 R 的坐标值 x,y 作为参数,计算 H=SHA(username,x,y)
 4
、如果 H=Hash 则注册成功。如果 H≠Hash ,则注册失败。
实现该算法时,可以采用一些 Open Source ECC 库,例如 borZoi
borZoi 选定有限域用的是这样的方法:
// Field polynomial: p(t) = t^163 + t^7 + t^6 + t^3 + 1
inline void use_NIST_B_163 () { F2X pt=Pentanomial (163, 7, 6, 3, 0); setModulus (pt); };
可想而知,这个有限域的值是很大的,但是这还只是borZoi中最小的有限域,可以选择强度更大的有限域。其中的F2X是封装了大数加减乘除等运算的类。
接下来定义曲线:
// Degree 163 Binary Field from fips186-2
// Pseudorandom curve E: y^2 + xy = x^3 + x^2 + b,
// b = 2 0a601907 b8c953ca 1481eb10 512f7874 4a3205fd
// Base point order:
// r = 5846006549323611672814742442876390689256843201587
// Base point G:
// Gx = 3 f0eba162 86a2d57e a0991168 d4994637 e8343e36
// Gy = 0 d51fbc6c 71a0094f a2cdd545 b11c5c0c 797324f1
// Cofactor f = 2
#define NIST_B_163 EC_Domain_Parameters (163, 3, 7, 6, 3, Curve ("1", "20a601907b8c953ca1481eb10512f78744a3205fd"), decto_BigInt ("5846006549323611672814742442876390689256843201587"), Point ("3f0eba16286a2d57ea0991168d4994637e8343e36", "0d51fbc6c71a0094fa2cdd545b11c5c0c797324f1"), decto_BigInt ("2"));
可以看到,borZoi用的曲线方程是y^2 + xy = x^3 + x^2 + ba1,但是b选择了一个大数:
b = 2 0a601907 b8c953ca 1481eb10 512f7874 4a3205fd
基点G的座标也是很大的数,rG点阶,可以验证rG=0
如果试图自己找到另外一个G点,是非常困难的一件事,我曾经试图自己找到一个,但是最终还是放弃了。虽然从道理上来说很简单,只要满足方程式就可以了。
我们先找到一对私钥和公钥。
use_NIST_B_163 ();
EC_Domain_Parameters dp = NIST_B_163;
ECPrivKey sk (dp);
ECPubKey pk (sk);
构造私钥的代码其实就是:
ECPrivKey::ECPrivKey (const EC_Domain_Parameters& ecdp) {
         dp = ecdp;
         s = GenRandom (dp.m);
         s %= dp.r;
}
实际上就是选择了一个小于阶r的一个整数。
根据私钥生成公钥的过程是:
ECPubKey::ECPubKey (const ECPrivKey& sk) {
         dp = sk.dp;
         Curve E (dp.a, dp.b);
         W = E.mul (sk.s, dp.G);
}
实际上就是按照算法要求的计算K=sGborZoi提供了曲线计算的很多方法,例如,此处计算sG,用的是Curve类的mul方法。
然后,我们根据算法要求,生成一个随机数rr<n,并且计算R=rG
BigInt r = GenRandom (pk.dp.m);
r %= pk.dp.r;
Curve C(pk.dp.a, pk.dp.b);
Point R=C.mul (r, pk.dp.G);
然后我们定义一个字符串作为用户名
OCTETSTR data(3);
data[0] = 'a'; data[1] = 'b'; data[2] = 'c';
将用户名和R点的座标计算SHA
OCTETSTR x_buf = FE2OSP (R.x);
OCTETSTR y_buf = FE2OSP (R.y);
BigInt Hash = OS2IP (SHA1 (data || x_buf || y_buf));
OCTETSTR 的定义实际就是:
typedef unsigned char OCTET;
typedef std::vector<OCTET> OCTETSTR;
然后计算 sn≡r - Hash * k (mod n)
BigInt sn = sk.s * Hash;
sn %= pk.dp.r;  // pk.dp.r 就是G点的阶
sn = r - sn;
if (sn < BigInt(0))
         sn += pk.dp.r;
sn Hash就是 data 的序列号了。
下面验证,根据算法要求, 计算点 R≡sn*G+Hash*K ( mod p )
Point R2 = C.mul(sn,pk.dp.G);
R2 = C.add(R2, C.mul(Hash,pk.W));
OCTETSTR x2_buf = FE2OSP (R2.x);
OCTETSTR y2_buf = FE2OSP (R2.y);
然后, H=SHA(username,x,y)
BigInt Hash2 = OS2IP (SHA1 (data || x2_buf || y2_buf));
判断两次的Hash值是否是相等的就可以了。
以上就是生成和验证序列号的方法,遗憾的是,borZoi的曲线和G点选择都太大了,生成的序列号太长了。
微软的序列号只有25位,这25位是根据114位的数据用base24编码生成的,这24个字符一般是不容易混淆的字符: BCDFGHJKMPQRTVWXY2346789
根据文章《 Microsoft 25 CDKey 里有什么 》记载,这 114 位的序列号包括:
31 位的 Data 部分,对应于我们例子中的 data 部分; 28 位的 Hash 55 位的签名。
根据算法的原理可知,Hash数据取多少位无所谓,如果需要28位,直接从前往后取28位就可以,不影响算法的准确性。
但是签名数据如果取55位的话,就比较麻烦,因为根据算法,我们知道如果签名是55位的话,就要求G点的阶r不能多于56位,否则不能保证签名一定会少于55位。因为我们需要找到一个G点,使他的阶r小于56位。我曾经试图找过,但是因为算法较笨,电脑跑了半天也没有找到,所以最后还是放弃了。

本文出自 “yuewen” 博客,转载请与作者联系!

分享至
一键收藏,随时查看,分享好友!
fbly
1人
了这篇文章
类别:未分类┆阅读( 0)┆评论( 0) ┆ 返回博主首页┆ 返回博客首页
下一篇 JASJAR使用心得

相关文章

  • 银行家算法
  • 安装使用mySQL
  • 软件开发自动化ANT

职位推荐

  • Java软件工程师
  • 【JAVA系统架构师】--楚楚街
  • JAVA工程师
  • PHP开发工程师
  • web前端开发工程师

文章评论

 
[1楼]        fbly  回复
2012-05-01 16:45:56
学习了,可以把代码发给我吗?谢谢   [email protected]

 

发表评论            

昵  称:
登录  快速注册
验证码:

点击图片可刷新验证码请点击后输入验证码博客过2级,无需填写验证码

内  容:

同时赞一个

每日博报 精彩不止一点关闭

你可能感兴趣的:(代码,算法,开发,软件,ECC)