DES--------Golang对称加密之模式问题实战

1. 背景

近期项目在对接第三方产品,传输过程中涉及到数据加密,数据加密流程为:

  • 发送数据DES加密
  • DES加密后的数据进行base64编码
  • 发送,接受数据
  • 接受读取的数据进行base64解码
  • base64解码完的数据机型DES解密

    由于采用golang对接,文档且无说明情况下,默认采用CBC模式加解密,导致很长时间对接不上。
    后通过对方Java Demo代码查看得知采用ECB加密模式,Java默认DES算法使用DES/ECB/PKCS5Padding工作方式,在GO语言中因为ECB的脆弱性,DES的ECB模式是故意不放出来的,但实际情况中有时我们并不需要那么安全。

DES介绍

DES 使用一个 56 位的密钥以及附加的 8 位奇偶校验位,产生最大 64 位的分组大小。这是一个迭代的分组密码,使用称为 Feistel 的技术,其中将加密的文本块分成两半。使用子密钥对其中一半应用循环功能,然后将输出与另一半进行"异或"运算;接着交换这两半,这一过程会继续下去,但最后一个循环不交换。DES 使用 16 个循环,使用异或,置换,代换,移位操作四种基本运算。

DES常见加密模式

CBC(加密分组链接模式)

密文分组链接方式,这是golang和.NET封装的DES算法的默认模式,它比较麻烦,加密步骤如下:

  1. 首先将数据按照8个字节一组进行分组得到D1D2......Dn(若数据不是8的整数倍,就涉及到数据补位了)
  2. 第一组数据D1与向量I异或后的结果进行DES加密得到第一组密文C1(注意:这里有向量I的说法,ECB模式下没有使用向量I)
  3. 第二组数据D2与第一组的加密结果C1异或以后的结果进行DES加密,得到第二组密文C2
  4. 之后的数据以此类推,得到Cn
  5. 按顺序连为C1C2C3......Cn即为加密结果。
  • 数据补位一般有NoPadding和PKCS7Padding(JAVA中是PKCS5Padding)填充方式,PKCS7Padding和PKCS5Padding实际只是协议不一样,根据相关资料说明:PKCS5Padding明确定义了加密块是8字节,PKCS7Padding加密快可以是1-255之间。但是封装的DES算法默认都是8字节,所以可以认为他们一样。数据补位实际是在数据不满8字节的倍数,才补充到8字节的倍数的填充过程。

  • NoPadding填充方式:算法本身不填充,比如.NET的padding提供了有None,Zeros方式,分别为不填充和填充0的方式。

  • PKCS7Padding(PKCS5Padding)填充方式:为.NET和JAVA的默认填充方式,对加密数据字节长度对8取余为r,如r大于0,则补8-r个字节,字节为8-r的值;如果r等于0,则补8个字节8。比如:

加密字符串为为AAA,则补位为AAA55555;加密字符串为BBBBBB,则补位为BBBBBB22;加密字符串为CCCCCCCC,则补位为CCCCCCCC88888888。

ECB(电子密码本模式)

电子密本方式,这是JAVA封装的DES算法的默认模式,就是将数据按照8个字节一段进行DES加密或解密得到一段8个字节的密文或者明文,最后一段不足8个字节,则补足8个字节(注意:这里就涉及到数据补位了)进行计算,之后按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。

CFB(加密反馈模式)

加密反馈模式克服了需要等待8个字节才能加密的缺点,它采用了分组密码作为流密码的密钥流生成器;

OFB(输出反馈模式)

与CFB模式不同之处在于,加密位移寄存器与密文无关了,仅与加密key和加密算法有关;
做法是不再把密文输入到加密移位寄存器,而是把输出的分组密文(Oi)输入到一位寄存器;

DES加密之golang的CBC和ECB模式代码实现

CBC和ECB模式加密

func DesECBEncrypt(data,key []byte)([]byte,error) {
    block,err := des.NewCipher(key)
    if err != nil {
        return nil,err
    }
    bs := block.BlockSize()
    data = PKCS5Padding(data,bs)
    if len(data)%bs != 0 {
        return nil,errors.New("Need a multiple of the blocksize")
    }
    out := make([]byte,len(data))
    dst := out
    for len(data) > 0 {
        block.Encrypt(dst,data[:bs])
        data = data[bs:]
        dst = dst[bs:]
    }
    return out,nil
}

func DesCBCEncrypt(origData,key []byte) ([]byte,err
    }
    origData = PKCS5Padding(origData,block.BlockSize())
    // origData = ZeroPadding(origData,block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block,key)
    crypted := make([]byte,len(origData))
    // 根据CryptBlocks方法的说明,如下方式初始化crypted也可以
    // crypted := origData
    blockMode.CryptBlocks(crypted,origData)
    return crypted,nil
}

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

CBC和ECB模式解密

func DesECBDecrypt(data,err
    }
    bs := block.BlockSize()
    if len(data)%bs != 0 {
        return nil,errors.New("crypto/cipher: input not full blocks")
    }
    out := make([]byte,len(data))
    dst := out
    for len(data) > 0 {
        block.Decrypt(dst,data[:bs])
        data = data[bs:]
        dst = dst[bs:]
    }
    out = PKCS5UnPadding(out)
    return out,nil
}

