嵌入式系统中的加解密签名(3)---国密的签名与验证

笔者来介绍一下RSA 和 SM2的原理和实现

a、RSA

原理以及数据格式介绍

RSA2048/3072/4096,非对称签名,hash算法一搬有hash256 、384和512,对称签名有:DES、AES128 和256等

RSA原理是利用大数难分解原理,正向容易,反向极难的核心思想。

  • RSA 涉及3个重要参数:n,e,d
  • 公钥(n,e),私钥(n,d),公钥公开,私钥保密
  • n = p*q,p和q为两个大素数,2048位就是256 B的n,
  • e满足如下规则:1< e <φ(n),φ(n) = (p-1) * (q-1),通常会选65537
  • d满足如下规则,e*d % φ(n) = 1,

公私钥裸数据格式,der格式如下:

下图可以看到n是256Byte大数,e为 01 00 01 ->65537

  • 私钥的格式如下:包括了公钥信息,

完整的结构如下:

  • 包括n/e/d/p/q等参数

TLV 格式说明

  • 加密原理:消息是m,m^e = C (mod n),C就是加密密文,====》n%(m^e)=KC,消息m是用户数据转成的大数字,然后参与运算。
  • 解密原理:c^d = m (mod n),解密出m消息 ====> n%(c^d)=Km

加密原理介绍

python算法

秘钥生成

python 复制代码
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization

# 生成私钥
private_key = rsa.generate_private_key(
    public_exponent=65537,
    key_size=2048,
    backend=default_backend()
)

# 生成公钥
public_key = private_key.public_key()

# 将私钥和公钥序列化为PEM格式
private_pem = private_key.private_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PrivateFormat.TraditionalOpenSSL,
    encryption_algorithm=serialization.NoEncryption()
)

public_pem = public_key.public_bytes(
    encoding=serialization.Encoding.PEM,
    format=serialization.PublicFormat.SubjectPublicKeyInfo
)

# 打印私钥和公钥
print(private_pem.decode('utf-8'))
print(public_pem.decode('utf-8'))

签名生成

python 复制代码
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

# 已经有了private_key和要签名的文件1.txt

# 读取文件内容
with open('1.txt', 'rb') as f:
    data = f.read()

# 使用私钥对文件进行签名
signature = private_key.sign(
    data,
    padding.PKCS1v15()
    hashes.SHA256()
)

# 将签名写入文件
with open('signature', 'wb') as f:
    f.write(signature)

签名校验

python 复制代码
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives import serialization


