现代密码学上机作业

实现RSA算法

实验摘要

利用Python实现RSA的公钥加密、私钥解密过程,运用了扩展欧几里得算法求乘法逆元。RSA利用了大素数分解的数学困难问题,同时用于加密和数字签名的算法。

实验过程

RSA加密过程:通过选取的随机大素数p和q计算n和n的欧拉函数f_n,选取e作为加密时的指数,将明文m转换成数字计算m**e(mod n)转换成字母得密文c。

RSA解密过程:计算e在模f_n下的乘法逆元d,将密文c转换成数字计算c**d(mod n)转换成字母得明文m。

代码

Python3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import base64
class RSA(object):
    def __init__(self):
        self.n = 0
        self.f_n = 0
        self.e = 0
        self.display_list = []
        self.display_str = ''
        
    def work(self):
        p = 2269
        q = 1427
        self.n = p * q
        self.f_n =(p - 1) * (q - 1)
        self.e = 4271
        # 4271
        while True:
            func_choice = int(input('加密请输入1,解密请输入2:'))
            if func_choice == 1:
                self.display_str = input('求输入想进行操作的字符串:')
                self.encrypt()
            elif func_choice == 2:
                self.display_list = input('求输入密文,用英文逗号隔开,不需要输入中括号:').replace(' ', '').split(',')
                self.decrypt()
            else:
                print('你输入了错误的选项!')
    def encrypt(self):
        self.display_list = []
        str_b64 = base64.b64encode(bytes(self.display_str, encoding='utf-8'))
        list_b64 = list(str_b64)
        for i in list_b64:
            self.display_list.append((i ** self.e) % self.n)
        print('密文为:', self.display_list)
        
    def decrypt(self):
        self.display_str = ''
        temp_list = []
        d = self.createPrivateKey()
        for i in self.display_list:
            temp_list.append(chr((int(i) ** d) % self.n))
        self.display_str = base64.b64decode(''.join(temp_list)).decode()
        print('明文为:', self.display_str)
        
    def createPrivateKey(self):
        # 产生私钥d
        temp = self.egcd(self.e, self.f_n)[1]
        while temp < 0:
            temp += self.f_n
        return temp
    
    def egcd(self, a, b):
        # 扩展欧几里得算法
        if b == 0:
            return a, 1, 0
        else:
            g, x, y = self.egcd(b, a % b)
            return g, y, x - a // b * y
        
        
if __name__ == '__main__':
    rsa = RSA()
    rsa.work()

image-20200401190010113

C

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <stdio.h>
#include <math.h>

int main(void)
{
    int p, q, n, n2, e, d, c, m, ch;
    printf("请输入两个素数:\n");
    scanf("%d%d", &p, &q);
    n = p * q;
    while (judege(p) + judege(q)) //不是素数 则报错
    {
        printf("错误!输入两个数不是素数");
        return 0;
    }
    n2 = (p - 1) * (q - 1);
    printf("请选择e,使得e与%d互素且小于%d且大于1:\n", n2, n2);
    scanf("%d", &e);
    d = cald(e, n2);
    printf("根据e=%d计算d=%d\n", e, d);
    printf("请选择1.加密 2.解密\n");
    scanf("%d", &ch);
    switch (ch)
    {
    case 1:
        printf("请输入你要加密的明文数字:\n");
        scanf("%d", &m);
        c = en(m, e, n);
        printf("得到的密文:%d\n", c);
        break;
    case 2:
        printf("请输入你要解密的密文数字:\n");
        scanf("%d", &m);
        c = dn(m, d, n);
        printf("得到的明文:%d", c);
        break;
    }
}
int judege(int a) //判断输入的两个数是否是素数
{
    int i = 2, j = 0;
    for (; i < a; i++)
    {
        if (a % i == 0)
            j = j + 1;
    }
    if (j >= 1)
        return 1;
    else
        return 0;
}
int cald(int b, int c) //计算d
{
    int i = 1;
    for (; i < c; i++)
    {

        if ((i * b) % c == 1)
            break;
    }
    return i;
}
int en(int a, int b, int c) //加密
{
    int r, i;
    r = a;
    for (i = 1; i < b; i++)
    {
        r = r * a;
        r = r % c;
    }
    return r;
}
int dn(int a, int b, int c) //解密
{
    int r, i;
    r = a;
    for (i = 1; i < b; i++)
    {
        r = r * a;
        r = r % c;
    }
    return r;
}

