前言
最近做的我的项目对安全性要求比拟高,特别强调:零碎不能波及 MD5、SHA1、RSA1024、DES 高风险算法。
那用什么嘞?甲方:倡议用国产明码算法 SM4。
善于麻利开发(CV 大法)的我,先去 GitHub 找了开源我的项目、又去网络上找了一些教程,然而或多或少都有些问题:
- 比方
golang.org/x/crypto/sm4
无奈装置编译 - 比方 C 站烂大巷的 SM4 教程,不能解决数据填充的问题,超过 16 位就解密失败了
- 比方如何封装成通用的办法,供零碎进行调用
- 更多就是复制粘贴了 SM4 的定义,很形象。
于是我花了 2 天工夫钻研 SM4 的原理和利用,解决了下面这些问题,整顿这篇文章分享给大家,让大家能少踩坑。
我会依照上面的程序分享这篇文章,不便大家更好的了解,如果你就是喜爱拿来主义(麻利开发),能够间接 copy 底部的示例代码,疾速上手应用即可。
文章目录
- SM4 的劣势
- IV 是什么?
- SM4 加密的形式和原理
- SM4 的各种工作模式比照
- 间接可用的「代码示例」
- 外围办法的源码解析
- 总结回顾
1. SM4 的劣势
相比于其余加密算法,SM4 加密算法具备以下几个劣势:
- 高安全性:SM4 是一种对称加密算法,采纳 128 位密钥长度,具备较高的安全性和抗攻击性。它通过了宽泛的安全性剖析和评估,并通过了多个密码学规范的验证。
- 高效性:SM4 算法的加密和解密速度较快,实用于对大量数据进行加密和解密的场景。它在硬件和软件实现上都具备高效性能。
- 简略性:SM4 算法的实现绝对简略,代码量较小,易于了解和应用。它的设计指标之一是提供一种易于实现和部署的加密算法。
- 标准化:SM4 算法是中国国家明码管理局公布的明码算法规范,失去了宽泛的利用和认可。它已成为国内上公认的明码算法之一。
- 广泛支持:SM4 算法在各种平台和编程语言中都有反对和实现,包含 Go、Java、C/C++ 等。它能够在不同的零碎和环境中进行跨平台的利用和部署。
- 可扩展性:SM4 算法反对不同的工作模式和填充形式,能够依据具体需要进行灵便配置。它能够与其余明码算法联合应用,提供更高级别的平安爱护。
小小的总结一下:SM4 加密算法在安全性、高效性、简略性、标准化和广泛支持等方面具备劣势,实用于各种数据保护和加密利用场景。它是一种牢靠的加密算法抉择。
2.IV 是什么?
我在学习的时候看到 IV 就蒙了,所以有必要先说分明 IV 的概念:
Initialization Vector(IV)是一种在密码学中应用的初始值。它是一个固定长度的随机数或者随机生成的值,用于在加密算法中初始化明码算法的状态。
在加密过程中,IV 的作用是引入随机性和唯一性,以减少加密的安全性。 它与密钥一起用于初始化明码算法的外部状态,确保每次加密操作都产生不同的输入,即便雷同的明文应用雷同的密钥进行加密。
IV 的长度和应用形式取决于具体的加密算法和利用场景。在应用加密算法时,IV 通常须要与密文一起传输给解密方,以便解密方可能正确还原明文。
须要留神的是:IV 自身不须要窃密,能够与密文一起传输。然而,为了确保加密的安全性,IV 应该是随机生成的,并且每次加密操作都应该应用不同的 IV。这样能够避免明码剖析者通过观察加密后果的模式来破解密钥或者明文。
3. SM4 加密的形式和原理
SM4 加密算法是一种对称加密算法,采纳分组明码的形式对数据进行加密。
上面是 SM4 加密的形式和原理的简要阐明:
- 密钥扩大:SM4 应用 128 位的密钥,首先对密钥进行扩大,生成 32 个子密钥,用于后续的加密轮操作。
- 初始轮:将明文分为 4 个字节的分组,与第一个子密钥进行异或操作。
-
加密轮:SM4 加密算法共进行 32 轮加密操作。每轮操作包含以下步骤:
- 字节替换:应用 S 盒进行字节替换。
- 行移位:对每个分组进行行移位操作。
- 列混同:对每个分组进行列混同操作。
- 轮密钥加:将以后轮的子密钥与分组进行异或操作。
- 最终轮:在最初一轮加密操作中,不进行列混同操作,只进行字节替换、行移位和轮密钥加操作。
- 输入:通过 32 轮加密操作后,失去加密后的密文。
SM4 加密算法的安全性和强度次要来自于其简单的轮函数和密钥扩大过程。它具备较高的安全性和抗攻击性,并且在理论利用中失去了宽泛的利用和认可。
须要留神的是:SM4 加密算法的安全性还依赖于密钥的保密性和随机性。在应用 SM4 进行加密时,应确保应用足够强度的密钥,并采取适当的密钥治理和保护措施。
4.SM4 的各种工作模式比照
SM4 加密算法能够应用不同的工作模式,其中包含 CBC(Cipher Block Chaining)模式。
我应用的是 CBC 模式,上面和大家分享一下 CBC 模式与其余模式的比照:
-
CBC 模式(Cipher Block Chaining):
- 特点:每个明文块与前一个密文块进行异或操作,而后再进行加密。初始块应用初始化向量(IV)。
- 长处:具备较好的安全性,可能暗藏明文的模式和重复性。
- 毛病:加密过程是串行的,不适宜并行处理。
-
ECB 模式(Electronic Codebook):
- 特点:将每个明文块独立加密,雷同的明文块会失去雷同的密文块。
- 长处:简略、并行处理效率高。
- 毛病:不能暗藏明文的模式和重复性,不适宜加密大量反复的数据。
-
CFB 模式(Cipher Feedback):
- 特点:将前一个密文块作为输出来加密以后的明文块,能够实现流明码的性能。
- 长处:可能解决不定长的数据流,实用于实时加密和流式传输。
- 毛病:加密过程是串行的,不适宜并行处理。
-
OFB 模式(Output Feedback):
- 特点:将前一个密文块作为输出来生成密钥流,而后与明文块进行异或操作,能够实现流明码的性能。
- 长处:可能解决不定长的数据流,实用于实时加密和流式传输。
- 毛病:加密过程是串行的,不适宜并行处理。
-
CTR 模式(Counter):
- 特点:应用一个计数器来生成密钥流,而后与明文块进行异或操作,能够实现流明码的性能。
- 长处:可能解决不定长的数据流,实用于实时加密和流式传输。并行处理效率高,适宜硬件实现。
- 毛病:须要保障计数器的唯一性,否则会导致密钥流的反复。
比照总结:
- CBC 模式和 ECB 模式相比,CBC 模式具备更好的安全性,可能暗藏明文的模式和重复性,而 ECB 模式无奈暗藏这些信息。
- CFB 模式、OFB 模式和 CTR 模式都是流明码模式,实用于不定长的数据流加密,可能实现实时加密和流式传输。它们的次要区别在于密钥流的生成形式和加密过程的并行性。
- CFB 模式和 OFB 模式的加密过程是串行的,不适宜并行处理,而 CTR 模式的加密过程能够并行处理,适宜硬件实现。
总的来说:CBC 模式在安全性方面较好,可能暗藏明文的模式和重复性。而流明码模式(CFB、OFB 和 CTR)实用于不定长数据流的加密,可能实现实时加密和流式传输,其中 CTR 模式具备较好的并行处理性能。抉择适合的加密模式取决于具体的利用需要和安全性要求。
5. 间接可用的「代码示例」
我始终认为能够通过复制粘贴,间接跑通的示例代码才是好代码。
没错,我的代码示例就是这样,并且要害代码都写好了正文:
package main
import (
"bytes"
"crypto/cipher"
"encoding/hex"
"fmt"
"github.com/tjfoc/gmsm/sm4"
)
// SM4 加密
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
}
// SM4 解密 传入 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]
//2. 将最初一个数据转换为整数
number := int(lastChar)
return plainText[:length-number]
}
func main() {
// 待加密的数据 模仿 18 位的身份证号
plainText := "131229199907097219"
//SM4 加密
decrypt, err := SM4Encrypt(plainText)
if err != nil {return}
fmt.Printf("sm4 加密后果:%s\n", decrypt)
//cipherString := hex.EncodeToString(cipherText)
//fmt.Printf("sm4 加密后果转成字符串:%s\n", cipherString)
//SM4 解密
sm4Decrypt, err := SM4Decrypt(decrypt)
if err != nil {return}
fmt.Printf("plainText:%s\n", sm4Decrypt)
flag := plainText == sm4Decrypt
fmt.Println("解密是否胜利:", flag)
}
运行后果如下:
6. 外围办法的源码解析
仔细的小伙伴应该又发现,(或者通过你实在的敲代码肯定能发现。
在加密和解密局部有一个 CryptBlocks()办法,咱们来解析一下这段源码:
// CryptBlocks encrypts or decrypts a number of blocks. The length of
// src must be a multiple of the block size. Dst and src must overlap
// entirely or not at all.
//
// If len(dst) < len(src), CryptBlocks should panic. It is acceptable
// to pass a dst bigger than src, and in that case, CryptBlocks will
// only update dst[:len(src)] and will not touch the rest of dst.
//
// Multiple calls to CryptBlocks behave as if the concatenation of
// the src buffers was passed in a single run. That is, BlockMode
// maintains state and does not reset at each CryptBlocks call.
CryptBlocks(dst, src []byte)
翻译翻译
CryptBlocks 办法用于加密或解密多个数据块。src 的长度必须是块大小的倍数。dst 和 src 必须齐全重叠或齐全不重叠。
如果 len(dst) < len(src),CryptBlocks 办法应该引发 panic。容许传递比 src 更大的 dst,此时 CryptBlocks 只会更新 dst[:len(src)],不会涉及 dst 的其余部分。
在这段代码正文中,dst 示意指标缓冲区,用于存储加密或解密后的后果。src 示意源缓冲区,蕴含要加密或解密的数据。这两个缓冲区能够是雷同的内存区域,也能够是不同的内存区域。CryptBlocks 办法会将 src 中的数据进行加密或解密,并将后果存储在 dst 中。
须要留神的是,dst 和 src 的长度必须是块大小的倍数,否则 CryptBlocks 办法可能会引发 panic。如果 dst 的长度小于 src 的长度,CryptBlocks 办法只会更新 dst 的前 len(src)个字节,并不会批改 dst 的其余部分。
此外,CryptBlocks 办法能够屡次调用,屡次调用的成果相当于将所有 src 缓冲区的数据连贯在一起,而后进行加密或解密。这意味着 BlockMode 会放弃状态,并且不会在每次 CryptBlocks 调用时重置。
如果你看正文翻译了解起来还是比拟形象的话,我换个形式介绍一下:
用我的话来说
在 SM4 加密中,CryptBlocks()办法是用于加密或解密多个数据块的办法。它是 SM4 算法中的一个外围函数。
具体来说,CryptBlocks()办法承受一个源数据缓冲区(src)和一个指标数据缓冲区(dst),并对源数据进行加密或解密操作,将后果存储在指标数据缓冲区中。
在加密过程中,CryptBlocks()办法会将源数据分成多个数据块,而后对每个数据块进行加密操作,并将后果存储在指标数据缓冲区中。加密过程中应用的密钥和其余参数由 SM4 算法的实现确定。
在解密过程中,CryptBlocks()办法会对源数据缓冲区中的数据块进行解密操作,并将解密后的后果存储在指标数据缓冲区中。
须要留神的是:CryptBlocks()办法要求源数据缓冲区和指标数据缓冲区的长度必须是 SM4 算法的块大小的倍数。否则,可能会引发谬误或产生不可预测的后果。
CryptBlocks()办法是 SM4 加密算法中用于加密或解密多个数据块的要害办法,它实现了 SM4 算法的外围性能。
7. 总结回顾
我之前也写过一篇解密解密的文章,欢送大家浏览指教:保障网络申请数据传输的安全性、一致性和防篡改:对称加密与非对称加密的联合
置信你读了这篇文章能对 SM4 加密有个整体了解,通过我在文章中提供的示例代码能够疾速跑通加密和解密流程。我还带着你剖析了 CryptBlocks()源码的作用。
欢送大家珍藏、点赞、转发。你的关注,是我更文的最大能源!
欢送关注 ❤
我的微信:wangzhongyang1993
视频号:王中阳 Go
公众号:程序员升职加薪之旅