前言

最近做的我的项目对安全性要求比拟高,特别强调:零碎不能波及MD5、SHA1、RSA1024、DES高风险算法。

那用什么嘞?甲方:倡议用国产明码算法SM4。

善于麻利开发(CV大法)的我,先去GitHub找了开源我的项目、又去网络上找了一些教程,然而或多或少都有些问题:

  1. 比方golang.org/x/crypto/sm4无奈装置编译
  2. 比方C站烂大巷的SM4教程,不能解决数据填充的问题,超过16位就解密失败了
  3. 比方如何封装成通用的办法,供零碎进行调用
  4. 更多就是复制粘贴了SM4的定义,很形象。

于是我花了2天工夫钻研SM4的原理和利用,解决了下面这些问题,整顿这篇文章分享给大家,让大家能少踩坑。

我会依照上面的程序分享这篇文章,不便大家更好的了解,如果你就是喜爱拿来主义(麻利开发),能够间接copy底部的示例代码,疾速上手应用即可。

文章目录

  1. SM4的劣势
  2. IV是什么?
  3. SM4加密的形式和原理
  4. SM4的各种工作模式比照
  5. 间接可用的「代码示例」
  6. 外围办法的源码解析
  7. 总结回顾

1. SM4的劣势

相比于其余加密算法,SM4加密算法具备以下几个劣势:

  1. 高安全性:SM4是一种对称加密算法,采纳128位密钥长度,具备较高的安全性和抗攻击性。它通过了宽泛的安全性剖析和评估,并通过了多个密码学规范的验证。
  2. 高效性:SM4算法的加密和解密速度较快,实用于对大量数据进行加密和解密的场景。它在硬件和软件实现上都具备高效性能。
  3. 简略性:SM4算法的实现绝对简略,代码量较小,易于了解和应用。它的设计指标之一是提供一种易于实现和部署的加密算法。
  4. 标准化SM4算法是中国国家明码管理局公布的明码算法规范,失去了宽泛的利用和认可。它已成为国内上公认的明码算法之一。
  5. 广泛支持:SM4算法在各种平台和编程语言中都有反对和实现,包含Go、Java、C/C++等。它能够在不同的零碎和环境中进行跨平台的利用和部署。
  6. 可扩展性:SM4算法反对不同的工作模式和填充形式,能够依据具体需要进行灵便配置。它能够与其余明码算法联合应用,提供更高级别的平安爱护。

小小的总结一下:SM4加密算法在安全性、高效性、简略性、标准化和广泛支持等方面具备劣势,实用于各种数据保护和加密利用场景。它是一种牢靠的加密算法抉择。

2.IV是什么?

我在学习的时候看到IV就蒙了,所以有必要先说分明IV的概念:

Initialization Vector(IV)是一种在密码学中应用的初始值。它是一个固定长度的随机数或者随机生成的值,用于在加密算法中初始化明码算法的状态。

在加密过程中,IV的作用是引入随机性和唯一性,以减少加密的安全性。 它与密钥一起用于初始化明码算法的外部状态,确保每次加密操作都产生不同的输入,即便雷同的明文应用雷同的密钥进行加密。

IV的长度和应用形式取决于具体的加密算法和利用场景。在应用加密算法时,IV通常须要与密文一起传输给解密方,以便解密方可能正确还原明文。

须要留神的是:IV自身不须要窃密,能够与密文一起传输。然而,为了确保加密的安全性,IV应该是随机生成的,并且每次加密操作都应该应用不同的IV。这样能够避免明码剖析者通过观察加密后果的模式来破解密钥或者明文。

3. SM4加密的形式和原理

SM4加密算法是一种对称加密算法,采纳分组明码的形式对数据进行加密。