image-20200401190044716

image-20200401190051719

实验总结

本程序实现完整RSA功能,注意问题及解决方案为以下几点:

  1. 文字和数字之间的转换,因设计数学运算和文字阅读
  2. 模指数运算来加快运算速度
  3. 素数的选择不再算法本身要考虑的范围内

实现SM2加密算法。给出“we are student”的加解密变换结果

实验摘要

根据国密推荐的SM2椭圆曲线公钥密码算法,首先产生随机数计算出曲线点C1,2个32byte的BigInteger大数,即为SM2加密结果的第1部分。第2部分则是真正的密文,是对明文的加密结果,长度和明文一样。第3部分是杂凑值,用来效验数据。按国密推荐的256位椭圆曲线,明文加密结果比原长度会大96byte。

代码

Python3

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
from random import choice
import SM3

sm2_N = int('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123', 16)
sm2_P = int('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF', 16)
sm2_G = '32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7bc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0'  # G点
sm2_a = int('FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC',16)
sm2_b = int('28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93',16)
sm2_a_3 = (sm2_a + 3) % sm2_P
Fp = 256


# sm2_N = int('BDB6F4FE3E8B1D9E0DA8C0D40FC962195DFAE76F56564677', 16)
# sm2_P = int('BDB6F4FE3E8B1D9E0DA8C0D46F4C318CEFE4AFE3B6B8551F', 16)
# sm2_G = '4AD5F7048DE709AD51236DE65E4D4B482C836DC6E410664002BB3A02D4AAADACAE24817A4CA3A1B014B5270432DB27D2'# G点
# sm2_a = int('BB8E5E8FBC115E139FE6A814FE48AAA6F0ADA1AA5DF91985',16)
# sm2_b = int('1854BEBDC31B21B7AEFC80AB0ECD10D5B1B3308E6DBF11C1',16)
# sm2_a_3 = (sm2_a + 3) % sm2_P
# Fp = 192

def get_random_str(strlen):
    letterlist = ['0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f']
    str = ''
    for i in range(strlen):
        a = choice(letterlist)
        str = '%s%s' % (str,a)
    return str


def kG(k, Point,len_para):  # kP运算
    Point = '%s%s' % (Point, '1')
    mask_str = '8'
    for i in range(len_para-1):
        mask_str += '0'
    # print(mask_str)
    mask = int(mask_str, 16)
    Temp = Point
    flag = False
    for n in range(len_para * 4):
        if (flag):
            Temp = DoublePoint(Temp,len_para)
        if (k & mask) != 0:
            if (flag):
                Temp = AddPoint(Temp, Point,len_para)
            else:
                flag = True
                Temp = Point
        k = k << 1
    return ConvertJacb2Nor(Temp,len_para)


