椭圆曲线算法可以看作是定义在特殊集合下数的运算,满足一定的规则。椭圆曲线在如下两个域中定义:Fp域和F2m域。
Fp域,素数域,p为素数;
F2m域:特征为2的有限域,称之为二元域或者二进制扩展域。该域中,元素的个数为2m个。
椭圆曲线标准文档如下:
1) X9.62
Public Key Cryptography For The Financial Services Industry: The Elliptic Curve Digital Signature Algorithm (ECDSA);
2) SEC 1
SEC 1:Elliptic Curve Cryptography;
3) SEC 2
SEC 2: Recommended Elliptic Curve Domain Parameters;
4) NIST
(U.S.) National Institute of Standards and Technology,美国国家标准。
这些标准一般都描述了Fp域和F2m域、椭圆曲线参数、数据转换、密钥生成以及推荐了多种椭圆曲线。
一些术语说明:
1) 椭圆曲线的阶(order of a curve)
椭圆曲线所有点的个数,包含无穷远点;
2) 椭圆曲线上点的阶(order of a point)
P为椭圆曲线上的点,nP=无穷远点,n取最小整数,既是P的阶;
3) 基点(base point)
椭圆曲线参数之一,用G表示,是椭圆曲线上都一个点;
4) 余因子(cofactor)
椭圆曲线的余因子,用h表示,为椭圆曲线点的个数/基点的阶
5) 椭圆曲线参数:
素数域:
(p,a,b,G,n,h)
其中,p为素数,确定Fp,a和b确定椭圆曲线方程,G为基点,n为G的阶,h为余因子。
二进制扩展域:
(m,f(x),a,b,G,n,h)
其中,m确定F2m,f(x)为不可约多项式,a和b用于确定椭圆曲线方程,G为基点,n为G的阶,h为余因子。
6) 椭圆曲线公钥和私钥
椭圆曲线的私钥是一个随机整数,小于n;
椭圆曲线的公钥是椭圆曲线上的一个点:Q=私钥*G。
20.2 openssl的ECC实现Openssl实现了ECC算法。ECC算法系列包括三部分:ECC算法(crypto/ec)、椭圆曲线数字签名算法ECDSA (crypto/ecdsa)以及椭圆曲线密钥交换算法ECDH(crypto/ecdh)。
研究椭圆曲线需要注意的有:
1) 数据结构
? 椭圆曲线数据结构:EC_GROUP,该结构不仅包含各个参数,还包含了各种算法;
? 点的表示:EC_POINT,其中的大数X、Y和Z为雅克比投影坐标;
? EC_CURVE_DATA:用于内置椭圆曲线,包含了椭圆曲线的各个参数;
? 密钥结构
椭圆曲线密钥数据结构如下,定义在crypto/ec_lcl.h中,对用户是透明的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
struct ec_key_st
{
int version;
EC_GROUP *group;
EC_POINT *pub_key;
BIGNUM *priv_key;
/* 其他项 */
}
2) 密钥生成
对照公钥和私钥的表示方法,非对称算法不同有各自的密钥生成过程。椭圆曲线的密钥生成实现在crytpo/ec/ec_key.c中。Openssl中,内置的椭圆曲线密钥生成时,首先用户需要选取一种椭圆曲线(openssl的crypto/ec_curve.c中内置实现了67种,调用EC_get_builtin_curves获取该列表),然后根据选择的椭圆曲线计算密钥生成参数group,最后根据密钥参数group来生公私钥。
3)签名值数据结构
非对称算法不同,签名的结果表示也不一样。与DSA签名值一样,ECDSA的签名结果表示为两项。ECDSA的签名结果数据结构定义在crypto/ecdsa/ecdsa.h中,如下:
typedef struct ECDSA_SIG_st
{
BIGNUM *r;
BIGNUM *s;
} ECDSA_SIG;
4) 签名与验签
对照签名结果,研究其是如何生成的。crypto/ecdsa/ ecs_sign.c实现了签名算法,crypto/ecdsa/ ecs_vrf.c实现了验签。
5) 密钥交换
研究其密钥交换是如何进行的;crypto/ecdh/ech_ossl.c实现了密钥交换算法。
文件说明:
EC_METHOD实现
ec2_smpl.c F2m 二进制扩展域上的EC_METHOD实现;
ecp_mont.c Fp 素数域上的EC_METHOD实现,(Montgomery 蒙哥马利)
ecp_smpl.c Fp 素数域上的EC_METHOD实现;
ecp_nist.c Fp 素数域上的EC_METHOD实现;
ec2_mult.c
F2m上的乘法;
ec2_smpt.c
F2m上的压缩算法;
ec_asn1.c
asn1编解码;
ec_check.c
椭圆曲线检测;
ec_curve.c
内置的椭圆曲线,
NID_X9_62_prime_field:X9.62的素数域;
NID_X9_62_characteristic_two_field:X9.62的二进制扩展域;
NIST:美国国家标准
ec_cvt.c
给定参数生成素数域和二进制扩展域上的椭圆曲线;
ec_err.c
错误处理;
ec_key.c
椭圆曲线密钥EC_KEY函数;
ec_lib.c
通用库实现,一般会调用底层的EC_METHOD方法;
ec_mult.c
This file implements the wNAF-based interleaving multi-exponentation method乘法;
ec_print.c
数据与椭圆曲线上点的相互转化;
ectest.c
测试源码,可以参考此源码学习椭圆曲线函数。
ec.h
对外头文件;
ec_lcl.h
内部头文件,数据结构一般在此定义。
20.3 主要函数20.3.1参数设置1) int EC_POINT_set_affine_coordinates_GF2m(const EC_GROUP *group, EC_POINT *point,
const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx)
说明:设置二进制域椭圆曲线上点point的几何坐标;
2) int EC_POINT_set_affine_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx)
说明:设置素数域椭圆曲线上点point的几何坐标;
3) int EC_POINT_set_compressed_coordinates_GF2m(const EC_GROUP *group, EC_POINT *point,const BIGNUM *x, int y_bit, BN_CTX *ctx)
说明:二进制域椭圆曲线,给定压缩坐标x和y_bit参数,设置point的几何坐标;用于将Octet-String转化为椭圆曲线上的点;
4) int EC_POINT_set_compressed_coordinates_GFp(const EC_GROUP *group, EC_POINT *point, const BIGNUM *x, int y_bit, BN_CTX *ctx)
说明:素数域椭圆曲线,给定压缩坐标x和y_bit参数,设置point的几何坐标;用于将Octet-String转化为椭圆曲线上的点;
5) int EC_POINT_set_Jprojective_coordinates_GFp(const EC_GROUP *group, EC_POINT *point,
const BIGNUM *x, const BIGNUM *y, const BIGNUM *z, BN_CTX *ctx)
说明:素数域椭圆曲线group,设置点point的投影坐标系坐标x、y和z;
6) int EC_POINT_set_to_infinity(const EC_GROUP *group, EC_POINT *point)
说明:将点point设为无穷远点
7) int EC_GROUP_set_curve_GF2m(EC_GROUP *group, const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
说明:设置二进制域椭圆曲线参数;
8) int EC_GROUP_set_curve_GFp(EC_GROUP *group, const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
说明:设置素数域椭圆曲线参数;
9) int EC_GROUP_set_generator(EC_GROUP *group, const EC_POINT *generator, const BIGNUM *order, const BIGNUM *cofactor)
说明:设置椭圆曲线的基G;generator、order和cofactor为输入参数;
10) size_t EC_GROUP_set_seed(EC_GROUP *group, const unsigned char *p, size_t len)
说明:设置椭圆曲线随机数,用于生成a和b;
11) EC_GROUP *EC_GROUP_new_curve_GF2m(const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
说明:生成二进制域上的椭圆曲线,输入参数为p,a和b;
12) EC_GROUP *EC_GROUP_new_curve_GFp(const BIGNUM *p, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
说明:生成素数域上的椭圆曲线。
20.3.2参数获取1) const EC_POINT *EC_GROUP_get0_generator(const EC_GROUP *group)
说明:获取椭圆曲线的基(G);
2) unsigned char *EC_GROUP_get0_seed(const EC_GROUP *group)
说明:获取椭圆曲线参数的随机数,该随机数可选,用于生成椭圆曲线参数中的a和b;
3) int EC_GROUP_get_basis_type(const EC_GROUP *group)
说明:获取二进制域多项式的类型;
4) int EC_GROUP_get_cofactor(const EC_GROUP *group, BIGNUM *cofactor, BN_CTX *ctx)
说明:获取椭圆曲线的余因子。cofactor为X9.62中定义的h,值为椭圆曲线点的个数/基点的阶,即:cofactor = #E(Fq)/n。
5) int EC_GROUP_get_curve_GF2m(const EC_GROUP *group, BIGNUM *p, BIGNUM *a, BIGNUM *b, BN_CTX *ctx)
说明:获取二元域椭圆曲线的三个参数,其中p可表示多项式;
6) int EC_GROUP_get_curve_GFp(const EC_GROUP *group, BIGNUM *p, BIGNUM *a, BIGNUM *b, BN_CTX *ctx)
说明:获取素数域椭圆曲线的三个参数;
7) int EC_GROUP_get_curve_name(const EC_GROUP *group)
说明:获取椭圆曲线名称,返回其NID;
8) int EC_GROUP_get_degree(const EC_GROUP *group)
说明:获取椭圆曲线密钥长度。对于素数域Fp来说,是大数p的长度;对二进制域F2m来说,等于m;
9) int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order, BN_CTX *ctx)
说明:获取椭圆曲线的阶;
10) int EC_GROUP_get_pentanomial_basis(const EC_GROUP *group, unsigned int *k1,
unsigned int *k2, unsigned int *k3)
int EC_GROUP_get_trinomial_basis(const EC_GROUP *group, unsigned int *k)
说明:获取多项式参数;
11) int EC_POINT_get_affine_coordinates_GF2m(const EC_GROUP *group, const EC_POINT *point,BIGNUM *x, BIGNUM *y, BN_CTX *ctx)
说明:获取二进制域椭圆曲线上某个点的x和y的几何坐标;
12) int EC_POINT_get_affine_coordinates_GFp(const EC_GROUP *group, const EC_POINT *point, BIGNUM *x, BIGNUM *y, BN_CTX *ctx)
说明:获取素数域上椭圆曲线上某个点的x和y的几何坐标;
13) int EC_POINT_get_Jprojective_coordinates_GFp(const EC_GROUP *group, const EC_POINT *point,BIGNUM *x, BIGNUM *y, BIGNUM *z, BN_CTX *ctx)
说明:获取素数域椭圆曲线上某个点的x、y和z的投影坐标系坐标。
20.3.3转化函数1) EC_POINT *EC_POINT_bn2point(const EC_GROUP *group, const BIGNUM *bn,EC_POINT *point,BN_CTX *ctx)
说明:将大数转化为椭圆曲线上的点;
2) EC_POINT *EC_POINT_hex2point(const EC_GROUP *group, const char *buf,EC_POINT *point,BN_CTX *ctx)
说明:将buf中表示的十六进制数据转化为椭圆曲线上的点;
3) int BN_GF2m_poly2arr(const BIGNUM *a, unsigned int p[], int max)
说明:将大数转化为多项式的各个项;
4) int BN_GF2m_arr2poly(const unsigned int p[], BIGNUM *a)
说明:将多项式的各个项转化为大数;
5) int EC_POINT_make_affine(const EC_GROUP *group, EC_POINT *point, BN_CTX *ctx)
说明:将椭圆曲线group上点的point转化为几何坐标系;
6) int EC_POINT_oct2point(const EC_GROUP *group, EC_POINT *point,
const unsigned char *buf, size_t len, BN_CTX *ctx)
说明:将buf中点数据转化为椭圆曲线上的点,len为数据长度;
7) BIGNUM *EC_POINT_point2bn(const EC_GROUP *group,const EC_POINT *point, point_conversion_form_t form,BIGNUM *ret,BN_CTX *ctx)
说明:将椭圆曲线上的点转化为大数,其中from为压缩方式,可以是POINT_CONVERSION_COMPRESSED、POINT_CONVERSION_UNCOMPRESSED或POINT_CONVERSION_HYBRID,可参考x9.62;
8) char *EC_POINT_point2hex(const EC_GROUP *group, const EC_POINT *point, point_conversion_form_t form,BN_CTX *ctx)
说明:将椭圆曲线上的点转化为十六进制,并返回该结果;
9) size_t EC_POINT_point2oct(const EC_GROUP *group, const EC_POINT *point, point_conversion_form_t form,unsigned char *buf, size_t len, BN_CTX *ctx)
说明:将椭圆曲线上的点转化为Octet-String,可分两次调用,用法见EC_POINT_point2bn的实现。
20.3.4其他函数1) size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems)
文件:ec_curve.c
说明:获取内置的椭圆曲线。当输入参数r为NULL或者nitems为0时,返回内置椭圆曲线的个数,否则将各个椭圆曲线信息存放在r中。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
#include <openssl/ec.h>
int main()
{
EC_builtin_curve *curves = NULL;
size_t crv_len = 0, n = 0;
int nid,ret;
EC_GROUP *group = NULL;
crv_len = EC_get_builtin_curves(NULL, 0);
curves = OPENSSL_malloc(sizeof(EC_builtin_curve) * crv_len);
EC_get_builtin_curves(curves, crv_len);
for (n=0;n<crv_len;n++)
{
nid = curves[n].nid;
group=NULL;
group = EC_GROUP_new_by_curve_name(nid);
ret=EC_GROUP_check(group,NULL);
}
OPENSSL_free(curves);
return 0;
}
2) const EC_METHOD *EC_GF2m_simple_method(void)
说明:返回二进制域上的方法集EC_METHOD
3) const EC_METHOD *EC_GFp_mont_method(void)
const EC_METHOD *EC_GFp_nist_method(void)
const EC_METHOD *EC_GFp_simple_method(void)
返回素数域上的方法集EC_METHOD
4) int EC_GROUP_check(const EC_GROUP *group, BN_CTX *ctx)
说明:检查椭圆曲线,成功返回1。
5) int EC_GROUP_check_discriminant(const EC_GROUP *group, BN_CTX *ctx)
说明:检查椭圆曲线表达式。对于素数域的椭圆曲线来说,该函数会调用ec_GFp_simple_group_check_discriminant函数,主要检查4*a^3 + 27*b^2 != 0 (mod p)。而对于二进制域的椭圆曲线,会调用ec_GF2m_simple_group_check_discriminant, 检查y^2 + x*y = x^3 + a*x^2 + b 是否是一个椭圆曲线并且 b !=0。
6) int EC_GROUP_cmp(const EC_GROUP *a, const EC_GROUP *b, BN_CTX *ctx)
说明:通过比较各个参数来确定两个椭圆曲线是否相等;
7) int EC_GROUP_copy(EC_GROUP *dest, const EC_GROUP *src)
EC_GROUP *EC_GROUP_dup(const EC_GROUP *a)
说明:椭圆曲线拷贝函数;
9) EC_GROUP *EC_GROUP_new_by_curve_name(int nid)
说明:根据NID获取内置的椭圆曲线;
10) int EC_KEY_check_key(const EC_KEY *eckey)
说明:检查椭圆曲线密钥;
11) int EC_KEY_generate_key(EC_KEY *eckey)
说明:生成椭圆曲线公私钥;
12) int EC_KEY_print(BIO *bp, const EC_KEY *x, int off)
说明:将椭圆曲线密钥信息输出到bio中,off为缩进量;
13) int EC_POINT_add(const EC_GROUP *group, EC_POINT *r, const EC_POINT *a, const EC_POINT *b, BN_CTX *ctx)
说明:椭圆曲线上点的加法;
14) int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a, BN_CTX *ctx)
说明:求椭圆曲线上某点a的逆元,a既是输入参数,也是输出参数;
15) int EC_POINT_is_at_infinity(const EC_GROUP *group, const EC_POINT *point)
说明:判断椭圆曲线上的点point是否是无穷远点;
16) int EC_POINT_is_on_curve(const EC_GROUP *group, const EC_POINT *point, BN_CTX *ctx)
说明:判断一个点point是否在椭圆曲线上;
17) int ECDSA_size
说明:获取ECC密钥大小字节数。
18)ECDSA_sign
说明:签名,返回1表示成功。
19)ECDSA_verify
说明:验签,返回1表示合法。
20)EC_KEY_get0_public_key
说明:获取公钥。
21)EC_KEY_get0_private_key
说明:获取私钥。
22)ECDH_compute_key
说明:生成共享密钥
23)EC_KEY *d2i_ECPrivateKey(EC_KEY **a, const unsigned char **in, long len)
说明:DER解码将椭圆曲线密钥;
24)int i2d_ECPrivateKey(EC_KEY *a, unsigned char **out)
说明:将椭圆曲线密钥DER编码;
20.4 编程示例下面的例子生成两对ECC密钥,并用它做签名和验签,并生成共享密钥。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
#include <string.h>
#include <stdio.h>
#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/objects.h>
#include <openssl/err.h>
int main()
{
EC_KEY *key1,*key2;
EC_POINT *pubkey1,*pubkey2;
EC_GROUP *group1,*group2;
int ret,nid,size,i,sig_len;
unsigned char *signature,digest[20];
BIO *berr;
EC_builtin_curve *curves;
int crv_len;
char shareKey1[128],shareKey2[128];
int len1,len2;
/* 构造EC_KEY数据结构 */
key1=EC_KEY_new();
if(key1==NULL)
{
printf("EC_KEY_new err!\n");
return -1;
}
key2=EC_KEY_new();
if(key2==NULL)
{
printf("EC_KEY_new err!\n");
return -1;
}
/* 获取实现的椭圆曲线个数 */
crv_len = EC_get_builtin_curves(NULL, 0);
curves = (EC_builtin_curve *)malloc(sizeof(EC_builtin_curve) * crv_len);
/* 获取椭圆曲线列表 */
EC_get_builtin_curves(curves, crv_len);
/*
nid=curves[0].nid;会有错误,原因是密钥太短
*/
/* 选取一种椭圆曲线 */
nid=curves[25].nid;
/* 根据选择的椭圆曲线生成密钥参数group */
group1=EC_GROUP_new_by_curve_name(nid);
if(group1==NULL)
{
printf("EC_GROUP_new_by_curve_name err!\n");
return -1;
}
group2=EC_GROUP_new_by_curve_name(nid);
if(group1==NULL)
{
printf("EC_GROUP_new_by_curve_name err!\n");
return -1;
}
/* 设置密钥参数 */
ret=EC_KEY_set_group(key1,group1);
if(ret!=1)
{
printf("EC_KEY_set_group err.\n");
return -1;
}
ret=EC_KEY_set_group(key2,group2);
if(ret!=1)
{
printf("EC_KEY_set_group err.\n");
return -1;
}
/* 生成密钥 */
ret=EC_KEY_generate_key(key1);
if(ret!=1)
{
printf("EC_KEY_generate_key err.\n");
return -1;
}
ret=EC_KEY_generate_key(key2);
if(ret!=1)
{
printf("EC_KEY_generate_key err.\n");
return -1;
}
/* 检查密钥 */
ret=EC_KEY_check_key(key1);
if(ret!=1)
{
printf("check key err.\n");
return -1;
}
/* 获取密钥大小 */
size=ECDSA_size(key1);
printf("size %d \n",size);
for(i=0;i<20;i++)
memset(&digest[i],i+1,1);
signature=malloc(size);
ERR_load_crypto_strings();
berr=BIO_new(BIO_s_file());
BIO_set_fp(berr,stdout,BIO_NOCLOSE);
/* 签名数据,本例未做摘要,可将digest中的数据看作是sha1摘要结果 */
ret=ECDSA_sign(0,digest,20,signature,&sig_len,key1);
if(ret!=1)
{
ERR_print_errors(berr);
printf("sign err!\n");
return -1;
}
/* 验证签名 */
ret=ECDSA_verify(0,digest,20,signature,sig_len,key1);
if(ret!=1)
{
ERR_print_errors(berr);
printf("ECDSA_verify err!\n");
return -1;
}
/* 获取对方公钥,不能直接引用 */
pubkey2 = EC_KEY_get0_public_key(key2);
/* 生成一方的共享密钥 */
len1=ECDH_compute_key(shareKey1, 128, pubkey2, key1, NULL);
pubkey1 = EC_KEY_get0_public_key(key1);
/* 生成另一方共享密钥 */
len2=ECDH_compute_key(shareKey2, 128, pubkey1, key2, NULL);
if(len1!=len2)
{
printf("err\n");
}
else
{
ret=memcmp(shareKey1,shareKey2,len1);
if(ret==0)
printf("生成共享密钥成功\n");
else
printf("生成共享密钥失败\n");
}
printf("test ok!\n");
BIO_free(berr);
EC_KEY_free(key1);
EC_KEY_free(key2);
free(signature);
free(curves);
return 0;
}
更多底层函数的使用示例可参考ec/ectest.c,特别是用户自行定义椭圆曲线参数。
转自:
%E7%BC%96%E7%A8%8B-%E7%AC%AC%E4%BA%8C%E5%8D%81%E7%AB%A0-%E6%A4%AD%E5%9C%86%E6%9B%B2%E7%BA%BF/
*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。