上面是SM4加密的形式和原理的简要阐明:

  1. 密钥扩大:SM4应用128位的密钥,首先对密钥进行扩大,生成32个子密钥,用于后续的加密轮操作。
  2. 初始轮:将明文分为4个字节的分组,与第一个子密钥进行异或操作。
  3. 加密轮:SM4加密算法共进行32轮加密操作。每轮操作包含以下步骤:

    • 字节替换:应用S盒进行字节替换。
    • 行移位:对每个分组进行行移位操作。
    • 列混同:对每个分组进行列混同操作。
    • 轮密钥加:将以后轮的子密钥与分组进行异或操作。
  4. 最终轮:在最初一轮加密操作中,不进行列混同操作,只进行字节替换、行移位和轮密钥加操作。
  5. 输入:通过32轮加密操作后,失去加密后的密文。

SM4加密算法的安全性和强度次要来自于其简单的轮函数和密钥扩大过程。它具备较高的安全性和抗攻击性,并且在理论利用中失去了宽泛的利用和认可。

须要留神的是:SM4加密算法的安全性还依赖于密钥的保密性和随机性。在应用SM4进行加密时,应确保应用足够强度的密钥,并采取适当的密钥治理和保护措施。

4.SM4的各种工作模式比照

SM4加密算法能够应用不同的工作模式,其中包含CBC(Cipher Block Chaining)模式。

我应用的是CBC模式,上面和大家分享一下CBC模式与其余模式的比照:

  1. CBC模式(Cipher Block Chaining):

    • 特点:每个明文块与前一个密文块进行异或操作,而后再进行加密。初始块应用初始化向量(IV)。
    • 长处:具备较好的安全性,可能暗藏明文的模式和重复性。
    • 毛病:加密过程是串行的,不适宜并行处理。
  2. ECB模式(Electronic Codebook):

    • 特点:将每个明文块独立加密,雷同的明文块会失去雷同的密文块。
    • 长处:简略、并行处理效率高。
    • 毛病:不能暗藏明文的模式和重复性,不适宜加密大量反复的数据。
  3. CFB模式(Cipher Feedback):

    • 特点:将前一个密文块作为输出来加密以后的明文块,能够实现流明码的性能。
    • 长处:可能解决不定长的数据流,实用于实时加密和流式传输。
    • 毛病:加密过程是串行的,不适宜并行处理。
  4. OFB模式(Output Feedback):

    • 特点:将前一个密文块作为输出来生成密钥流,而后与明文块进行异或操作,能够实现流明码的性能。
    • 长处:可能解决不定长的数据流,实用于实时加密和流式传输。
    • 毛病:加密过程是串行的,不适宜并行处理。
  5. CTR模式(Counter):

    • 特点:应用一个计数器来生成密钥流,而后与明文块进行异或操作,能够实现流明码的性能。
    • 长处:可能解决不定长的数据流,实用于实时加密和流式传输。并行处理效率高,适宜硬件实现。
    • 毛病:须要保障计数器的唯一性,否则会导致密钥流的反复。

比照总结:

  • CBC模式和ECB模式相比,CBC模式具备更好的安全性,可能暗藏明文的模式和重复性,而ECB模式无奈暗藏这些信息。
  • CFB模式、OFB模式和CTR模式都是流明码模式,实用于不定长的数据流加密,可能实现实时加密和流式传输。它们的次要区别在于密钥流的生成形式和加密过程的并行性。
  • CFB模式和OFB模式的加密过程是串行的,不适宜并行处理,而CTR模式的加密过程能够并行处理,适宜硬件实现。

总的来说:CBC模式在安全性方面较好,可能暗藏明文的模式和重复性。而流明码模式(CFB、OFB和CTR)实用于不定长数据流的加密,可能实现实时加密和流式传输,其中CTR模式具备较好的并行处理性能。抉择适合的加密模式取决于具体的利用需要和安全性要求。

5. 间接可用的「代码示例」

我始终认为能够通过复制粘贴,间接跑通的示例代码才是好代码。

没错,我的代码示例就是这样,并且要害代码都写好了正文:

package mainimport (    "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 输入stringfunc 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

公众号:程序员升职加薪之旅