# 倍点运算
def DoublePoint(Point,len_para):
    l = len(Point)
    len_2 = 2 * len_para
    if l< len_para*2:
        return None
    else:
        x1 = int(Point[0:len_para], 16)
        y1 = int(Point[len_para:len_2], 16)
        if l == len_2:
            z1 = 1
        else:
            z1 = int(Point[len_2:], 16)
        T6 = (z1 * z1) % sm2_P
        T2 = (y1 * y1) % sm2_P
        T3 = (x1 + T6) % sm2_P
        T4 = (x1 - T6) % sm2_P
        T1 = (T3 * T4) % sm2_P
        T3 = (y1 * z1) % sm2_P
        T4 = (T2 * 8) % sm2_P
        T5 = (x1 * T4) % sm2_P
        T1 = (T1 * 3) % sm2_P
        T6 = (T6 * T6) % sm2_P
        T6 = (sm2_a_3 * T6) % sm2_P
        T1 = (T1 + T6) % sm2_P
        z3 = (T3 + T3) % sm2_P
        T3 = (T1 * T1) % sm2_P
        T2 = (T2 * T4) % sm2_P
        x3 = (T3 - T5) % sm2_P

        if (T5 % 2) == 1:
            T4 = (T5 + ((T5 + sm2_P) >> 1) - T3) % sm2_P
        else:
            T4 = (T5 + (T5 >> 1) - T3) % sm2_P

        T1 = (T1 * T4) % sm2_P
        y3 = (T1 - T2) % sm2_P

        form = '%%0%dx' % len_para
        form = form * 3
        return form % (x3, y3, z3)


# 点加函数,P2点为仿射坐标即z=1,P1为Jacobian加重射影坐标
def AddPoint(P1, P2, len_para):
    len_2 = 2 * len_para
    l1 = len(P1)
    l2 = len(P2)
    if (l1 < len_2) or (l2 < len_2):
        return None
    else:
        X1 = int(P1[0: len_para], 16)
        Y1 = int(P1[len_para: len_2], 16)
        if (l1 == len_2):
            Z1 = 1
        else:
            Z1 = int(P1[len_2:], 16)
        x2 = int(P2[0:len_para], 16)
        y2 = int(P2[len_para:len_2], 16)

        T1 = (Z1 * Z1) % sm2_P
        T2 = (y2 * Z1) % sm2_P
        T3 = (x2 * T1) % sm2_P
        T1 = (T1 * T2) % sm2_P
        T2 = (T3 - X1) % sm2_P
        T3 = (T3 + X1) % sm2_P
        T4 = (T2 * T2) % sm2_P
        T1 = (T1 - Y1) % sm2_P
        Z3 = (Z1 * T2) % sm2_P
        T2 = (T2 * T4) % sm2_P
        T3 = (T3 * T4) % sm2_P
        T5 = (T1 * T1) % sm2_P
        T4 = (X1 * T4) % sm2_P
        X3 = (T5 - T3) % sm2_P
        T2 = (Y1 * T2) % sm2_P
        T3 = (T4 - X3) % sm2_P
        T1 = (T1 * T3) % sm2_P
        Y3 = (T1 - T2) % sm2_P

        form = '%%0%dx' % len_para
        form = form * 3
        return form % (X3, Y3, Z3)

def ConvertJacb2Nor(Point,len_para): # Jacobian加重射影坐标转换成仿射坐标
    len_2 = 2 * len_para
    x = int(Point[0:len_para], 16)
    y = int(Point[len_para:len_2], 16)
    z = int(Point[len_2:], 16)
    # z_inv = Inverse(z, P)
    z_inv = pow(z, sm2_P - 2, sm2_P)
    z_invSquar = (z_inv * z_inv) % sm2_P
    z_invQube = (z_invSquar * z_inv) % sm2_P
    x_new = (x * z_invSquar) % sm2_P
    y_new = (y * z_invQube) % sm2_P
    z_new = (z * z_inv) % sm2_P
    if z_new == 1:
        form = '%%0%dx' % len_para
        form = form * 2
        return form % (x_new, y_new)
    else:
        return 'Convert Error'


# 可逆,可用pow()代替
def Inverse(data, M,len_para):
    tempM = M - 2
    mask_str = '8'
    for i in range(len_para - 1):
        mask_str += '0'
    mask = int(mask_str, 16)
    tempA = 1
    tempB = data

    for i in range(len_para*4):
        tempA = (tempA * tempA) % M
        if (tempM & mask) != 0:
            tempA = (tempA * tempB) % M
        mask = mask >> 1

    return tempA


