[密码学]: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

  1. 密钥扩展
    在密钥扩展方案中,种子密钥经过扩展生成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的主要内容,如果未能解决你的问题,请参考以下文章

密码学之公钥密码体系:ElGamal算法

密码学之公钥密码体系:ElGamal算法

一文详解密码学中的Hash 算法

密码学之公钥密码体系:RSA算法

密码学之对称加密算法

密码学——公钥密码体系之ElGamal算法3