当前位置: 首页 > 工具软件 > sm-validator > 使用案例 >

SM2公钥加密

缪茂勋
2023-12-01

简介

国密SM2算法并不仅仅是提供了新的曲线参数,而是在算法上对ECC进行了修改。

SM2的曲线使用了Weierstrass模型:
y 2 = x 3 + a x + b m o d    P y^2=x^3+ax+b \mod P y2=x3+ax+bmodP

推荐参数

p=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFF
a=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF 00000000 FFFFFFFF FFFFFFFC
b=28E9FA9E 9D9F5E34 4D5A9E4B CF6509A7 F39789F5 15AB8F92 DDBCBD41 4D940E93
n=FFFFFFFE FFFFFFFF FFFFFFFF FFFFFFFF 7203DF6B 21C6052B 53BBF409 39D54123
Gx=32C4AE2C 1F198119 5F990446 6A39C994 8FE30BBF F2660BE1 715A4589 334C74C7
Gy=BC3736A2 F4F6779C 59BDCEE3 6B692153 D0A9877C C62A4740 02DF32E5 2139F0A0

1. 前置条件

1.1 点到字符串的转换

椭圆曲线是关于x轴对称的,点和字节数据转换时,y坐标只需要获取/提供正负即可。

压缩

PC(Padding Char) = 02或03;

int nLsbY = epoint_get(epointC1, bigX, bigX);
if (0 == nLsbY)
{
    PC = 2;
}
else // 1 == nLsbY
{
    PC = 3;
}
S = PC || bigX;

未压缩

PC = 04

PC = 04;
S = PC || X1 || Y1;

混合形式

PC = 06或07

int nLsbY = epoint_get(epointC1, bigX, bigY);
if (0 == nLsbY)
{
    PC = 6;
}
else // 1 == nLsbY
{
    PC = 7;
}
S = PC || bigX || bigY;

1.2 密钥派生函数

 int KDF(const uint8_t* pData
	, uint32_t nData
	, uint32_t nKeyBytesLen
	, PFnHash pfnHash
	, uint32_t nHash
	, uint8_t* pOut	)
{
	int nRet = 0;
	uint32_t nLoop = 0;
	uint32_t zt = 1;
	uint32_t i = 0;

	uint8_t *pBuf = NULL;
	uint32_t nBuf = nData + 4/*sizeof(zt)*/;
	if (!pData || !pOut)
	{
		return 0;
	}


	// alloc memory
	pBuf = (uint8_t *)calloc(nBuf, 1);
	if(!pBuf) return 0;
	memcpy(pBuf, pData, nData);

	nLoop = (nKeyBytesLen + nHash - 1) / nHash;	// upper(nKeyBytesLen/nHash)
	for (i = 0; i < nLoop; ++i)
	{
		u32to8_big(&(pBuf[nData]), zt);
		if (ERR_OK != pfnHash(pBuf, nBuf
			, pOut + i * nHash, nHash))
		{
			break;
		}
		++zt;
	}
	if (i == nLoop)
	{
		nRet = nKeyBytesLen;
	}
	return nRet;
}

6. 加解密

加密流程

  1. 生成随机数k
  2. 计算点C1 = kG = (Xc1, Yc1)
  3. 大数库可能封装了这一步:公钥为Q, h为余因子,S=[h]Q ,若S是无穷远点,则报错并退出;
  4. kQ = (x2, y2)
  5. t=KDF(x2 ∥ y2, nMsg), 若t为全0比特串,则报错并退出
  6. C2 = Msg xor t
  7. C3 = Hash(x2 || Msg || y2)
  8. Cipher = C1 || C2 || C3,C1压缩方式任意。

官网加解密示例使用了未压缩的转换方式。

解密流程

  1. 从密文中取出C1,验证是否为曲线上的点;
  2. 点S=[h]C1,若S是无穷远点,则报错并退出;
  3. 私钥为d,dC1 = (x2, y2)
  4. t=KDF(x2 ∥ y2, nMsg),这一步nMsg = nCipher - nC1 - nHash
  5. 取出C2, Msg = C2 xor t
  6. 验证u = Hash(x2 ∥ Msg ∥ y2) == C3,
  7. 明文为Msg

实现

https://github.com/C0deStarr/CryptoImp/tree/main/pubkey/ecc

  • sm2.h
  • sm2.c

参考资料

国家密码管理局关于发布《SM2椭圆曲线公钥密码算法》公告(国密局公告第21号)_国家密码管理局 (sca.gov.cn)

 类似资料: