SM2公钥加密

简介

国密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)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SM2公钥加密算法是一种基于椭圆曲线密码的公钥加密算法,由国密局发布。KDF(Key Derivation Function)是密钥派生函数,用于从明文或其他输入数据中生成密钥。在SM2算法中,KDF使用的是PBKDF2(Password-Based Key Derivation Function 2)算法。 SM2公钥加密算法的工作流程如下: 1. 选择一条椭圆曲线和基点G(一个大素数)。 2. 计算私钥d(一对大素数)。 3. 计算公钥Q = d * G。 4. 使用KDF(如PBKDF2)从密码和其他输入数据(如盐值salt)中派生出一个密钥。 5. 使用派生的密钥对消息进行加密。 以下是使用Python的cryptography库实现SM2公钥加密算法的示例代码: ```python from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import ec from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC from cryptography.hazmat.backends import default_backend import os # 生成SM2密钥对 private_key = ec.generate_private_key(ec.SECP256R1(), default_backend()) public_key = private_key.public_key() # 定义密码和盐值 password = b"my_password" salt = os.urandom(16) # 使用PBKDF2派生密钥 kdf = PBKDF2HMAC( algorithm=hashes.SHA256(), length=32, salt=salt, iterations=100000, backend=default_backend() ) derived_key = kdf.derive(password) # 使用派生的密钥加密消息 message = b"Hello, SM2!" ciphertext = public_key.encrypt(message, derived_key, None) ``` 注意:在实际应用中,为了安全起见,建议使用硬件安全模块(HSM)或可信的第三方服务来生成和存储密钥。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值