# 验签函数,Sign签名r||s,E消息hash,PA公钥
def Verify(Sign, E, PA,len_para):
    r = int(Sign[0:len_para], 16)
    s = int(Sign[len_para:2*len_para], 16)
    e = int(E, 16)
    t = (r + s) % sm2_N
    P1 = kG(s, sm2_G,len_para)
    P2 = kG(t, PA,len_para)

    if P1 == P2:
        P1 = '%s%s' % (P1, 1)
        P1 = DoublePoint(P1,len_para)
    else:
        P1 = '%s%s' % (P1, 1)
        P1 = AddPoint(P1, P2,len_para)
        P1 = ConvertJacb2Nor(P1,len_para)

    x = int(P1[0:len_para], 16)
    return (r == ((e + x) % sm2_N))


# 签名函数, E消息的hash,DA私钥,K随机数,均为16进制字符串
def Sign(E, DA, K,len_para):
    e = int(E, 16)
    d = int(DA, 16)
    k = int(K, 16)

    P1 = kG(k, sm2_G,len_para)

    x = int(P1[0:len_para], 16)
    R = ((e + x) % sm2_N)
    d_1 = pow(d+1, sm2_N - 2, sm2_N)
    S = (d_1*(k + R) - R) % sm2_N
    return '%064x%064x' % (R,S)


# 加密函数,M消息,PA公钥
def Encrypt(M,PA,len_para,Hexstr = 0):
    if Hexstr:
        msg = M # 输入消息本身是16进制字符串
    else:
        msg = SM3.str2byte(M)
        msg = SM3.byte2hex(msg) # 消息转化为16进制字符串
    k = get_random_str(len_para)
    # k = '59276E27D506861A16680F3AD9C02DCCEF3CC1FA3CDBE4CE6D54B80DEAC1BC21'
    # k = '384F30353073AEECE7A1654330A96204D37982A3E15B2CB5'
    C1 = kG(int(k, 16),sm2_G, len_para)
    # print('C1 = %s'%C1)
    xy = kG(int(k, 16), PA, len_para)
    # print('xy = %s' % xy)
    x2 = xy[0:len_para]
    y2 = xy[len_para:2 * len_para]
    ml = len(msg)
    # print('ml = %d'% ml)
    t = SM3.KDF(xy,ml / 2)
    # print(t)
    if int(t,16) == 0:
        return None
    else:
        form = '%%0%dx' % ml
        C2 = form % (int(msg,16) ^ int(t,16))
        # print('C2 = %s'% C2)
        # print('%s%s%s'% (x2,msg,y2))
        C3 = SM3.Hash_sm3('%s%s%s'% (x2,msg,y2),1)
        # print('C3 = %s' % C3)
        return '%s%s%s' % (C1,C3,C2)


# 解密函数,C密文(16进制字符串),DA私钥
def Decrypt(C, DA, len_para):
    len_2 = 2 * len_para
    len_3 = len_2 + 64
    C1 = C[0:len_2]
    C3 = C[len_2:len_3]
    C2 = C[len_3:]
    xy = kG(int(DA,16), C1, len_para)
    # print('xy = %s' % xy)
    x2 = xy[0:len_para]
    y2 = xy[len_para:len_2]
    cl = len(C2)
    # print(cl)
    t = SM3.KDF(xy, cl/2)
    # print('t = %s'%t)
    if int(t,16) == 0:
        return None
    else:
        form = '%%0%dx' % cl
        M = form % (int(C2,16) ^ int(t,16))
        # print('M = %s' % M)
        u = SM3.Hash_sm3('%s%s%s'% (x2,M,y2),1)
        if  (u == C3):
            return M
        else:
            return None


