之前的无用功:
- 我用C++复现了别人开源的SM4,但是发现只能加密16进制数字,不合我的想法(我想可以加解密中文,怎么都有我不知道怎么解决的报错,然后太麻烦了(我不是很想研究透SM4,因为我只是为了完成课程作业,所以我知识了解了SM4的数学实现过程)于是我就想换一个更契合我想法的开源代码学习。
- 然后折腾了很久后,我突然意识到我不一定要用C++呀,我就去实验了最简单的python,只要调用就可以了,但是我觉得实在是太简单了,怕老师不满意☹️。
- 于是我想起我正在学习
go
!于是我正在用go
来实现, 给大家看一下我的第一版(半成品,解密那里出bug
了,明天考完试再debug
。
/*
SM4加密的方式和原理的简要说明:
1、密钥扩展:SM4使用128位的密钥,首先对密钥进行扩展,生成32个子密钥,用于后续的加密轮操作。
2、初始轮:将明文分为4个字节的分组,与第一个子密钥进行异或操作。
3、加密轮:SM4加密算法共进行32轮加密操作。每轮操作包括以下步骤:
字节替换:使用S盒进行字节替换。
行移位:对每个分组进行行移位操作。
列混淆:对每个分组进行列混淆操作。
轮密钥加:将当前轮的子密钥与分组进行异或操作。
4、最终轮:在最后一轮加密操作中,不进行列混淆操作,只进行字节替换、行移位和轮密钥加操作。
5、输出:经过32轮加密操作后,得到加密后的密文。
*/
package main
import (
"bufio"
"bytes" // 提供字节切片操作函数,用于实现PKCS#7填充和去填充
"crypto/cipher" // 提供加密块模式的实现,用于构建完整的加密过程
"encoding/hex" // 提供十六进制编解码功能,用于密钥、IV的输入输出
"fmt" // 提供格式化输入输出功能,用于调试和结果展示
"github.com/tjfoc/gmsm/sm4" // 提供SM4算法的具体实现,包括加密块生成、密钥扩展等核心功能
"os"
"strings"
)
func SM4Encrypt(data string) (result string, err error) {
// 字符串转byte切片
plainText := []byte(data)
// 建议从配置文件中读取秘钥,进行统一管理
SM4Key := "Uv6tkf2M3xYSRuFv"
// TODO 注意:iv需要是随机的,进一步保证加密的安全性,将iv的值和加密后的数据一起返回给外部
SM4Iv := "04TzMuvkHm_EZnHm"
iv := []byte(SM4Iv)
key := []byte(SM4Key)
//实例化sm4加密对象
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
//明文数据填充
paddingData := paddingLastGroup(plainText, block.BlockSize())
//声明SM4的加密工作模式
blockMode := cipher.NewCBCEncrypter(block, iv)
//为填充后的数据进行加密处理
cipherText := make([]byte, len(paddingData))
//使用CryptBlocks这个核心方法,将paddingData进行加密处理,将加密处理后的值赋值到cipherText中
blockMode.CryptBlocks(cipherText, paddingData)
//加密结果使用hex转成字符串,方便外部调用
cipherString := hex.EncodeToString(cipherText)
return cipherString, nil
}
// SM4Decrypt 传入string 输出string
func SM4Decrypt(data string) (res string, err error) {
//秘钥
SM4Key := "Uv6tkf2M3xYSRuFv"
//iv是Initialization Vector,初始向量,
SM4Iv := "04TzMuvkHm_EZnHm"
iv := []byte(SM4Iv)
key := []byte(SM4Key)
block, err := sm4.NewCipher(key)
if err != nil {
panic(err)
}
//使用hex解码
decodeString, err := hex.DecodeString(data)
if err != nil {
return "", err
}
//CBC模式 优点:具有较好的安全性,能够隐藏明文的模式和重复性。 缺点:加密过程是串行的,不适合并行处理。
blockMode := cipher.NewCBCDecrypter(block, iv)
//下文有详解这段代码的含义
blockMode.CryptBlocks(decodeString, decodeString)
//去掉明文后面的填充数据
plainText := unPaddingLastGroup(decodeString)
//直接返回字符串类型,方便外部调用
return string(plainText), nil
}
// 明文数据填充
func paddingLastGroup(plainText []byte, blockSize int) []byte {
//1.计算最后一个分组中明文后需要填充的字节数
padNum := blockSize - len(plainText)%blockSize
//2.将字节数转换为byte类型
char := []byte{byte(padNum)}
//3.创建切片并初始化
newPlain := bytes.Repeat(char, padNum)
//4.将填充数据追加到原始数据后
newText := append(plainText, newPlain...)
return newText
}
// 去掉明文后面的填充数据
func unPaddingLastGroup(plainText []byte) []byte {
//1.拿到切片中的最后一个字节
length := len(plainText)
lastChar := plainText[length-1] // 解密报错:plainText 为空,length-1 就是 -1,索引越界错误
//2.将最后一个数据转换为整数
number := int(lastChar)
return plainText[:length-number]
}
func main() {
// 创建一个缓冲读取器,用于读取标准输入
reader := bufio.NewReader(os.Stdin)
var plainText string
var decrypt string
// 选择模式[加密/解密/退出]
for {
fmt.Print("请输入命令 [加密/解密/退出]: ")
// 允许用户输入包含空格的内容。(从标准输入读取一整行内容,直到遇到 \n)
command, err := reader.ReadString('\n')
if err != nil {
fmt.Println("读取输入失败:", err)
}
// 删除首尾的空白字符,确保后续判断准确。
command = strings.TrimSpace(command)
switch command {
case "加密":
//待加密的数据 手动输入
fmt.Printf("请输入要加密的内容:")
_, err := fmt.Scanln(&plainText)
if err != nil {
return
}
//SM4加密
decrypt, err := SM4Encrypt(plainText)
if err != nil {
return
}
fmt.Printf("要加密的明文是:%s\n", plainText)
fmt.Printf("SM4加密结果:%s\n", decrypt)
//cipherString := hex.EncodeToString(cipherText)
//fmt.Printf("sm4加密结果转成字符串:%s\n", cipherString)
case "解密":
//SM4解密
sm4Decrypt, err := SM4Decrypt(decrypt)
if err != nil {
return
}
fmt.Printf("SM4解密结果:%s\n", sm4Decrypt)
flag := plainText == sm4Decrypt
fmt.Println("解密是否成功:", flag)
case "退出":
fmt.Println("程序已退出")
return
default:
fmt.Println("无效命令,请重新输入")
}
}
}
报错信息
panic: runtime error: index out of range [-1]
goroutine 1 [running]:
main.unPaddingLastGroup(...)
/Users/*/sm4-demo/main.go:98
main.SM4Decrypt({0x0, 0x0})
/Users/*/main.go:76 +0x134
main.main()
/Users/*/main.go:141 +0x308
错误原因应该是unPaddingLastGroup
的lastChar := plainText[length-1]
在明文不存在时会发生数组越界的情况。
- 但突发通知,我代表 党**** 为学院的党纪知识竞赛出了决赛的部分题目,出完题没多久图书馆就响铃,迫不得已结束学习,明天有重要的课和国家体测呜呜呜