时代新秀golang--golang加密算法之DES

Go语言的DES加密(CBC模式, ECB模式) ---- 与java加密互通

问题场景:

业务需要对接接口, 采用DES加密方式加密, 于是google一下go的DES加密方式,

go的DES的默认隐藏了ECB模式, 因为go认为ECB不安全, 所以不建议使用,就隐藏了,

然而由于历史遗留问题接口却需要采用ECB模式。

ECB

  • 概念

ECB(电子密本方式)就是将数据按照8个字节一段进行DES加密或解密得到一段8个字节的密文或者明文,最后一段不足8个字节,按照需求补足8个字节进行计算,之后按照顺序将计算所得的数据连在一起即可,各段数据之间互不影响。

  • 特点
  1. 简单,有利于并行计算,误差不会被传送;
  2. 不能隐藏明文的模式;在密文中出现明文消息的重复
  3. 可能对明文进行主动攻击;加密消息块相互独立成为被攻击的弱点

CBC

  • 概念

CBC(密文分组链接方式)有向量的概念, 它的实现机制使加密的各段数据之间有了联系。

  • 加密步骤:
  1. 首先将数据按照8个字节一组进行分组得到D1D2......Dn(若数据不是8的整数倍,用指定的PADDING数据补位)
  2. 第一组数据D1与初始化向量I异或后的结果进行DES加密得到第一组密文C1(初始化向量I为全零)
  3. 第二组数据D2与第一组的加密结果C1异或以后的结果进行DES加密,得到第二组密文C2
  4. 之后的数据以此类推,得到Cn
  5. 按顺序连为C1C2C3......Cn即为加密结果。
  • 解密是加密的逆过程:
  1. 首先将数据按照8个字节一组进行分组得到C1C2C3......Cn
  2. 将第一组数据进行解密后与初始化向量I进行异或得到第一组明文D1(注意:一定是先解密再异或)
  3. 将第二组数据C2进行解密后与第一组密文数据进行异或得到第二组数据D2
  4. 之后依此类推,得到Dn
  5. 按顺序连为D1D2D3......Dn即为解密结果。
  • 特点
  1. 不容易主动攻击,安全性好于ECB,适合传输长度长的报文,是SSL、IPSec的标准。

