G8.3 实现不可逆加密 - 用Go语言 Golang实现

陆满庭 · · 1148 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前两节介绍的加密方法都属于“可逆”的加密算法,因为加密后的密文经过解密的过程就可以还原出原文。还有一类加密算法属于“不可逆”的加密算法,是指一般无法进行正常解密还原出原文的加密算法。


不可逆加密算法的应用场景也有很多,最典型的是服务端保存用户登录密码的方式。从保护用户隐私角度考虑,服务器端不应该存储用户的登录密码原文,也不应该保存可逆加密后的密文(因为也可以被解密还原),这时候实践中常用的方法是在服务器保存密码时将其用不可逆的算法转换成密文存储,下次用户登录时服务器将发来的用户密码以同样的不可逆算法加密后再与保存的密码密文做比对来判断是否相同。


从这个例子也可以看出,不可逆加密算法一般要满足这个要求:同样的明文用同样的方式加密后,得到的密文必须是一样,否则就无法用于比对了。


最简单的不可逆加密算法可能就是取模算法了,例如18 % 5 的结果是3,而23 % 5的结果也是3,如果18和23代表明文,“% 5”这个取模操作(也就是求对于5的余数)代表加密算法,3代表加密后的密文,那么显然这个加密算法是不可逆的,因为从密文“3”无法确定原文是18还是23(甚至还可能是其他更多的数字),而任何数字对5取模的结果都是不变的。那么该算法如果用于对密码加密,可以将密码转换后的字节切片中的每个字节数值对5取模后形成新的密文字节切片,服务器端保存这个密文就可以了,以后用户登录时传上来的密码做同样的加密操作,得到的结果与保存的密文应该是一致的。


当然,取模操作作为加密方法来说加密强度太低,实际应用中用的最多的不可逆加密算法是MD5算法,该算法应用了散列函数的原理,可以将任何数据转换成固定长度的字节序列(一般是16个字节),并且与取模操作一样具有原文对应到密文的唯一性。


取模操作也可以看做散列函数的一种,可以将任何数据散列到[0, n)的范围内(n是取模的模数)。散列函数一般都具备这样的特性:原始数据与散列后的数据是多对一的关系,即同样的原始数据用同样的散列函数处理后必然得到同样的结果,但同样的结果不一定对应同一个原始数据。这也是为什么大多数不可逆算法使用散列函数作为核心方法的原因。


下面的代码演示了如何使用Go语言中的crypto/md5等包来实现MD5加密算法。


package main


import (

  "crypto/md5"

  "encoding/hex"

  "io"

  "log"

  "os"

  "strings"

  t "github.com/topxeq/goexamples/tools"

)


func main() {


  //原始字符串

  originalTextT := "测试字符串"


  //对原始字符串生成md5码,md5BytesArrayT是[16]byte类型的数组

  md5BytesArrayT :=md5.Sum([]byte(originalTextT))


  //将数组转换为切片

  md5BytesT := md5BytesArrayT[:]


  t.Printfln("md5字节切片(16个字节): %#v", md5BytesT)


  //将md5字节切片转换为16进制的文本,将有32个字符

  //并转换为全大写字母

  md5TextT := strings.ToUpper(hex.EncodeToString(md5BytesT))


  t.Printfln("md5文本(32位): %#v", md5TextT)


  //用流式方法对字符串进行md5编码

  md5Encoder := md5.New()


  //向md5Encoder中写入字符串

  io.WriteString(md5Encoder, originalTextT)


  //调用Sum函数进行md5编码,并转换为十六进制字符串

  md5TextT = hex.EncodeToString(md5Encoder.Sum(nil))


  t.Printfln("流式编码的md5文本(小写): %#v", md5TextT)


  //直接用流式方法对一个文件进行md5编码

  fileT, errT := os.Open(`c:\test\long.txt`)


  if errT != nil {

       log.Fatal(errT)

  }


  defer fileT.Close()


  md5Encoder = md5.New()


  //带有初始化语句的条件判断

  if _, errT = io.Copy(md5Encoder, fileT); errT!= nil {

       log.Fatal(errT)

  }


  t.Printfln("文件的md5码:%x", md5Encoder.Sum(nil))

}

代码 14‑3 MD5加密示例


代码14‑3中已经有详尽的解释,再附加几点解释如下:


-> 代码中用到的md5包中的函数最主要的是md5.Sum函数,该函数实现了将任意一个字节切片编码为MD5,编码结果的数据类型是[16]byte,即16个字节的字节数组;

-> 由于字节数组与字节切片并不是同一个类型,所以还需要进行转换,将[16]byte类型的数据转换为[]byte类型才能符合后面hex.EncodeToString等函数的传入参数类型要求;

-> 目前一般使用时都是用MD码的十六进制文本形式,这会有32个字符长度;

-> 代码后面也演示了如何用流式方法来直接编码一个字符串为MD码以及直接编码一个文件;md5.New函数将新建一个MD5的编码器(hash.Hash类型),该编码器支持流式写入,写入完所有内容后,调用该编码器的Sum函数就会进行MD5编码;


代码14‑3的运行结果是:


md5字节切片(16个字节): []byte{0x1f, 0x3c, 0xa0, 0x51, 0x2, 0x8d, 0x1d, 0x1e, 0x95, 0xa6, 0xf4, 0xe2, 0x69, 0xd7, 0x27, 0xab}

md5文本(32位): "1F3CA051028D1D1E95A6F4E269D727AB"

流式编码的md5文本(小写): "1f3ca051028d1d1e95a6f4e269d727ab"

文件的md5码:9da81dede7a381a6d9fecc0cd69a81a9


可以看出,无论使用普通方式还是流式方式进行MD5编码,对同样的输入字符串的结果都是一样的(注意,十六进制文本的大小写一般在各个系统中都可以识别,目前的趋势更多的是使用小写形式)。


类似MD5这样的不可逆加密,也不需要使用密码就可以直接进行加密。


最后顺便提一下,任何加密理论上都可以被破解,即使是不可逆的加密算法也是有办法破解的。MD5的破解方法已经在网络上有所流传,实质上是穷举法的破解方式,就是不停地收集和计算各种明文的MD5编码并将这两者的成对保存起来,然后根据保存的海量数据,对MD5密文进行反查,这需要大量的积累,消耗的资源也较大,查出来的结果也可能是多个(MD5的密文与明文是一对多的关系),但确实也算是一种方法,在此说明一下供大家了解。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:陆满庭

查看原文:G8.3 实现不可逆加密 - 用Go语言 Golang实现

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1148 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传