保护数据隐私:深入探索Golang中的SM4加密解密算法
前言
最近做的项目对安全性要求比较高,特别强调:系统不能涉及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
公众号:程序员升职加薪之旅
保护数据隐私:深入探索Golang中的SM4加密解密算法的更多相关文章
- golang AES/ECB/PKCS5 加密解密 url-safe-base64
因为项目的需要用到golang的一种特殊的加密解密算法AES/ECB/PKCS5,但是算法并没有包含在标准库中,经过多次失败的尝试,终于解码成功,特此分享: /* 描述 : golang AES/EC ...
- 支付接口中常用的加密解密以及验签rsa,md5,sha
一.常用加密类型分类 1.对称加密:采用单钥对信息进行加密和解密,即同一个秘钥既可以对信息进行加密,也可以进行解密.此类型称之为对称加密.特点速度快,常用于对大量数据信息或文件加密时使用.常用例子:D ...
- C#开发中常用的加密解密方法
转载自:https://www.cnblogs.com/bj981/p/11203711.html C#开发中常用的加密解密方法 相信很多人在开发过程中经常会遇到需要对一些重要的信息进行加密处理,今天 ...
- go-dongle 0.2.0 版本发布了,一个轻量级、语义化的 golang 编码解码、加密解密库
dongle 是一个轻量级.语义化.对开发者友好的 Golang 编码解码和加密解密库 Dongle 已被 awesome-go 收录, 如果您觉得不错,请给个 star 吧 github.com/g ...
- sm4加密 解密(oc)
前几天项目用到sm4加密解密,加密为十六进制字符串,再将十六进制字符串解密.网上百度了下,sm4是密钥长度和加密明文加密密文都为16个字节十六进制数据,网上的sm4 c语言算法很容易搜到,笔者刚开始没 ...
- N个整数(数的大小为0-255)的序列,把它们加密为K个整数(数的大小为0-255).再将K个整数顺序随机打乱,使得可以从这乱序的K个整数中解码出原序列。设计加密解密算法,且要求K<=15*N.
N个整数(数的大小为0-255)的序列,把它们加密为K个整数(数的大小为0-255).再将K个整数顺序随机打乱,使得可以从这乱序的K个整数中解码出原序列.设计加密解密算法,且要求K<=15*N. ...
- 转 关于Https协议中的ssl加密解密流程
关于Https协议中的ssl加密解密流程 2016年09月28日 09:51:15 阅读数:14809 转载自:http://www.cnblogs.com/P_Chou/archive/2010/1 ...
- ASP.NET中的DEC加密解密过程
本文章分享自 青青果树园的博客,地址是:http://www.cnblogs.com/qqingmu/archive/2008/01/10/1034168.html 我们做网页时经常会遇到URL传输( ...
- java中使用MD5加密的算法
MD5,全名Message Digest Algorithm 5,中文名为消息摘要算法第五版,为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.以下是JAVA语言中使用MD5加密的工具 ...
- android 中文件加密 解密 算法实战
现在项目里面有一个需求,本项目里面下载的视频和文档都不允许通过其他的播放器播放,在培训机构里面这样的需求很多.防止有人交一份钱,把所有的课件就拷给了别人.这样的事情培训机构肯定是不愿意的.现在我项目里 ...
随机推荐
- Python property、setter、deleter
面向对象封装特点之一就是通过实现好的方法来访问,限制对数据的不合理访问,把对象状态私有化,仅供类的内部进行操作 下方示例,Test方法的number属性类实例的时候传递1,number是一个公开属性, ...
- 百度松果菁英班--oj赛(第二次)
目录 一.小码哥剪绳子 二.咖啡品鉴师小码哥 三.均分糖果 四.持盾 五.活动安排 六.甜品供应 七.斐波那契数列的组合 八.小码哥的布阵指挥 九.活动分组 十.外卖递送 一.小码哥剪绳子 题目:马上 ...
- Java学习笔记08
1. static关键字 static可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的. 1.1 静态变量 ...
- 对象数组排序 和 类比JDK实现 sort()的方法
1.定义自己的 MyComparable 接口 1 package Test.treeSetDemo; 2 3 public interface MyComparable <E>{ 4 i ...
- extend笔记
JavaScript面向对象 继承extend 1. 概念(主要用途) 将子类中的共性代码 ( 属性和方法 ) 抽取出来 放到父类中 每当有一个新的子类需要用到共性的属性或者方法时 不需要在自己内容复 ...
- C# ConfigureWait
ConfigureAwait 参数为bool类型.true:尝试将延续任务封送回原始上下文 我们一般使用的是false,用于避免强制在原始上下文或调度程序中进行回调. 原理: 以await DoSom ...
- Python 列表定义
列表定义 由一系列按特定排序排列的元素组成,各元素之间无任何关系 用方括号[]来表示列表,并用逗号分隔其中的元素 访问列表元素 列表是有序集合,访问列表元素时,只需将该元素的位置或索引告知python ...
- #AI 1分钟学会,利用AI制作思维导图 (NewBing&X-Mind )
思维导图是一种有效的思考和学习工具,它可以帮助你整理和呈现信息,激发你的创造力和记忆力.但是,传统的思维导图软件往往需要你花费大量的时间和精力来设计和绘制,而且难以修改和分享.有没有一种更简单和智能的 ...
- 2022-10-04:以下go语言代码输出什么?A:{123} main.T{x:123} B:{123} T{x:123} C:boo boo D:boo main.T{x:123}。 packag
2022-10-04:以下go语言代码输出什么?A:{123} main.T{x:123} B:{123} T{x:123} C:boo boo D:boo main.T{x:123}. packag ...
- vue小坑之Vetur报错:相对路径报错
话不多说先上图 俗话说:面向百度编程,这话是没错滴,找不到相同问题的博客至少你还可以找谷歌翻译 以上图片问题就是:你导入的组件的相对路径不对.(有可能是你手动敲进去的,然后vetur这边检测不到) 解 ...