func DesCBCDecrypt(crypted,err
    }
    blockMode := cipher.NewCBCDecrypter(block,key)
    //origData := make([]byte,len(crypted))
    origData := crypted
    blockMode.CryptBlocks(origData,crypted)
    //origData = PKCS5UnPadding(origData)

    origData = PKCS5UnPadding(origData)
    return origData,nil
}

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

总结

以需求驱动技术,技术本身没有优略之分,只有业务之分。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


简介 WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket让客户端和服务端之间的数据交换变得非常简单,且允许服务器主动向客户端推送数据,并且之后客户端和服务端所有的通信都依靠这个专用协议进行。 在websocket出现之前,一些网站为了实现消息的推送,采用最多的技术是A
使用gin框架编写服务端应用,配置路由接收websocket请求并处理。同时实现一个websocket命令行客户端用于与服务端通信。
## 前言 linux自带的crontab默认情况下只能精确到分钟,没法执行秒级任务。当然,也不是不行,比如: ```shell * * * * * for i in $(seq 1 11);do echo hello >> /home/heruos/tmp.txt;sleep 5;do
前言 代码参考自《Building Distributed Application in Gin》 需求:设计一个食谱相关的API,数据存放到切片中。 设计模型和API 模型 type Recipe struct { // 菜品名 Name string `json:"name"
前言 通过钉钉群机器人的webhook,实现消息推送。 本文代码仅示例markdown格式的消息。 示例代码 注意修改钉钉机器人的webhook package main import ( "bytes" "encoding/json" "fmt&q
golang-jwt是go语言中用来生成和解析jwt的一个第三方库,早先版本也叫jwt-go。本文中使用目前最新的v5版本。
## 前言 假设gRPC服务端的主机名为`qw.er.com`,需要为gRPC服务端和客户端之间的通信配置tls双向认证加密。 ## 生成证书 1. 生成ca根证书。生成过程会要求填写密码、CN、ON、OU等信息,记住密码。 ```shell openssl req -x509 -newkey rs
前言 在go语言中,因为字符串只能被访问,不能被修改,所以进行字符串拼接的时候,golang都需要进行内存拷贝,造成一定的性能消耗。 方式1:操作符 + 特点:简单,可读性良好。每次拼接都会产生内存拷贝,性能一般。仅适用于字符串类型的变量。 示例代码: str1 := "hello &qu
前言 正常情况下,主协程一旦退出,其子协程也会全部中止并退出。为了阻塞主协程,可以使用time.Sleep(),也可以使用WaitGroup。 用法说明 // 导入sync import "sync" // 定义一个sync.WaitGroup var wg sync.WaitG
前言 方便在内网环境中获取服务器本机IP,省了在脚本中过滤ip或ifconfig的结果。 如果内网中有nginx的话,通过nginx获取本机IP也很方便,可参考 借助nginx自动获取本机IP 示例代码 package main import ( "fmt" "net&
简介 logrus是一个第三方日志库,性能虽不如zap和zerolog,但方便易用灵活。logrus完全兼容标准的log库,还支持文本、JSON两种日志输出格式。 特点 相较于标准库,logrus有更细致的日志级别,从高到低分别是:trace > debug > info > wa
基于Gin框架编写的Web API,实现简单的CRUD功能,数据存放在MongoDB,并设置Redis缓存。
## 简介 借助 `github.com/hpcloud/tail` ,可以实时追踪文件变更,达到类似shell命令`tail -f`的效果。 ## 示例代码 以下示例代码用于实时读取nginx的`access.log`日志文件,读取到后输出到控制台。如果nginx日志做了json格式化,还可以解析
前言 go在操作MySQL时,可以使用ORM(比如gorm、xorm),也可以使用原生sql。本文以使用sqlx为例,简单记录步骤。 go version: 1.16 安装相关库 # mysql驱动 go get github.com/go-sql-driver/mysql # 基于MySQL驱动的
前言 某次在客户内网传输数据的时候,防火墙拦截了SSH的数据包,导致没法使用scp命令传输文件,tcp协议和http协议也只放开了指定端口,因此想了个用http传输的“曲线救国”方案。 假设要从192.168.1.23传输到192.168.2.34,因防火墙限制,只能从1.23访问2.34,不能从2
前言 go version: 1.18 本文主要包含JSON、Form、Uri、XML的数据解析与绑定。 JSON数据解析与绑定 go代码 package main import ( "net/http" "github.com/gin-gonic/gin"
## 前言 假设一个场景,服务端部署在内网,客户端需要通过暴露在公网的nginx与服务端进行通信。为了避免在公网进行 http 明文通信造成的信息泄露,nginx与客户端之间的通信应当使用 https 协议,并且nginx也要验证客户端的身份,也就是mTLS双向加密认证通信。 这条通信链路有三个角色
使用gin框架编写web程序作为alertmanager的webhook receiver,解析数据并发送到钉钉
前言 多阶段封装docker镜像,使用scratch镜像,尽量减小镜像包的体积。 封装用于编译的go镜像 Dockerfile FROM golang:1.20.1 AS builder WORKDIR /apps COPY . /apps/ ENV CGO_ENABLED=0 ENV GOOS=l
前言 标准库strconv提供了字符串类型与其他常用数据类型之间的转换。 strconv.FormatX()用于X类型转字符串,如strconv.FormatFloat()用于浮点型转字符串。 strconv.ParseX()用于字符串转X类型,如strconv.ParseFloat()用于字符串转