# 使用公钥验证签名
try:
    public_key.verify(
        signature,
        message,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    print("签名验证成功,消息未被篡改。")
except Exception as e:
    print(f"签名验证失败: {e}")

只是在知道公钥的情况下,也可以进行签名验证

python 复制代码
def rsa_verify_sign(public_type, public_key_str, sign_data, file_path):
    from cryptography.hazmat.primitives.asymmetric import padding
    from cryptography.hazmat.primitives import serialization,fronhashes
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import hashes
    # load public key
    public_key = serialization.load_pem_public_key(public_key_str.encode(), backend=default_backend())# use the public ky verify sign
    with open(file_path, "rb") as f:
        data = f.read()
    public_key.verify(sign_data, data, padding.PKcs1v15(), hashes.SHA256())

OpenSSL

shell 复制代码
# 生成2048位的RSA私钥
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048

# 从私钥中提取公钥
openssl rsa -pubout -in private_key.pem -out public_key.pem

# 使用私钥签名文件,生成签名文件
openssl dgst -sha256 -sign private_key.pem -out signature.bin example.txt

# 验证签名
openssl dgst -sha256 -verify public_key.pem -signature signature.bin example.txt

# 生成私钥的der格式,即二进制格式
openssl rsa -in private_key.pem -outform DER -out private_key.der

# 生成公钥的der格式,即二进制格式
openssl rsa -in public_key.pem -pubin -outform DER -out public_key.der

Openssl 支持的hash 、填充以及加密算法如下

b、SM2 国密算法

原理及数据格式介绍

SM2:非对称签名算法,SM3:hash算法,SM4:对称加密算法

SM2 原理介绍:

  • SM2是利用离散对数问题,建立在基于椭圆曲线的离散对数问题上的密码体制,给定椭圆曲线上的一个点G,并选取一个整数k,求解K=kG很容易(注意根据kG求解出来的K也是椭圆曲线上的一个点);反过来,在椭圆曲线上给定两个点K和G,若使K=kG,求整数k是一个难题。其就是建立在此数学难题之上,这一数学难题称为椭圆曲线离散对数问题。其中椭圆曲线上的点K则为公钥(注意公钥K不是一个整数而是一个椭圆曲线点),整数k则为私钥(实际上是一个大整数)。

  • 其公钥是椭圆曲线上面的一个坐标,为256位

  • 每个坐标32Byte,总计64Byte,实际中编码方式可能是ASN.1,所以其公钥长度必定大于64位,

  • 考虑如下等式:K=kG 其中 K,G为Ep(a,b)上的点,k为小于n(n是点G的阶)的整数,我们把点G称为基点(base point)。

  • 密码学中,描述一条Fp上的椭圆曲线,常用到六个参量:T=(p,a,b,n,x,y),(p 、a 、b) 用来确定一条椭圆曲线,p为素数域内点的个数,a和b是其内的两个大数;x,y为G基点的坐标,也是两个大数;n为点G基点的阶;以上六个量就可以描述一条椭圆曲线,有时候我们还会用到h(椭圆曲线上所有点的个数p与n相除的整数部分)

  • SM2默认的参数如下:

    SM2秘钥数据格式:

  • SM2私钥是一个大于等于1 且小于n1 的整数(n 为SM2 算法的阶, 其值参见 GM/T 0003) , 简记为k, 长度为256位

  • SM2公钥是SM2曲线上的一个点, 由横坐标和纵坐标两个分量来表示, 记为(x,y) , 简记为 Q, 每个分量的长度为256 位,总长度512位

  • SM2 算法私钥数据格式的 ASN.1定义为:

    SM2 Private Key : : = 整数

    SM2 算法公钥数据格式的 ASN.1 定义为:

    SM2 Public Key : : = bit string

    SM2 Public Key 为bit string 类型, 内容为04‖ X‖ Y, 其中,X 和 Y 分别标识公钥的x 分量和y

    分量, 其长度各为256 位。

具体例子如下:

  • 私钥:5c01d0cf8c243994138438c5965c4af5126592e78ce410bf0100bb4a04f2d032(256 BIT)
  • 公钥:04c5a147483701488f316614071ed6868861398aecd05560348d54fbdefbe82538486605039809C839088783609fC8853764024193C5690c14c1850828988 (528 BIT,开始两Byte固定:04)

签名的格式也是一个坐标:

  • R:整数第一部分
  • S:整数第二部分

还有可能是ASN.1 DER格式,需要解析:

解析流程如下:

python 复制代码
    def verify(self, Sign, data):
        # 验签函数,sign签名r||s,E消息hash,public_key公钥
        if self.asn1:
            unhex_sign = unhexlify(Sign.encode())
            seq_der = DerSequence()
            origin_sign = seq_der.decode(unhex_sign)
            r = origin_sign[0]
            s = origin_sign[1]
        else:
            r = int(Sign[0:self.para_len], 16)
            s = int(Sign[self.para_len:2*self.para_len], 16)

签名的时候也对应了相应的模式,

python 复制代码
    def sign(self, data, K):
        """
        签名函数, data消息的hash,private_key私钥,K随机数,均为16进制字符串
        :param self: 
        :param data: data消息的hash
        :param K: K随机数
        :return: 
        """
        E = data.hex()  # 消息转化为16进制字符串
        e = int(E, 16)

        d = int(self.private_key, 16)
        k = int(K, 16)

        P1 = self._kg(k, self.ecc_table['g'])

        x = int(P1[0:self.para_len], 16)
        R = ((e + x) % int(self.ecc_table['n'], base=16))
        if R == 0 or R + k == int(self.ecc_table['n'], base=16):
            return None
        d_1 = pow(
            d+1, int(self.ecc_table['n'], base=16) - 2, int(self.ecc_table['n'], base=16))
        S = (d_1*(k + R) - R) % int(self.ecc_table['n'], base=16)
        if S == 0:
            return None
        elif self.asn1:
            return DerSequence([DerInteger(R), DerInteger(S)]).encode().hex()
        else:
            return '%064x%064x' % (R, S)

Python算法

  • GMSSL的Python库里面有SM2的签名和验签函数

  • 签名公私钥都是pem str格式的

  • 里面没有对于SM2 ID的参数,不过可以自己新增接口

python 复制代码
def sm2_verify_sign(self, public_type, public_key_str, sign_data, file_path):
    from gmssl import sm2, sm3, func
    with open(file_path, "rb") as f:
        data = f.read()
    public_key = public_key_str[2:]
    # import asnlcrypto
    # from asnlcrypto.keys import PublickeyInfo
    # public_key = "".join(public_key_str.split("\n")[1:-2])
    # public_key_byte = base64.b64decode(public_key)
    # public_key_info = PublicKeyInfo.load(public_key_byte)
    # public_key_byte = public_key_info['public_key'].native
    # public_key = public_key_byte.hex()
    sm2_crypt = sm2.CryptSM2(private_key="", public_key=public_key, asnl=True)
    if public_type == "customer":
        result = sm2_crypt.verify(sign_data, data)
    else:
        pass
    
def sm2_sign(self, private_key, public_key, message_bytes):
    from gmssl import sm2, sm3, func
    sm2_signer = sm2.CryptSM2(private_key=private_key, public_key=public_key, asnl=True)
    random_hex_str ='fa0ff179b5c2397262d393365cc3e43aef6646c7e48e31de05af2b2e1fde9a06'
    signature = sm2_signer.sign_with_sm3(message_bytes, random_hex_str=random_hex_str)
    signature = bytes.fromhex(signature)

新增SM2 ID的签名算法,这是Python GMSSL库的算法

  • 默认id 是:31323334353637383132333435363738
  • 所以这个可以作为参数
python 复制代码
    def _sm3_z(self, data):
        """
        SM3WITHSM2 签名规则:  SM2.sign(SM3(Z+MSG),PrivateKey)
        其中: z = Hash256(Len(ID) + ID + a + b + xG + yG + xA + yA)
        """
        # sm3withsm2 的 z 值
        z = '0080'+'31323334353637383132333435363738' + \
            self.ecc_table['a'] + self.ecc_table['b'] + self.ecc_table['g'] + \
            self.public_key
        z = binascii.a2b_hex(z)
        Za = sm3.sm3_hash(func.bytes_to_list(z))
        M_ = (Za + data.hex()).encode('utf-8')
        e = sm3.sm3_hash(func.bytes_to_list(binascii.a2b_hex(M_)))
        return e

GMSSL

国密的官方网站http://gmssl.org

Linux下使用的更新的GLIBC编译的,所以现在环境部支持,无法运行。需要在linux下面重新编译

linux编译,进入build下面,

复制代码
cmake ..

接着进行make编译,最后会生成gmssl 可执行程序

复制代码
make

到这里及生成完成

输入./bin/gmssl ,可以运行国密相关的操作。

输入 make test,可以执行case 验证

相关推荐
海的透彻4 天前
jmeter预制处理器JSR223-加解密
开发语言·jmeter·sm2·jsr233
诸葛老刘10 天前
国密python调java服务
java·python·国密·sm2
恼书:-(空寄10 天前
深入理解敏感信息加密:AES+RSA混合使用前后端对接
aes·数据加密·rsa
Asurplus16 天前
【VUE】17、使用JSEncrypt对数据加解密
javascript·vue.js·jsencrypt·rsa
九流下半1 个月前
OpenHarmony签名指南:自动与手动详解
签名·openharmony·系统信息·系统应用签名
aovenus2 个月前
RSA 算法介绍
rsa
酿情师2 个月前
2026软件系统安全赛初赛RSA(赛后复盘)
android·网络·安全·密码学·rsa
温中志2 个月前
RSA加密算法入门
rsa·计算机密码学
Y5neKO2 个月前
某国赛CTF密码学题目Writeup:RSA
密码学·ctf·rsa