每个密文块依赖于所有的信息明文消息中一个改变会影响所有密文块

  1. 发送方和接收方都需要知道初始化向量
  2. 加密过程是串行的,无法被并行化(在解密时,从两个邻接的密文块中即可得到一个平文块。因此,解密过程可以被并行化。

代码:

//des_ecb_pkcs5padding.go
package main
import (
 "bytes"
 "crypto/des"
 "encoding/base64"
 "errors"
)
//明文补码算法
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
 padding := blockSize - len(ciphertext) % blockSize
 padtext := 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 ZeroPadding(ciphertext []byte, blockSize int) []byte {
 padding := blockSize - len(ciphertext)%blockSize
 padtext := bytes.Repeat([]byte{0}, padding)
 return append(ciphertext, padtext...)
}
func ZeroUnPadding(origData []byte) []byte {
 return bytes.TrimFunc(origData,
 func(r rune) bool {
 return r == rune(0)
        })
}
func DesEncrypt(src, key []byte) ([]byte, error) {
 block, err := des.NewCipher(key)
 if err != nil {
 return nil, err
    }
 bs := block.BlockSize()
 //对明文数据进行补码
 src = PKCS5Padding(src, bs)
 if len(src) % bs != 0 {
 return nil, errors.New("Need a multiple of the blocksize")
    }
 out := make([]byte, len(src))
 dst := out
 //对明文按照blocksize进行分块加密
 //必要时可以使用go关键字进行并行加密
 for len(src) > 0 {
        block.Encrypt(dst, src[:bs])
 src = src[bs:]
 dst = dst[bs:]
    }
 dstBase64 := make([]byte, base64.StdEncoding.EncodedLen(len(out)))
    base64.StdEncoding.Encode(dstBase64, out)
 return dstBase64, nil
}
func DesDecrypt(src, key []byte) ([]byte, error) {
 srcBase64 := make([]byte, base64.StdEncoding.DecodedLen(len(src)))
 n, err := base64.StdEncoding.Decode(srcBase64, src)
 if err != nil {
 return nil, err
    }
 srcUnBase64 := srcBase64[:n]
 block, err := des.NewCipher(key)
 if err != nil {
 return nil, err
    }
 out := make([]byte, len(srcUnBase64))
 dst := out
 bs := block.BlockSize()
 if len(srcUnBase64) % bs != 0 {
 return nil, errors.New("crypto/cipher: input not full blocks")
    }
 for len(srcUnBase64) > 0 {
        block.Decrypt(dst, srcUnBase64[:bs])
 srcUnBase64 = srcUnBase64[bs:]
 dst = dst[bs:]
    }
 out = PKCS5UnPadding(out)
 return out, nil
}
//main.go 使用DES算法来加密电话号码
package main
import (
 "bufio"
 "flag"
 "fmt"
 "io"
 "os"
 "runtime"
 "sync"
)
const (
 GPNUM = 1000
 //key只能是8位,不能少于8位也不能多于8位.
 KEY = "ImDesKey"
)
var (
 inputFile, outputFile string
)
type outputWriter struct {
    file *os.File
    fWriter *bufio.Writer
    rwMutex *sync.RWMutex
ch chan []byte 
wg *sync.WaitGroup
}
func (ow *outputWriter) Write(row []byte) {
    ow.rwMutex.Lock()
    ow.fWriter.Write(row)
    ow.rwMutex.Unlock()
}
func (ow *outputWriter) Flush() {
    ow.rwMutex.Lock()
    ow.fWriter.Flush()
    ow.rwMutex.Unlock()
}
func (ow *outputWriter) Close() {
    ow.file.Close()
}
func NewOutputWriter(dstFile string, gp int) (*outputWriter, error) {
 f, err := os.OpenFile(dstFile,os.O_WRONLY|os.O_CREATE|os.O_TRUNC,0644)
 if err != nil {
 return nil, err
    }
 w := bufio.NewWriter(f)
ch := make(chan []byte, gp)
return &outputWriter{file:f, fWriter:w, rwMutex:&sync.RWMutex{}, ch:ch, wg:&sync.WaitGroup{}}, nil
}
func init() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    flag.StringVar(&inputFile, "srcfile", "tel.txt", "src file")
    flag.StringVar(&outputFile, "dstfile", "tel_en.txt", "dst file")
    flag.Parse()
}
func concurrentProcess(gp int, src string) {
 //ch := make(chan []byte, gp)
//wg := &sync.WaitGroup{}
 fReader, err := os.Open(src)
 if err != nil {
        fmt.Errorf("Error: %v\n", err)
        os.Exit(1)
    }
 //defer fReader.Close()
 reader := bufio.NewReader(fReader)
 writer, err := NewOutputWriter(outputFile, gp)
 if err != nil {
        fmt.Errorf("Error: %v\n", err)
    }
//defer writer.Close()
 for {
lineBytes, _, err := reader.ReadLine()
 if err == io.EOF {
 break
}
 //tab键的ascii码为9
 //lineBytesSlice := bytes.Split(lineBytes, []byte{9})
 //telBytes := lineBytesSlice[0]
 //provBytes := lineBytesSlice[1]
 //cityBytes := lineBytesSlice[2]
        writer.wg.Add(1)
 go func(ch chan []byte) {
lineWriteBytes := make([]byte,0,45)
telBytes := make([]byte,0,11)
for i:=0;i<=10;i++{
telBytes = append(telBytes, lineBytes[i])
}
//注意:这里不能使用telEnBytes, telEnErr := DesEncrypt(lineBytes[:11], []byte(KEY))来加密电话号码,否则会修改lineBytes[11:]中的内容
telEnBytes, telEnErr := DesEncrypt(telBytes, []byte(KEY))
 if telEnErr != nil {
                fmt.Errorf("Error: %v\n", telEnErr)
 //os.Exit(1)
            }
for i:=0;i<=11;i++ {
lineWriteBytes = append(lineWriteBytes,lineBytes[i])
}
for i:=0;i<=23;i++{
lineWriteBytes = append(lineWriteBytes, telEnBytes[i])
}
for i:=11;i<len(lineBytes);i++{
lineWriteBytes = append(lineWriteBytes, lineBytes[i])
}
//换行的ascii码为10lineBytes[:11]
lineWriteBytes = append(lineWriteBytes,10)
 //writer.Write(lineWriteBytes)
 //writer.Flush()
ch <- lineWriteBytes
 defer writer.wg.Done()
        }(writer.ch)
writer.wg.Add(1)
go func(ch chan []byte) {
writer.Write(<-ch)
writer.Flush()
defer writer.wg.Done()
}(writer.ch)
    }
 //writer.Flush()
    writer.wg.Wait()
    fReader.Close()
writer.Close()
}
func main() {
 concurrentProcess(GPNUM, inputFile)
}
//tel.txt 电话号码文件
13615660000    551    0566 
13665660000    551    0566
13615580000    551    0566
13615670000    551    0566
13615680000    551    0566
13615510000    551    0566
13615600000    551    0566
13615690000    551    0566
13645640000    551    0566
13665540000    551    0566

