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)

来源:千干

物联沃分享整理
物联沃-IOTWORD物联网 » Python:SM2

发表评论