Python:SM2

SM2介绍

SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法。
SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,在我们国家商用密码体系中被用来替换RSA算法。
随着密码技术和计算机技术的发展,目前常用的1024位RSA算法面临严重的安全威胁,我们国家密码管理部门经过研究,决定采用SM2椭圆曲线算法替换RSA算法。
相比于RSA,SM2性能更优更安全:密码复杂度高、处理速度快、机器性能消耗更小

算法流程

国家密码管理局-SM2椭圆曲线公钥密码算法
在上面的官方文件中详细描述了各个函数及其实现,包括函数实现过程中的数据类型转换(第1部分4.2 P5~7)
本文的算法为其中的素域下的公钥加密算法(第4部分:80 / 93)
加解密算法流程如图:

输入输出及部分函数和参数说明

1、加密

输入: 任意不含中文的字符串,本文采用了读文件的形式(可改),输入中若含有中文则会在解密后被转换成\x的16进制,最后验证也会False(听说可以decode后encode,但我试过好像没用
输出: 16进制字符串

2、解密

输入: 16进制字符串(三个)和椭圆曲线的参数
输出: 你输入的不明字符串

3、部分函数说明

addition(x1,y1,x2,y2,a,p):椭圆曲线E上的点在素域下的加法运算(第1部分P3,9 / 93),运算的时候不涉及椭圆曲线中的参数b所以就没加
mutipoint(x,y,k,a,p): 椭圆曲线E上的点在素域下的倍点运算(第1部分P22,28 / 93)
kdf(z,klen): 密钥派生函数(第4部分P4,87 / 93)

参数说明

椭圆曲线参数(a,b,p)和基点G及其阶n可改,官方推荐参数

源代码

运行前说明

运行环境:python3
Look 第3、4行的引用
第3行是之前的prime.py,实际上就一个模逆运算
第4行是SM3密码杂凑函数(SM3哈希函数)
所以光运行下面的代码肯定是会报错的
还有就是那些注释,是我验证的时候ctrl+c上去的,为啥不删?问就是懒
技术有限,仅供参考

真·代码

from random import randint
import math
from prime import modinv
from sm3 import sm3hash

def addition(x1,y1,x2,y2,a,p):
    if x1==x2 and y1==p-y2:
        return False
    if x1!=x2:
        lamda=((y2-y1)*modinv(x2-x1, p))%p
    else:
        lamda=(((3*x1*x1+a)%p)*modinv(2*y1, p))%p
    x3=(lamda*lamda-x1-x2)%p
    y3=(lamda*(x1-x3)-y1)%p
    return x3,y3

def mutipoint(x,y,k,a,p):
    k=bin(k)[2:]
    qx,qy=x,y
    for i in range(1,len(k)):
        qx,qy=addition(qx, qy, qx, qy, a, p)
        if k[i]=='1':
            qx,qy=addition(qx, qy, x, y, a, p)
    return qx,qy

def kdf(z,klen):
    ct=1
    k=''
    for _ in range(math.ceil(klen/256)):
        k=k+sm3hash(hex(int(z+'{:032b}'.format(ct),2))[2:])
        ct=ct+1
    k='0'*((256-(len(bin(int(k,16))[2:])%256))%256)+bin(int(k,16))[2:]
    return k[:klen]

#parameters
p=0x8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3
a=0x787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498
b=0x63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A
gx=0x421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D
gy=0x0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2
n=0x8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7
#待加密的消息M:encryption standard
#消息M的16进制表示:656E63 72797074 696F6E20 7374616E 64617264
'''
dB=0x1649AB77A00637BD5E2EFE283FBF353534AA7F7CB89463F208DDBC2920BB0DA0
xB=0x435B39CCA8F3B508C1488AFC67BE491A0F7BA07E581A0E4849A5CF70628A7E0A
yB=0x75DDBA78F15FEECB4C7895E2C1CDF5FE01DEBB2CDBADF45399CCF77BBA076A42
'''
dB=randint(1,n-1)
xB,yB=mutipoint(gx,gy,dB,a,p)


def encrypt(m:str):
    plen=len(hex(p)[2:])
    m='0'*((4-(len(bin(int(m.encode().hex(),16))[2:])%4))%4)+bin(int(m.encode().hex(),16))[2:]
    klen=len(m)
    while True:
        k=randint(1, n)
        while k==dB:
            k=randint(1, n)
        x2,y2=mutipoint(xB, yB, k, a, p)
        x2,y2='{:0256b}'.format(x2),'{:0256b}'.format(y2)
        t=kdf(x2+y2, klen)
        if int(t,2)!=0:
            break
    x1,y1=mutipoint(gx, gy, k, a, p)
    x1,y1=(plen-len(hex(x1)[2:]))*'0'+hex(x1)[2:],(plen-len(hex(y1)[2:]))*'0'+hex(y1)[2:]
    c1='04'+x1+y1
    c2=((klen//4)-len(hex(int(m,2)^int(t,2))[2:]))*'0'+hex(int(m,2)^int(t,2))[2:]
    c3=sm3hash(hex(int(x2+m+y2,2))[2:])
    return c1,c2,c3

def decrypt(c1,c2,c3,a,b,p):
    c1=c1[2:]
    x1,y1=int(c1[:len(c1)//2],16),int(c1[len(c1)//2:],16)
    if pow(y1,2,p)!=(pow(x1,3,p)+a*x1+b)%p:
        return False
    x2,y2=mutipoint(x1, y1, dB, a, p)
    x2,y2='{:0256b}'.format(x2),'{:0256b}'.format(y2)
    klen=len(c2)*4
    t=kdf(x2+y2, klen)
    if int(t,2)==0:
        return False
    m='0'*(klen-len(bin(int(c2,16)^int(t,2))[2:]))+bin(int(c2,16)^int(t,2))[2:]
    u=sm3hash(hex(int(x2+m+y2,2))[2:])
    if u!=c3:
        return False
    return hex(int(m,2))[2:]

f=open('test.txt','r')
fstr=f.read()
f.close()
print(fstr)
c1,c2,c3=encrypt(fstr)
c=(c1+c2+c3).upper()
print('\nciphertext:')
for i in range(len(c)):
    print(c[i*8:(i+1)*8],end=' ')
print('\n\nplaintext:')
'''
输出密文C= C1∥C2∥C3:
04245C26 FB68B1DD DDB12C4B 6BF9F2B6 D5FE60A3 83B0D18D 1C4144AB F17F6252
E776CB92 64C2A7E8 8E52B199 03FDC473 78F605E3 6811F5C0 7423A24B 84400F01
B8650053 A89B41C4 18B0C3AA D00D886C 00286467 9C3D7360 C30156FA B7C80A02
76712DA9 D8094A63 4B766D3A 285E0748 0653426D
'''
m1=decrypt(c1, c2, c3, a, b, p)
if m1:
    m1=str(bytes.fromhex(m1))
    m1='\n'.join(m1[2:-1].split('\\n'))
    print(m1)
    print(fstr==m1)
else:
    print(False)

补充说明(代码缺失部分

  1. 无穷远点的判定: 需要在适当处(具体参考官方文档、算法流程)加入对无穷远点的判断,具体是否为无穷远点,可以用其性质判定(若S为无穷远点,则对于椭圆曲线上的任意一点P(可用基点G),有 S+P==P;或S+S==S+ 为椭圆曲线上的点加运算)
  2. 部分传参修改:
  • 加密函数的返回值: 最后应只返回一个连续密文 C C C C = C 1 + C 2 + C 3 C=C_1+C_2+C_3 C=C1+C2+C3,而不应为三部分
  • 解密函数的修改: 对应只传入密文 C C C ,而后需要 C 1 , C 2 , C 3 C_1,C_2,C_3 C1C2C3 C C C中识别拆分出来, l e n ( C 1 ) = 2 l + 1 len(C_1)=2l+1 len(C1)=2l+1 l = ⌈ ( log ⁡ 2 p ) / 8 ⌉ l=\lceil(\log_2 p)/8\rceil l=⌈(log2p)/8(文档P13 / 93),其他你懂的
  1. 公钥的有效性验证: 文档P15 / 93,共四点

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值