[密码学]:SM4
Posted Kiki__c
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[密码学]:SM4相关的知识,希望对你有一定的参考价值。
[密码学]:SM4
SM4算法是我国商用密码标准,其前身是SMS4算法。SM4算法是一个分组加密算法,分组长度和密钥长度均128bit。SM4算法使用32轮的非线性迭代结构。SM4在最后一轮非线性迭代之后加上了一个反序变换,因此SM4中只要解密密钥是加密密钥的逆序,它的解密算法与加密算法就可以保持一致。SM4的主体运算是非平衡Feistel网络。整体逻辑结构如图1所示,经过32轮变换把明文变换为密文。
其中密钥扩展运算把128 bit的种子密钥扩展为32个32 bit的子密钥。下面分别介绍轮函数、密钥扩展和加解密。
图3 SM4算法L函数
非线性变换 由四个S盒并行组成。设变换 的输入 ,输出是 ,则 。不同于DES等分组密码算法,SM4算法中的这四个S盒实际上是同一个8 bit -> 8bit的S盒,详见下表:
表1 SM4算法的S盒
0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0ax 0xb 0xc 0xd 0ex 0xf
0x0 D6 90 E9 FE CC E1 3D B7 16 B6 14 C2 28 FB 2C 05
0x1 2B 67 9A 76 2A BE 04 C3 AA 44 13 26 49 86 06 99
0x2 9C 42 50 F4 91 EF 98 7A 33 54 0B 43 ED CF AC 62
0x3 E4 B3 1C A9 C9 08 E8 95 80 DF 94 FA 75 8F 3F A6
0x4 47 07 A7 FC F3 73 17 BA 83 59 3C 19 E6 85 4F A8
0x5 68 6B 81 B2 71 64 DA 8B F8 EB 0F 4B 70 56 9D 35
0x6 1E 24 0E 5E 63 58 D1 A2 25 22 7C 3B 01 21 78 87
0x7 D4 00 46 57 9F D3 27 52 4C 36 02 E7 A0 C4 C8 9E
0x8 EA BF 8A D2 40 C7 38 B5 A3 F7 F2 CE F9 61 15 A1
0x9 E0 AE 5D A4 9B 34 1A 55 AD 93 32 30 F5 8C B1 E3
0xa 1D F6 E2 2E 82 66 CA 60 C0 29 23 AB 0D 53 4E 6F
0xb D5 DB 37 45 DE FD 8E 2F 03 FF 6A 72 6D 6C 5B 51
0xc 8D 1B AF 92 BB DD BC 7F 11 D9 5C 41 1F 10 5A D8
0xd 0A C1 31 88 A5 CD 7B BD 2D 74 D0 12 B8 E5 B4 B0
0xe 89 69 97 4A 0C 96 77 7E 65 B9 F1 09 C5 6E C6 84
0xf 18 F0 7D EC 3A DC 4D 20 79 EE 5F 3E D7 CB 39 48
- 密钥扩展
在密钥扩展方案中,种子密钥经过扩展生成32个轮密钥,每个轮密钥长度为32 bit。首先,128 bit的种子密钥SK分为四组 ,再给定系统参数
FK = (FK0, FK1, FK2, FK3) = 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc;
与固定参数 (其中 ),密钥扩展规则如下:
.
3. 加解密
加密与解密的轮函数结构完全相同,唯一的区别是解密密钥是加密密钥的逆序。加密轮密钥的使用顺序为RK0,…,RK31,解密时轮密钥的使用顺序为RK31,…,RK0。
代码
import re
SboxTable = [
0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c, 0x05,
0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed, 0xcf, 0xac, 0x62,
0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa, 0x75, 0x8f, 0x3f, 0xa6,
0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c, 0x19, 0xe6, 0x85, 0x4f, 0xa8,
0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb, 0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35,
0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87,
0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52, 0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e,
0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38, 0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1,
0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34, 0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3,
0x1d, 0xf6, 0xe2, 0x2e, 0x82, 0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f,
0xd5, 0xdb, 0x37, 0x45, 0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51,
0x8d, 0x1b, 0xaf, 0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8,
0x0a, 0xc1, 0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0,
0x89, 0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39, 0x48,
]
Roundk=[]
Ck=[]
def xor(str1, str2): # 异或运算
r = ''
for i in range(len(str1)):
t = int(str1[i]) ^ int(str2[i])
if (int(t) == 1):
r += '1'
else:
r += '0'
return r
def str2bits(str):
r = ''
for i in str:
t = bin(ord(i))[2:]
for j in range(0, 8 - len(t)):
t = '0' + t # 把输出的b给去掉
r += t
return (r)
def bin2str(bin_str):
r = ""
tmp = re.findall(r'.8', bin_str)
for i in tmp:
r+= chr(int(i, 2))
return r
def int2bit(x,n): #十进制转为n位2进制
r=bin(x)[2:]
while(len(r)<n):
r='0'+r
return r
def move(k, n): # 循环移位
return k[n:]+k[:n] #移位数在长度之内,未做溢出判断
def T(x): #x:32bits,
B='' #B:32bits
for i in range(0,8,2): #分为四组
B+=int2bit(SboxTable[int(x[i*4:(i+1)*4],2)*16+int(x[(i+1)*4:(i+2)*4],2)],8) #高四位为排,低四位为列。
C=xor(B,xor(move(B,2),xor(move(B,10),xor(move(B,18),move(B,24)))))
return C
def TT(x):
B = '' # B:32bits
for i in range(0, 8, 2): # 分为四组
B+=int2bit(SboxTable[int(x[i*4:(i+1)*4],2)*16+int(x[(i+1)*4:(i+2)*4],2)],8) # 高四位为排,低四位为列。
C = xor(B,xor(move(B,13),move(B,23)))
return C
def f(x): # Rk:32bits
t = ''
x=str2bits(x) #字符串转为比特
#x=int2bit(int('0x0123456789abcdeffedcba9876543210',16),128)
for j in range(32):
t = xor(x[j*32:(j+1) * 32], T(xor(x[(j+1) * 32:(j+2) * 32], xor(x[(j+2) * 32:(j+3) * 32], xor(Roundk[j], x[(j+3) * 32:(j+4) * 32]))))) #x128bits,分为四组进行异或操作。
x+=t
return x[35*32:36*32]+ x[34* 32:35* 32] +x[33* 32:34* 32]+x[32*32:33 * 32] #倒序输出
def ff(x):
t = ''
for j in range(32):
t = xor(x[j*32:(j+1) * 32], T(xor(x[(j+1) * 32:(j+2) * 32], xor(x[(j+2) * 32:(j+3) * 32], xor(Roundk[j], x[(j+3) * 32:(j+4) * 32]))))) ##x128bits,分为四组进行异或操作。
x+=t
return x[35*32:36*32]+ x[34* 32:35* 32] +x[33* 32:34* 32]+x[32*32:33 * 32] # 倒序输出
def set_Ck(): #Ck[i]:32bits
for i in range(32):
t=''
for j in range(4):
t+=int2bit(7*(4*i+j)%256,8)
Ck.append(t)
def RoundKey(): #key:128bits
key=str2bits(input('请输入16字节密钥:'))
#key=int2bit(int('0x0123456789abcdeffedcba9876543210',16),128)
K=[]
FK1, FK2, FK3, FK4 = 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc
K.append(xor(int2bit(FK1,32),key[0:32])) #k0
K.append(xor(int2bit(FK2,32),key[32:64])) #k1
K.append(xor(int2bit(FK3,32),key[64:96])) #k2
K.append(xor(int2bit(FK4,32),key[96:128])) #k3
for i in range(32):
t=xor(K[i],TT(xor(K[i+1],xor(K[i+2],xor(K[i+3],Ck[i])))))
K.append(t) #K[4]-K[35]
Roundk.append(t) #加入轮密钥
def encrypt():
r=''
set_Ck()
RoundKey()
m=input('请输入明文(输入#结束):')
if m == '0x0123456789abcdeffedcba9876543210':
r+=f(int2bit(int(m,16),128))
print(hex(int(r,2)))
else:
while(len(m)%16!=0):
m+='0'
for i in range(0,len(m),16):
r+=f(m[i:i+16]) #分组加密|m为字符串
print('密文:'+r)
def decrypt():
r=''
set_Ck()
RoundKey()
Roundk.reverse() #解密密钥是加密密钥的逆序
m = input('请输入密文')
for i in range(0,len(m),128):
r+=ff(m[i:i+128])
r=bin2str(r)
for i in range(len(r) - 1, 0, -1):
if r[i] == "#":
r = r[0:i]
break
print('明文:' + r)
option=int(input('请选择:1、加密 2、解密\\n'))
if option==1:
encrypt()
else:
decrypt()
示例
加密:
解密:
4.27更新
由于老师突然增加一个要求,验证SM4算法,我又对代码进行了一些修改
密码技术--国密SM2椭圆曲线公钥密码算法及Go语言应用
SM2椭圆曲线公钥密码算法
SM2算法和RSA算法都是公钥密码算法,SM2算法是一种更先进安全的算法,SM2是国家密码局与2010年12月17日发布的椭圆曲线公钥密码算法,在我们国家商用密码体系中被用来替换RSA算法。
SM2加解密
package main
import (
"crypto/rand"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
"os"
)
func GerenateSM2Key(){
//1.生成sm2密钥对
privateKey, err := sm2.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
//2.通过x509将私钥反序列化并进行pem编码
privateKeyToPem, err := x509.WritePrivateKeyToPem(privateKey, nil)
if err != nil {
panic(err)
}
//3.将私钥写入磁盘文件
file, err := os.Create("sm2Private.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(privateKeyToPem)
if err != nil {
panic(err)
}
//4.进行SM2公钥断言
publicKey := privateKey.Public().(*sm2.PublicKey)
//5.将公钥通过x509序列化并进行pem编码
publicKeyToPem, err := x509.WritePublicKeyToPem(publicKey)
if err != nil {
panic(err)
}
//6.将公钥写入磁盘文件
file, err = os.Create("sm2Public.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(publicKeyToPem)
if err != nil {
panic(err)
}
}
//加密
func EncryptSM2(plainText []byte, pubFileName string) []byte {
//1.打开公钥文件读取公钥
file, err := os.Open(pubFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
//2.将pem格式公钥解码并反序列化
publicKeyFromPem, err := x509.ReadPublicKeyFromPem(buf)
if err != nil {
panic(err)
}
//3.加密
cipherText, err := publicKeyFromPem.EncryptAsn1(plainText, rand.Reader)
if err != nil {
panic(err)
}
return cipherText
}
//解密
func DecryptSM2(cipherText []byte, priFileName string) []byte {
//1.打开私钥问价读取私钥
file, err := os.Open(priFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
//2.将pem格式私钥文件解码并反序列话
privateKeyFromPem, err := x509.ReadPrivateKeyFromPem(buf, nil)
if err != nil {
panic(err)
}
//3.解密
planiText, err := privateKeyFromPem.DecryptAsn1(cipherText)
if err != nil {
panic(err)
}
return planiText
}
func main(){
GerenateSM2Key()
src := []byte("这是使用SM2椭圆曲线算法进行数据加解密测试")
cipherText := EncryptSM2(src, "sm2Public.pem")
plainText := DecryptSM2(cipherText, "sm2Private.pem")
flag := bytes.Equal(plainText, src)
fmt.Println("解密是否成功:", flag)
}
SM2签名验签
package main
import (
"crypto/rand"
"github.com/tjfoc/gmsm/sm2"
"github.com/tjfoc/gmsm/x509"
"os"
)
func GerenateSM2Key(){
//1.生成sm2密钥对
privateKey, err := sm2.GenerateKey(rand.Reader)
if err != nil {
panic(err)
}
//2.通过x509将私钥反序列化并进行pem编码
privateKeyToPem, err := x509.WritePrivateKeyToPem(privateKey, nil)
if err != nil {
panic(err)
}
//3.将私钥写入磁盘文件
file, err := os.Create("sm2Private.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(privateKeyToPem)
if err != nil {
panic(err)
}
//4.进行SM2公钥断言
publicKey := privateKey.Public().(*sm2.PublicKey)
//5.将公钥通过x509序列化并进行pem编码
publicKeyToPem, err := x509.WritePublicKeyToPem(publicKey)
if err != nil {
panic(err)
}
//6.将公钥写入磁盘文件
file, err = os.Create("sm2Public.pem")
if err != nil {
panic(err)
}
defer file.Close()
_, err = file.Write(publicKeyToPem)
if err != nil {
panic(err)
}
}
//签名
func SignSM2(plainText []byte, priFileName string) []byte {
//1.打开私钥问价读取私钥
file, err := os.Open(priFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
//2.将pem格式私钥文件解码并反序列话
privateKeyFromPem, err := x509.ReadPrivateKeyFromPem(buf, nil)
if err != nil {
panic(err)
}
//3.签名
sign, err := privateKeyFromPem.Sign(rand.Reader, plainText, crypto.SHA256)
if err != nil {
panic(err)
}
return sign
}
//验签
func VerifySM2(plainText, signed []byte, pubFileName string) bool {
//1.打开公钥文件读取公钥
file, err := os.Open(pubFileName)
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, fileInfo.Size())
_, err = file.Read(buf)
if err != nil {
panic(err)
}
//2.将pem格式公钥解码并反序列化
publicKeyFromPem, err := x509.ReadPublicKeyFromPem(buf)
if err != nil {
panic(err)
}
//3.验签
verify := publicKeyFromPem.Verify(plainText, signed)
return verify
}
func main(){
src := []byte("这是使用SM2椭圆曲线算法进行的签名验签测试")
signSM2 := SignSM2(src, "sm2Private.pem")
flag := VerifySM2(src, signSM2, "sm2Public.pem")
fmt.Println("验签结果:", flag)
}
以上是关于[密码学]:SM4的主要内容,如果未能解决你的问题,请参考以下文章