if __name__ == '__main__':
    len_para = int(Fp / 4)
    # print(len_para)
    # e = get_random_str(len_para)
    e = SM3.byte2hex(SM3.str2byte('we are student'))
    d = get_random_str(len_para)
    k = get_random_str(len_para)
    # e = '656E6372797074696F6E207374616E64617264'
    # d = '3945208F7B2144B13F36E38AC6D39F95889393692860B51A42FB81EF4DF7C5B8'
    # d = '58892B807074F53FBF67288A1DFAA1AC313455FE60355AFD'
    Pa = kG(int(d, 16), sm2_G, len_para)
    Sig = Sign(e, d, k, len_para)
    print(Verify(Sig, e, Pa, len_para))
    # print(Pa)
    print('M = %s' % e)
    C = Encrypt(e, Pa, len_para, 1)
    print('C = %s' % C)
    print('Decrypt')
    # print(Decrypt(C, d, len_para))
    print(Decrypt(C, d, len_para))

image-20200401190116841

实验总结

SM2加密算法在Java中同样也可以基于使用BouncyCastle库实现。一般使用数字证书来标识身份,同时使用证书中公钥加密数据。SM2中有随机数的参与,所以每次即使相同的明文也会加密成不同的密文。

参考文献

SM2非对称算法加解密

实现SM2签名算法。给出“we are student”的签名。

实验摘要

SM3数字签名部分和加解密部分相似

代码

此部分代码包含在上一个代码里,在此仅把关键部分摘出

Python3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 验签函数,Sign签名r||s,E消息hash,PA公钥
def Verify(Sign, E, PA,len_para):
    r = int(Sign[0:len_para], 16)
    s = int(Sign[len_para:2*len_para], 16)
    e = int(E, 16)
    t = (r + s) % sm2_N
    P1 = kG(s, sm2_G,len_para)
    P2 = kG(t, PA,len_para)

    if P1 == P2:
        P1 = '%s%s' % (P1, 1)
        P1 = DoublePoint(P1,len_para)
    else:
        P1 = '%s%s' % (P1, 1)
        P1 = AddPoint(P1, P2,len_para)
        P1 = ConvertJacb2Nor(P1,len_para)

    x = int(P1[0:len_para], 16)
    return (r == ((e + x) % sm2_N))


# 签名函数, E消息的hash,DA私钥,K随机数,均为16进制字符串
def Sign(E, DA, K,len_para):
    e = int(E, 16)
    d = int(DA, 16)
    k = int(K, 16)

    P1 = kG(k, sm2_G,len_para)

    x = int(P1[0:len_para], 16)
    R = ((e + x) % sm2_N)
    d_1 = pow(d+1, sm2_N - 2, sm2_N)
    S = (d_1*(k + R) - R) % sm2_N
    return '%064x%064x' % (R,S)

image-20200401190353179

实验总结

对于要签名的信息e,这个是原始信息经过一定的处理通过散列函数得到的,散列算法用的是国密SM3算法

参考文献:

  1. 国密SM3算法及基于SM3的密钥派生函数KDF
  2. python3实现的国密SM2

实现SM3算法

实验摘要

SM3主要部分包括:所需函数—布尔函数和置换函数,和参数(IV、T)。SM3对数据进行扩展和填充,然后迭代压缩生成Hash值。

代码

Python3

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
from math import ceil


# 初始值IV
IV = '7380166f 4914b2b9 172442d7 da8a0600 a96f30bc 163138aa e38dee4d b0fb0e4e'
IV = int(IV.replace(' ', ''), 16)
# IV = 52242288850037289314637692563605787808083148960692295188198367823108413001294
# 接下来挨个取出来IV
a = []
for i in range(0, 8):
   a.append(0)
   a[i] = (IV >> ((7 - i) * 32)) & 0xFFFFFFFF
IV = a


# 打印输出
def out_hex(list1):
   for i in list1:
       print("%08x" % i)
   print("\n")


# 循环左移
def rotate_left(a, k):
   k = k % 32
   return ((a << k) & 0xFFFFFFFF) | ((a & 0xFFFFFFFF) >> (32 - k))


# 常量T
T_j = []
# 0<= j <= 15
for i in range(0, 16):
   T_j.append(0)
   T_j[i] = 0x79cc4519
