在Go项目中封装AES加解密客户端接口

1.摘要

在一个中型以上的项目中, 我们一般会在项目工程中开辟一个pkg文件夹用来存放一些基础工具接口,比如:数据库、中间件、加解密算法、基础协议等等。在这篇文章中, 我主要分享一下在基于Go语言的项目中, 加解密算法中如何封装一个通用的加解密接口, 并以使用比较广泛的AES加解密算法实现为基础进行讲解, 最后模拟客户端分别演示调用AES的加密接口和解密接口。

2.工程文件结构

在一个正规项目中, 我们要封装的文件主要添加在算法文件夹下, 目录结构规划如下:

pkg|---- algorithm|---- base.go        // 基础接口函数定义|---- aes.go         // aes加解密算法接口 |---- aes_test.go    // aes加解密算法接口函数测试

我在名为"algorithm"文件夹下新建了三个文件, 其中base.go为基础接口函数定义, 因为以后可能要加入进来的算法会比较多,因此需要有一个基础类文件来定义通用函数接口。

aes.go文件中主要实现AES算法的加解密过程, 并提供一个对外的初始化接口,方便应用层调用。

aes_test.go是作为单元测试的文件, 在里面可以针对AES加密函数和解密函数写测试用例, 不用编译整个工程实现单元测试。

如果后面有新的算法加入进来, 例如:des算法, 只需要添加一个des.go和des_test.go文件, 在里面实现函数功能即可。

3.基础接口实现

基础接口实现主要在base.go文件中, 因为对于所有加密算法来讲, 都有两个最基础通用的方法:加密函数和解密函数,因此这里定义了两个通用的方法接口:

type IAlgorithm interface {Encrypt() // 加密函数接口Decrypt() // 解密函数接口
}

因为现在不知道项目默认需要使用什么算法,因此实现这两个方法的空接口:

type DefaultAlgorithm struct{}
​
func (dal DefaultAlgorithm) Encrypt() {}
​
func (dal DefaultAlgorithm) Decrypt() {}

考虑在应用层方便切换不同的算法, 这里需要设计一个管理接口的方法, 首先定义一个结构体:

type AlgorithmManager struct {algorithm IAlgorithm
}

在这个结构体中, 成员是上面接口名称的对象。

然后我定义了两个方法, 一个是设置算法对象的方法, 另一个是执行算法方式的方法。

首先是设置算法对象的方法:

func (gor *AlgorithmManager) SetAlgorithm(algorithm IAlgorithm) {gor.algorithm = algorithm
}

这个方法会接收一个参数,这个参数就是用户想要调用哪种算法的对象, 只有给接口赋对应算法的对象,接口才知道调用哪个算法的方法。

其次是运行算法类型的方法:

const (encryptMode = "encrypt"decryptMode = "decrypt"
)
​
func (gor *AlgorithmManager) RunAlgorithm(runMode string) {switch runMode {case encryptMode:gor.algorithm.Encrypt()breakcase decryptMode:gor.algorithm.Decrypt()break}
}

这里我定义了两个模式用来标识加密模式和解密模式, 当给RunAlgorithm传参encryptMode, 则会执行加密函数,反之则执行解密函数。

4.AES加解密算法实现

在AES加解密客户端调用接口中, 我选择了选项设计模式, 用户可以根据加密算法和解密算法参数不同进行灵活的选项传参。

首先定义一个方法结构体:

type AesAlgorithm struct {AppAlg            *AlgorithmManagerEncryptKey        string // 密钥PlaintextContent  string // 明文内容CiphertextContent string // 密文内容
}

在这个结构体中, 密钥、明文内容、密文内容是我们在使用功能过程中必须传入的参数, 其中还带有一个结构对象指针: *AlgorithmManager, 方便我们将AES算法的对象传给接口,让其调用AES的加密方法或解密方法。

其次定义一个方便客户端调用的接口, 并使用动态选项传参,实现代码如下:

type AesAlgorithmOption func(aes *AesAlgorithm)
​
// 用户初始化调用并传参
func NewAesAlgorithm(options ...AesAlgorithmOption) *AesAlgorithm {aesAlg := &AesAlgorithm{AppAlg:            new(AlgorithmManager),EncryptKey:        "",PlaintextContent:  "",CiphertextContent: "",}for _, option := range options {option(aesAlg)}return aesAlg
}
​
// 通过该选项函数传入key
func WithEncryptKey(key string) AesAlgorithmOption {return func(aes *AesAlgorithm) {aes.EncryptKey = key}
}
​
// 通过该选项函数传入明文
func WithPlaintextContent(plainText string) AesAlgorithmOption {return func(aes *AesAlgorithm) {aes.PlaintextContent = plainText}
}
​
// 通过该选项函数传入密文
func WithCiphertextContent(cipherContent string) AesAlgorithmOption {return func(aes *AesAlgorithm) {aes.CiphertextContent = cipherContent}
}

下面我们还实现了两个内部函数,分别是加密和解密过程中需要填充块的实现方法,代码如下:

加密填充块:

func pkcs5Padding(cipherText []byte, blockSize int) []byte {padding := blockSize - len(cipherText)%blockSizepadtext := bytes.Repeat([]byte{byte(padding)}, padding)return append(cipherText, padtext...)
}

解密填充块:

func pkcs5UnPadding(origData []byte) []byte {length := len(origData)unpadding := int(origData[length-1])return origData[:(length - unpadding)]
}

最后实现了加密接口函数和解密接口函数,代码如下:

加密接口函数实现:

func (aalg *AesAlgorithm) Encrypt() {tmpKeys := []byte(aalg.EncryptKey)tmpPlaintext := aalg.PlaintextContentblock, err := aes.NewCipher(tmpKeys)if err != nil {fmt.Println("aes加密失败,原因:" + err.Error())return}blockSize := block.BlockSize()origData := pkcs5Padding([]byte(tmpPlaintext), blockSize)
​blockMode := cipher.NewCBCEncrypter(block, tmpKeys[:blockSize])crypted := make([]byte, len(origData))blockMode.CryptBlocks(crypted, origData)aalg.CiphertextContent = hex.EncodeToString(crypted)
}

解密接口函数实现:

func (aalg *AesAlgorithm) Decrypt() {tmpKeys := []byte(aalg.EncryptKey)cryptedByte, _ := hex.DecodeString(aalg.CiphertextContent)block, err := aes.NewCipher(tmpKeys)if err != nil {fmt.Println("aes解密失败,原因:" + err.Error())return}blockSize := block.BlockSize()blockMode := cipher.NewCBCDecrypter(block, tmpKeys[:blockSize])origin := make([]byte, len(cryptedByte))blockMode.CryptBlocks(origin, cryptedByte)decryptStrings := pkcs5UnPadding(origin)aalg.PlaintextContent = string(decryptStrings)
}

5.AES加密函数验证

我在aes_test.go中实现加密函数测试模块:TestEncrypt(t *testing.T), 代码如下:

func TestEncrypt(t *testing.T) {aesAlg := NewAesAlgorithm(WithEncryptKey("ZEplYJFPLlhhMaJI"),WithPlaintextContent("qYWwo7!!Eq-TX3q"),)aesAlg.AppAlg.SetAlgorithm(aesAlg)aesAlg.AppAlg.RunAlgorithm("encrypt")fmt.Println(aesAlg.CiphertextContent)
}

在上面的代码中, 我们调用了AES算法的对外统一接口函数:NewAesAlgorithm, 并分别调用WithEncryptKey和WithPlaintextContent传入了Key内容和明文内容, 并调用接口管理方法:SetAlgorithm进行对象赋值, 最后调用RunAlgorithm("encrypt")方法进行AES加密,实际结果如下:

6.AES解密函数验证

同样在aes_test.go中实现加密函数测试模块:TestDecrypt(t *testing.T), 代码如下:

func TestDecrypt(t *testing.T) {aesAlg := NewAesAlgorithm(WithEncryptKey("ZEplYJFPLlhhMaJI"),WithCiphertextContent("31404e2eb60e2d16faae152106882f4b"),)aesAlg.AppAlg.SetAlgorithm(aesAlg)aesAlg.AppAlg.RunAlgorithm("decrypt")fmt.Println(aesAlg.PlaintextContent)
}

在上面的代码中, 我们调用了AES算法的对外统一接口函数:NewAesAlgorithm, 并分别调用WithEncryptKey和WithCiphertextContent传入了Key内容和上面加密的密文内容, 并调用接口管理方法:SetAlgorithm进行对象赋值, 最后调用RunAlgorithm("decrypt")方法进行AES解密,实际结果如下:

可以看到,成功解密出密文且跟加密时传入的明文一致,解密正确。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/119753.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

机器学习2(Numpy)

1、numpy ndarray 案例演示 可以在创建的时候就指定元素类型 生成0/1数组 从现有数组中生成 生成固定数组 生成随机数组

如何将 huggingface上的模型文件下载到本地

写在前面 缘由:国内的GPU服务器直接调取 huggingface 上模型经常会失败,因此下载到本地就能免去许多麻烦。 方法三基于知乎上一位博主所提出方法的基础上进行改进,可以将huggingface上模型由 Colab 存进 谷歌云盘 或者 百度云盘。特别是有些…

项目管理之如何有效定义项目目标

项目目标管理是项目管理中非常重要的一个环节,它可以帮助项目团队明确目标,制定合理可行的计划,确保项目顺利实施并取得成功。在定义项目目标时,需要遵循SMART原则,确保目标具体、明确、可衡量、可实现、相关且有时间和…

基于ssm的宠物医院管理系统的设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:Vue 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…

简单8位CPU设计verilog微处理器,源码/视频

