(四)--openssl 实现国密算法(签名和验签)
程序员文章站
2022-03-16 08:41:13
...
我们接着来签名和验签。
下列符号适用于本部分。
A,B:使用公钥密码系统的两个用户。
a,b: Fq中的元素,它们定义Fq上的一条椭圆曲线E。
2dA:用户A的私钥。
E(Fq): Fq上椭圆曲线E 的所有有理点(包括无穷远点O)组成的集合。
e:密码杂凑函数作用于消息M的输出值。
e′:密码杂凑函数作用于消息M ′的输出值。
Fq
:包含q个元素的有限域。
G:椭圆曲线的一个基点,其阶为素数。
Hv
( ):消息摘要长度为v比特的密码杂凑函数。
IDA:用户A的可辨别标识。
M:待签名的消息。
M ′:待验证消息。
modn:模n运算。例如, 23mod7=2。
n:基点G的阶(n是#E(Fq)的素因子)。
O:椭圆曲线上的一个特殊点,称为无穷远点或零点,是椭圆曲线加法群的单位元。
PA:用户A的公钥。
q:有限域Fq中元素的数目。
x ∥ y: x与y的拼接,其中x、 y可以是比特串或字节串。
ZA:关于用户A的可辨别标识、部分椭圆曲线系统参数和用户A公钥的杂凑值。
(r,s):发送的签名。
(r′,s′):收到的签名。
[k]P:椭圆曲线上点P的k倍点,即, [k]P= P + P + · · · + P(k个P, k是正整数)。
[x,y]:大于或等于x且小于或等于y的整数的集合。
⌈x⌉:顶函数,大于或等于x的最小整数。例如, ⌈7⌉=7, ⌈8.3⌉=9。
⌊x⌋:底函数,小于或等于x的最大整数。例如, ⌊7⌋=7, ⌊8.3⌋=8。
#E(Fq): E(Fq)上点的数目,称为椭圆曲线E(Fq)的阶。
sm2签名算法流程
设待签名的消息为M,为了获取消息M的数字签名(r,s),作为签名者的用户A应实现以下运算步
骤:
A1:置M=ZA ∥ M;
A2:计算e = Hv(M),按本文本第1部分4.2.3和4.2.2给出的细节将e的数据类型转换为整数;
A3:用随机数发生器产生随机数k ∈[1,n-1];
A4:计算椭圆曲线点(x1,y1)=[k]G,按本文本第1部分4.2.7给出的细节将x1的数据类型转换为整
数;
A5:计算r=(e+x1) modn,若r=0或r+k=n则返回A3;
A6:计算s = ((1 + dA)−1 · (k − r · dA)) modn,若s=0则返回A3;
A7:按本文本第1部分4.2.1给出的细节将r、 s的数据类型转换为字节串,消息M 的签名为(r,s)。
下面是流程图:
sm2算法验签流程
为了检验收到的消息M ′及其数字签名(r′, s′),作为验证者的用户B应实现以下运算步骤:
B1:检验r′ ∈[1,n-1]是否成立,若不成立则验证不通过;
B2:检验s′ ∈[1,n-1]是否成立,若不成立则验证不通过;
B3:置M ′=ZA ∥ M ′;
B4:计算e′ = Hv(M ′),按本文本第1部分4.2.3和4.2.2给出的细节将e′的数据类型转换为整数;
B5:按本文本第1部分4.2.2给出的细节将r′、 s′的数据类型转换为整数,计算t = (r′ + s′) modn,
若t = 0,则验证不通过;
B6:计算椭圆曲线点(x′ 1; y1 ′ )=[s′]G + [t]PA;
B7:按本文本第1部分4.2.7给出的细节将x′ 1的数据类型转换为整数,计算R = (e′ + x′ 1) modn,检
验R=r′是否成立,若成立则验证通过;否则验证不通过。
下面是流程图:
最后是最关键的一步上代码:
签名的代码
int iret;
unsigned char ENTLA[3] = {0};
unsigned char ZA[33] = {0};
unsigned char* Z = NULL, *M1 = NULL;
int z_len;
EC_POINT* kG = NULL;
BIGNUM* k = NULL, *e = NULL, *kGx = NULL,
*kGy= NULL, *r=NULL, *s=NULL, *dA=NULL;
ENTLA[0] = (ida_len>>8)&0xff;
ENTLA[1] = ida_len&0xff;
z_len = ida_len + 194;
Z = new unsigned char[z_len + 1];
memset(Z, 0, z_len + 1);
memcpy(Z, ENTLA, 2);
memcpy(&Z[2], IDA, ida_len);
BN_bn2bin(this->a, &Z[ida_len+2]);
BN_bn2bin(this->b, &Z[ida_len+34]);
BN_bn2bin(this->gx, &Z[ida_len+66]);
BN_bn2bin(this->gy, &Z[ida_len + 98]);
BN_bn2bin(this->pubx, &Z[ida_len + 130]);
BN_bn2bin(this->puby, &Z[ida_len + 162]);
if(!hash(Z, z_len, ZA, "sha256"))
{
iret = -1;
goto signature_end;
}
M1 = new unsigned char[33 + m_len];
memset(M1, 0, 33 + m_len);
memcpy(M1, ZA, 32);
memcpy(&M1[32], M, m_len);
if(!hash(M1, m_len + 32, ZA, "sha256"))
{
iret = -2;
goto signature_end;
}
k = BN_new();
BN_rand_range(k, this->z);
e = BN_new();
BN_bin2bn(ZA, 32, e);
kG = EC_POINT_new(this->mGroup);
EC_POINT_mul(this->mGroup, kG, NULL,
this->mGP, k, this->ctx);
kGx = BN_new();
kGy = BN_new();
if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup,
kG, kGx, kGy, this->ctx))
{
iret = -3;
goto signature_end;
}
r = BN_new();
s = BN_new();
//r=(e+x1) modn
BN_add(s, kGx, e);
BN_mod(r, s, this->z, this->ctx);
//s=((k − r*dA)*(1 + dA)逆)modn
dA = BN_new();
BN_copy(dA, EC_KEY_get0_private_key(this->mKey));
BN_mul(s, r, dA, this->ctx);
BN_sub(k, k, s);
BN_add_word(dA, 1);
BN_mod_inverse(s, dA, this->z, this->ctx);
BN_mod_mul(s, s, k, this->z ,this->ctx);
BN_bn2bin(r, out);
BN_bn2bin(s, &out[BN_num_bytes(r)]);
signature_end:
if(M1 != NULL) delete[] M1;
if(Z != NULL) delete[] Z;
M1 = NULL;
Z = NULL;
if(kG != NULL)EC_POINT_free(kG);
if(k != NULL)BN_free(k);
if(e != NULL)BN_free(e);
if(kGx != NULL)BN_free(kGx);
if(kGy != NULL)BN_free(kGy);
if(r != NULL)BN_free(r);
if(s != NULL)BN_free(s);
if(dA != NULL)BN_free(dA);
return iret;
验签的算法
int iret;
unsigned char br[65] = {0};
unsigned char bs[65] = {0};
int z_len;
unsigned char* Z = NULL, *M1 = NULL;
unsigned char ZA[33] = {0};
unsigned char ENTLA[3] = {0};
EC_POINT* T = NULL, *sG = NULL, *tPa= NULL;
BIGNUM* r = NULL, *s = NULL, *temp= NULL,
*e = NULL, *t = NULL, *x = NULL, *y = NULL;
memcpy(&br[32], sign, 32);
memcpy(&bs[32], &sign[32], 32);
r = BN_new();
s = BN_new();
BN_bin2bn(br, 64, r);
BN_bin2bn(bs, 64, s);
temp = BN_new();
BN_one(temp);
if( BN_ucmp( r, this->z)>0
|| BN_ucmp(s, this->z)>0
|| BN_ucmp(temp, r) > 0
|| BN_ucmp(temp, s) > 0)
{
iret = -1;
goto verify_end;
}
z_len = ida_len + 194;
Z = new unsigned char[z_len + 1];
ENTLA[0] = (ida_len>>8)&0xff;
ENTLA[1] = ida_len&0xff;
memset(Z, 0, z_len + 1);
memcpy(Z, ENTLA, 2);
memcpy(&Z[2], IDA, ida_len);
BN_bn2bin(this->a, &Z[ida_len+2]);
BN_bn2bin(this->b, &Z[ida_len+34]);
BN_bn2bin(this->gx, &Z[ida_len+66]);
BN_bn2bin(this->gy, &Z[ida_len + 98]);
BN_bn2bin(this->pubx, &Z[ida_len + 130]);
BN_bn2bin(this->puby, &Z[ida_len + 162]);
hash(Z, z_len, ZA, "sha256");
M1 = new unsigned char[33 + m_len];
memset(M1, 0, 33 + m_len);
memcpy(M1, ZA, 32);
memcpy(&M1[32], M, m_len);
hash(M1, m_len + 32, ZA, "sha256");
e = BN_new();
BN_bin2bn(ZA, 32, e);
t = BN_new();
BN_mod_add(t, r, s, this->z, this->ctx);
sG = EC_POINT_new(this->mGroup);
tPa = EC_POINT_new(this->mGroup);
EC_POINT_mul(this->mGroup, sG, NULL,
this->mGP, s, this->ctx);
EC_POINT_mul(this->mGroup, tPa, NULL,
EC_KEY_get0_public_key(this->mKey),
t, this->ctx);
T = EC_POINT_new(this->mGroup);
EC_POINT_add(this->mGroup, T, sG, tPa, this->ctx);
x = BN_new();
y = BN_new();
if(!EC_POINT_get_affine_coordinates_GFp(this->mGroup,
T, x, y, this->ctx))
{
iret = -2;
goto verify_end;
}
BN_mod_add(temp, x, e, this->z, this->ctx);
if(BN_cmp(temp, r))
{
iret = -3;
goto verify_end;
}
verify_end:
if (Z!= NULL)delete[] Z;
if(M1 != NULL)delete[] M1;
Z = NULL;
M1 = NULL;
if (sG != NULL)EC_POINT_free(sG);
if (tPa != NULL)EC_POINT_free(tPa);
if (T != NULL)EC_POINT_free(T);
if(r != NULL) BN_free(r);
if(s != NULL)BN_free(s);
if(temp != NULL)BN_free(temp);
if(e != NULL)BN_free(e);
if(t != NULL)BN_free(t);
if(x != NULL)BN_free(x);
if(y != NULL)BN_free(y);
return iret;
这只是代码的实现,后面我会对算法的效率进行优化,但说实在的我们调用了openssl 优化幅度可能不大,牛逼的可以自己去全部实现以下,而且现在很少有人用软算法了,大家都用硬件加密了。