本文分享自微信公众号 - 糖果的实验室(mycandylab)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-10-24

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我的小碗汤

通过插图学习 Go 语言的并发

你很可能或多或少听说过Go。它越来越受欢迎。Go快速,简单,并且拥有一个很棒的社区。并发模型是学习这门语言最令人兴奋的方面之一。Go的并发原语使创建并发的多线程...

7420
来自专栏戚银技术成长之路

【Go】那么多数值类型,应该选哪个?

Go 内置很多种数值类型,往往初学者不知道编写程序如何选择,使用哪种数值类型更有优势。

7820
来自专栏戚银技术成长之路

【go】一次读锁重入导致的死锁故障

在两天前第一次遇到自己的程序出现死锁, 我一直非常的小心使用锁,了解死锁导致的各种可能性, 这次的经历让我未来会更加小心,下面来回顾一下死锁发生的过程与代码演进...

8820
来自专栏一起学Golang

为什么使用通信来共享内存

『不要通过共享内存来通信,我们应该使用通信来共享内存』,这是一句使用 Go 语言编程的人经常能听到的观点,然而我们可能从来都没有仔细地思考过 Go 语言为什么鼓...

8020
来自专栏java思维导图

图片验证码的需求分析、优雅实现

通常我们最登录的时候,为了防止多次尝试或攻击登录接口,我们需要弄一个验证码的功能,只有输入验证码正确的情况下,我们才会去做密码校验,这样就减少了密码可能会被试出...

8440
来自专栏戚银技术成长之路

【Go】高效截取字符串的一些思考

最近我在 Go Forum 中发现了 [SOLVED] String size of 20 character 的问题,“hollowaykeanho” 给出了...

11320
来自专栏我的小碗汤

手把手教你编写一个operator在中间件容器化中的实践

Operator 是 CoreOS 推出的旨在简化复杂有状态应用管理,它是一个感知应用状态的控制器,通过扩展 Kubernetes API 来自动创建、管理和配...

8310
来自专栏戚银技术成长之路

【Go】strings.Replace 与 bytes.Replace 调优

标准库中函数大多数情况下更通用,性能并非最好的,还是不能过于迷信标准库,最近又有了新发现,strings.Replace 这个函数自身的效率已经很好了,但是在特...

5010
来自专栏戚银技术成长之路

【Go】深入剖析slice和array

array 和 slice 看似相似,却有着极大的不同,但他们之间还有着千次万缕的联系 slice 是引用类型、是 array 的引用,相当于动态数组, 这些都...

6930
来自专栏我的小碗汤

进阶 K8s 高级玩家必备 | Kubebuilder:让编写 CRD 变得更简单

导读:自定义资源 CRD(Custom Resource Definition)可以扩展 Kubernetes API,掌握 CRD 是成为 Kubernete...

29030

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励