名称:8位CPU设计微处理器 软件:QuartusII 语言:Verilog 代码功能: 设计一个简单的处理器,可以实现加减法以及简单的逻辑运算。 设计包括程序计数器电路,指令存储器电路,指令译码器电路(控制器…

nginx浏览器缓存和上流缓存expires指令_nginx配置HTTPS

1.nginx控制浏览器缓存是针对于静态资源[js,css,图片等] 1.1 expires指令 location /static {alias/home/imooc;#设置浏览器缓存10s过期expires 10s;#设置浏览器缓存时间晚上22:30分过期expires @22h30m;#设置浏览器缓存1小时候过期expires -1h;#设置浏览器不缓存expires …

NSSCTF做题第9页(2)

[SWPUCTF 2022 新生赛]ez_1zpop <?php error_reporting(0); class dxg { function fmm() { return "nonono"; } } class lt { public $impohi; public $md51weclome; public $md52to NSS; function __construct() { $this-&…

springboot + redis实现签到与统计功能

在很多项目中都会有签到与统计功能&#xff0c;最容易想到的方案是创建一个签到表来记录每个用户的签到记录&#xff0c;比如设计一个mysql数据库表&#xff1a; CREATE TABLE tb_sign id bigint(20) unsigned NOT NULL AUTOINCREMENT COMMENT 主键, user_id bigint(20) unsig…

【Java技术专题】「入门到精通系列教程」深入探索Java特性中并发编程体系的原理和实战开发指南( 实现可伸缩IO专题)— 上

深入探索Java特性中并发编程体系的原理和实战开发指南&#xff08; 实现可伸缩IO专题&#xff09; 总体内容概览可扩展的网络服务分布式对象传统的阻塞式网络服务每个请求或连接可以在独立的线程中进行处理Server服务处理请求类Handler处理逻辑类优点缺点 可扩展性目标平稳降级…

MySQL Join 类型

文章目录 1 Join 类型有哪些2 Inner Join3 Left Join4 Right Join5 Full Join 1 Join 类型有哪些 SQL Join 类型的区别 Inner Join: 左,右表都有的数据Left Join: 左表返回所有的行, 右表没有的补充为 NULLRight Loin: 右表返回所有的行, 左表没有的补充为 NULLFull Outer J…

Excel多线程导入数据库

文章目录 Excel多线程导入数据库1. CountDownLatch2.多线程导入数据库 Excel多线程导入数据库 书接上文 Excel20w数据5s导入 1. CountDownLatch CountDownLatch 维护了一个计数器&#xff0c;初始值为指定的数量。当一个或多个线程调用 await() 方法时&#xff0c;它们会被阻…

redis 配置主从复制,哨兵模式案例

哨兵(Sentinel)模式 1 . 什么是哨兵模式&#xff1f; 反客为主的自动版&#xff0c;能够自动监控master是否发生故障&#xff0c;如果故障了会根据投票数从slave中挑选一个 作为master&#xff0c;其他的slave会自动转向同步新的master&#xff0c;实现故障自动转义 2 . 原理…

简析新能源汽车充电桩设计与应用

叶根胜 安科瑞电气股份有限公司 上海嘉定 201801 摘要&#xff1a;本文针对新能源汽车充电桩建设工作进行探究&#xff0c;采用案例分析法、文献查阅法&#xff0c;指出了新能源汽车充电桩建设存在的问题&#xff0c;阐述了充电桩建设与优化的对策。研究表明&#xff1a;目前…

element-ui的日历组件el-calendar高度咋调小

最近项目首页有个空余 不知道放啥 打算放个日历card 充充位置&#xff0c; el-calendar日历组件的整体宽度可以用el-row el-col :gutter :span来控制自适应 但是官网文档没说高度咋缩小 细长一条好难看 自己尝试改了改element的样式没整出来 最后照着这位博主的方法改是好使滴…

软考系列(系统架构师)- 2014年系统架构师软考案例分析考点

试题一 软件架构&#xff08;MYC 架构、扩展接口模式&#xff09; MVC架构风格最初是Smalltalk-80中用来构建用户界面时采用的架构设计风格。其中M代表模型&#xff08;Model)&#xff0c;V代表视图&#xff08;View)&#xff0c;C代表控制器&#xff08;Controller)。在该风格…

动态规划(记忆化搜索)

AcWing 901. 滑雪 给定一个 R行 C 列的矩阵&#xff0c;表示一个矩形网格滑雪场。 矩阵中第 i 行第 j 列的点表示滑雪场的第 i 行第 j 列区域的高度。 一个人从滑雪场中的某个区域内出发&#xff0c;每次可以向上下左右任意一个方向滑动一个单位距离。 当然&#xff0c;一个人能…

liunx Centos-7.5上 rabbitmq安装

在安装rabbitmq中需要注意&#xff1a; 1、rabbitmq依赖于erlang&#xff0c;需要先安装erlang 2、erlang和rabbitmq版本有对应关系 可参考网页&#xff1a;https://www.rabbitmq.com/which-erlang.html 第一步&#xff0c;安装编译工具及库文件,如果服务器上已经有了&…

长连接的原理

Apollo的长连接实现是 Spring的DeferredResult来实现的,先看怎么用 import ...RestController RequestMapping("deferredResult") public class DeferredResultController {private Map<String, Consumer<DeferredResultResponse>> taskMap new HashMa…

SpringBoot_minio sdk使用自签名https证书错误处理

minio sdk使用自签名https证书错误处理 1.问题描述1.1 报错日志1.2 maven 依赖配置1.3 当前spring MinioClient配置 2.问题分析3.问题解决3.1 使用受信任的证书3.2 忽略证书验证3.2.1 minio客户端3.2.2 minio sdk 忽略证书验证3.2.2.1 拓展: 补充minioclient请求日志 4. 问题总…

树形数据增删改查

功能描述&#xff1a; 默认展示所有项目点击项目展示当前项目下的所有区域点击区域展示当前区域下的所有工位以上以树形图格式展示项目&#xff0c;区域&#xff0c;和工位都可进行增加 修改 和删除&#xff0c;每个图标hover时显示对应提示信息项目&#xff0c;区域&#xff…