前语
最近做的项目对安全性要求比较高,特别强调:体系不能涉及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
也欢迎我们重视我的,点赞、留言、转发。你的支撑,是我更文的最大动力!