# 16<= j <= 63
for i in range(16, 64):
   T_j.append(0)
   T_j[i] = 0x7a879d8a


# 布尔函数FF_J,起混淆作用
def FF_j(X, Y, Z, j):
   if 0 <= j < 16:
       ret = X ^ Y ^ Z
   elif 16 <= j < 64:
       ret = (X & Y) | (X & Z) | (Y & Z)
   return ret


# 布尔函数GG_J,起混淆作用
def GG_j(X, Y, Z, j):
   if 0 <= j < 16:
       ret = X ^ Y ^ Z
   elif 16 <= j < 64:
       # ret = (X | Y) & ((2 ** 32 - 1 - X) | Z)
       ret = (X & Y) | ((~ X) & Z)
   return ret


# 置换函数P_0,起扩散作用
def P_0(X):
   return X ^ (rotate_left(X, 9)) ^ (rotate_left(X, 17))


# 置换函数P_1,起扩散作用
def P_1(X):
   return X ^ (rotate_left(X, 15)) ^ (rotate_left(X, 23))


# 压缩函数CF
def CF(V_i, B_i):
   # 将消息分组按以下方法扩展生成132个字W_0,W_1...W_67,W_0',W_1'...W_63'
   # 将消息分组划分为16个字W_0,W_1...W_15
   W = []
   for i in range(16):
       weight = 0x1000000
       data = 0
       for k in range(i * 4,(i + 1) * 4):
           data = data + B_i[k] * weight
           weight = int(weight / 0x100)
       W.append(data)

   for j in range(16, 68):
       W.append(0)
       W[j] = P_1(W[j - 16] ^ W[j-9] ^ (rotate_left(W[j - 3], 15))) ^ (rotate_left(W[j - 13], 7)) ^ W[j - 6]
       # str1 = "%08x" % W[j]

   # W_j' = W_j ^ W_j+4
   W_1 = []
   for j in range(0, 64):
       W_1.append(0)
       W_1[j] = W[j] ^ W[j+4]
       # str1 = "%08x" % W_1[j]

   A, B, C, D, E, F, G, H = V_i
   """
   print "00",
   out_hex([A, B, C, D, E, F, G, H])
   """
   # 压缩函数,令ABCDEFGH为字寄存器,SS1,SS2,TT1,TT2为中间变量,压缩函数V_i+1 = CF(V_(i), B_(i))
   for j in range(0, 64):
       SS1 = rotate_left(((rotate_left(A, 12)) + E + (rotate_left(T_j[j], j))) & 0xFFFFFFFF, 7)
       SS2 = SS1 ^ (rotate_left(A, 12))
       TT1 = (FF_j(A, B, C, j) + D + SS2 + W_1[j]) & 0xFFFFFFFF
       TT2 = (GG_j(E, F, G, j) + H + SS1 + W[j]) & 0xFFFFFFFF
       D = C
       C = rotate_left(B, 9)
       B = A
       A = TT1
       H = G
       G = rotate_left(F, 19)
       F = E
       E = P_0(TT2)

       A = A & 0xFFFFFFFF
       B = B & 0xFFFFFFFF
       C = C & 0xFFFFFFFF
       D = D & 0xFFFFFFFF
       E = E & 0xFFFFFFFF
       F = F & 0xFFFFFFFF
       G = G & 0xFFFFFFFF
       H = H & 0xFFFFFFFF
       """
       str1 = "%02d" % j
       if str1[0] == "0":
           str1 = ' ' + str1[1:]
       print str1,
       out_hex([A, B, C, D, E, F, G, H])
       """

   V_i_1 = []
   V_i_1.append(A ^ V_i[0])
   V_i_1.append(B ^ V_i[1])
   V_i_1.append(C ^ V_i[2])
   V_i_1.append(D ^ V_i[3])
   V_i_1.append(E ^ V_i[4])
   V_i_1.append(F ^ V_i[5])
   V_i_1.append(G ^ V_i[6])
   V_i_1.append(H ^ V_i[7])
   return V_i_1


def hash_msg(msg):
   # 将消息填充为512比特(64字节)的整数倍
   len1 = len(msg)
   reserve1 = len1 % 64
   # 先添加一个1bit
   msg.append(0x80)
   reserve1 = reserve1 + 1
   # 56-64, add 64 byte
   # 即如果目前长度占用最后8个byte的表示长度的位置,则另外再增加64byte
   range_end = 56
   if reserve1 > range_end:
       range_end = range_end + 64
   # 从新增的1到倒数第65bit全部填充0
   for i in range(reserve1, range_end):
       msg.append(0x00)

   bit_length = (len1) * 8
   bit_length_str = [bit_length % 0x100]
   for i in range(7):
       bit_length = int(bit_length / 0x100)
       bit_length_str.append(bit_length % 0x100)
   for i in range(8):
       msg.append(bit_length_str[7 - i])

   # group_count = round(len(msg) / 64)
   # 组数
   group_count = len(msg) // 64

   B = []
   for i in range(0, group_count):
       B.append(msg[i * 64:(i + 1) * 64])

   V = []
   V.append(IV)
   for i in range(0, group_count):
       V.append(CF(V[i], B[i]))

   y = V[i+1]
   result = ''
   for i in y:
       result = '%s%08x' % (result, i)
   # 最后的结果
   return result


# 字符串转换成byte数组
def str2byte(msg):
   ml = len(msg)
   msg_byte = []
   msg_bytearray = msg.encode('utf-8')
   for i in range(ml):
       # msg_byte.append(ord(msg[i]))
       msg_byte.append(msg_bytearray[i])
   return msg_byte


# 16进制字符串转换成byte数组
def hex2byte(msg):
   ml = len(msg)
   if ml % 2 != 0:
       msg = '0' + msg
   ml = int(len(msg) / 2)
   msg_byte = []
   for i in range(ml):
       # print(msg[i*2:i*2+2])
       msg_byte.append(int(msg[i * 2:i * 2 + 2], 16))
   return msg_byte


# byte数组转换成16进制字符串
def byte2hex(msg):
   ml = len(msg)
   hexstr = ""
   for i in range(ml):
       hexstr = hexstr + ('%02x' % msg[i])
   return hexstr


def Hash_sm3(msg, Hexstr=0):
   if(Hexstr):
       msg_byte = hex2byte(msg)
   else:
       msg_byte = str2byte(msg)
   return hash_msg(msg_byte)


def KDF(Z,klen): # Z为16进制表示的比特串(str),klen为密钥长度(单位byte)
   klen = int(klen)
   ct = 0x00000001
   # ceil为向上取整函数
   rcnt = ceil(klen / 32)
   Zin = hex2byte(Z)
   Ha = ''
   for i in range(rcnt):
       msg = Zin + hex2byte('%08x' % ct)
       # print(msg)
       Ha = Ha + hash_msg(msg)
       # print(Ha)
       ct += 1
   return Ha[0: klen * 2]


if __name__ == '__main__':
   print('liushengwen')
   y = Hash_sm3('liushengwen')
   print(y)

   # # klen = 19
   # # print(KDF("57E7B63623FAE5F08CDA468E872A20AFA03DED41BF1403770E040DC83AF31A67991F2B01EBF9EFD8881F0A0493000603", klen))
   #
   # m = 'You are so beautiful'
   # m = hex2byte(m)
   # m = byte2hex(m)
   # print(m)

image-20200401190423849

实验总结

算法描述:

  1. 填充对数据填充的目的是使数据在填充后长度的512的整数倍。
  2. 迭代压缩处理
  3. 消息扩展
  4. 压缩函数(SM3安全的关键)

参考文献

  1. 国密SM3算法及基于SM3的密钥派生函数KDF
  2. python3实现的国密SM2
updatedupdated2020-05-252020-05-25