加密算法浅析

加密算法浅析

前言

数字签名信息加密 是前后端开发都经常需要使用到的技术,应用场景包括了用户登入、交易、信息通讯、oauth 等等,不同的应用场景也会需要使用到不同的签名加密算法,或者需要搭配不一样的 签名加密算法 来达到业务目标。

加密算法介绍

一般来说加密算法大体分成两类,对称加密非对称加密

对称加密

img

对称加密简单来说就是双方拿着一个相同的密钥对密文进行加密解密。这就相当于发电报,无论是发电报的人还是接受电报的人都需要拿到那个密码本才能知道对方想要发送的信息。

  1. 数据加密过程:在对称加密算法中,数据发送方明文 (原始数据) 和 加密密钥 一起经过特殊 加密处理,生成复杂的 加密密文 进行发送。
  2. 数据解密过程:数据接收方 收到密文后,若想读取原数据,则需要使用 加密使用的密钥 及相同算法的 逆算法 对加密的密文进行解密,才能使其恢复成 可读明文

非对称加密

img

非对称加密如图所示,需要有两个不同的密钥(公钥和私钥)。因为 加密解密 使用的是两个不同的密钥,所以这种算法称为 非对称加密算法

在这个算法中,发送方如果使用公钥对密文加密,接收方则需要使用私钥对已加密的密文进行解密。举个例子就是,你是接受密文的人,你需要把你的公钥给发送密文的人,然后当他发送密文时,你拿出你好生保管的私钥进行解密。当然,也可以反过来用私钥加密公钥解密。

  1. 如果使用 公钥 对数据 进行加密,只有用对应的 私钥 才能 进行解密
  2. 如果使用 私钥 对数据 进行加密,只有用对应的 公钥 才能 进行解密

一些题外话

hash算是加密算法吗?

hash其实从严格意义上来说并不是一种加密算法。因为加密应该是可以根据加密后的数据还原的,但hash是不可逆的。

(所以文章题目不要太在意)

base 64 算是加密算法吗?

一般来说并不认为它是一个加密算法,而是认为它只是一种编码方式。

常用密码加密算法介绍及在golang中的使用

什么是hash

(摘自百度百科)一般翻译做散列、杂凑,音译为哈希,是把任意长度的输入通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。

Hash有哪些流行的算法

目前流行的 Hash 算法包括 MD5SHA-1 SHA-2

MD4(RFC 1320)是 MIT 的 Ronald L. Rivest 在 1990 年设计的,MD 是 Message Digest 的缩写。其输出为 128 位。MD4 已证明不够安全。

MD5(RFC 1321)是 Rivest 于1991年对 MD4 的改进版本。它对输入仍以 512 位分组,其输出是 128 位。MD5MD4 复杂,并且计算速度要慢一点,更安全一些。MD5 已被证明不具备”强抗碰撞性”。

SHA (Secure Hash Algorithm)是一个 Hash 函数族,由 NIST(National Institute of Standards and Technology)于 1993 年发布第一个算法。目前知名的SHA-1在 1995 年面世,它的输出为长度 160 位的 hash 值,因此抗穷举性更好。SHA-1 设计时基于和 MD4 相同原理,并且模仿了该算法。SHA-1 已被证明不具”强抗碰撞性”。

为了提高安全性,NIST 还设计出了 SHA-224SHA-256SHA-384,和 SHA-512 算法(统称为 SHA-2),跟 SHA-1 算法原理类似。SHA-3 相关算法也已被提出。

可以看出,上面这几种流行的算法,它们最重要的一点区别就是”强抗碰撞性”。

什么是hash算法的碰撞

其实就相当于是hash冲突。因为作为一种可用的散列算法,其位数一定是有限的,也就是说它能记录的文件是有限的——而文件数量是无限的,两个文件指纹发生碰撞的概率永远不会是零。

但这并不意味着散列算法就不能用了,因为凡事都要考虑代价,买光所有彩票去中一次头奖是毫无意义的。现代散列算法所存在的理由就是,它的不可逆性能在较大概率上得到实现,也就是说,发现碰撞的概率很小,这种碰撞能被利用的概率更小。

MD 5

MD 5不仅可以用来对密码加密,同时也可以用来生成文件指纹。

MD 5 有很多种方法可以破解,不过需要明确一点,这里所谓的破解,并非把密文还原成原文。对于 MD 5 的破解,实际上都属于碰撞。比如原文 A 通过 MD 5 可以生成密文 Y,我们并不需要把 Y 还原成 A,只需要找到原文 B,生成同样的密文 Y 即可。

MD 5 破解的方法有很多:

  • 暴力枚举法:简单粗暴地枚举出所有原文,并计算出它们的哈希值,看看哪个哈希值和给定的密文一致。(破解时间长)。
  • 字典法:黑客利用一个巨大的字典,存储尽可能多的原文和对应的哈希值。每次用给定的密文摘要查找字典,即可快速找到碰撞的结果。(存储空间大)。
  • 彩虹表法:组合了暴力枚举法和字典法,并在这两者之中取得一个折中,用我们可以承受的时间和存储空间进行破解.
  • 差分攻击:差分攻击是通过比较分析有特定区别的明文在通过加密后的变化传播情况来攻击密码算法的。

  虽然彩虹表有着非常惊人的破解效率,但我们仍然有办法防御彩虹表。最有效的方法就是加盐: 在密码学中加盐是指通过在密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符,这种过程称之为加盐。加盐后的密码经过哈希加密得到的哈希串与加盐前的哈希串完全不同,因此加盐可以大大降低密码泄露的概率。

密码加密示例代码如下

1
2
3
4
5
6
7
8
const salt = "xxxxx" // 自己定义的盐
// 加密
func encryption(password string) string {
h := md5.New()
h.Write([]byte(salt))
h.Write([]byte(password))
return fmt.Sprintf("%x", string(h.Sum(nil)))
}

文件指纹示例代码如下

一般来说用这种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func planA() {
f, err := os.Open(path)
if err != nil {
fmt.Println("Open", err)
return
}

defer f.Close()

body, err := ioutil.ReadAll(f)
if err != nil {
fmt.Println("ReadAll", err)
return
}

md5.Sum(body)
fmt.Printf("%x\n", md5.Sum(body))
}

在文件过大时用如下方法会更有效率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func planB() {
f, err := os.Open(path)
if err != nil {
fmt.Println("Open", err)
return
}

defer f.Close()

md5hash := md5.New()
if _, err := io.Copy(md5hash, f); err != nil {
fmt.Println("Copy", err)
return
}

md5hash.Sum(nil)
fmt.Printf("%x\n", md5hash.Sum(nil))
}

sha1

都什么年代了还在用sha1加密

(摘自百度百科) SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160(20字节)散列值,散列值通常的呈现形式为40个十六进制数。 [1]

SHA-1已经不再视为可抵御有充足资金、充足计算资源的攻击者。2005年,密码分析人员发现了对SHA-1的有效攻击方法,这表明该算法可能不够安全,不能继续使用,自2010年以来,许多组织建议用**SHA-2或SHA-3来替换SHA-1**。MicrosoftGoogle以及Mozilla都宣布,它们旗下的浏览器将在2017年前停止接受使用SHA-1算法签名的SSL证书

2017年2月23日,CWI Amsterdam与Google宣布了一个成功的SHA-1碰撞攻击,发布了两份内容不同但SHA-1散列值相同的PDF文件作为概念证明。

在golang中sha1的使用与md5差不多,这里不进行赘述。

BCrypt

本文重头戏,bcrypt算法。

bcrypt 有三个特点:

  • 每一次 Hash 出来的值不一样。
  • 计算非常缓慢。
  • 每次的 salt 是随机生成的,不用担心 salt 会泄露。

Bcrypt是单向Hash加密算法,类似Pbkdf2算法 不可反向破解生成明文

高情商:自己寻找的知识最巩固,所以更多特点可以自己查。

低情商:我懒得复制粘贴过来了。

总的来说相对来说这个算法是非常安全的。

示例代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 加密密码
func HashAndSalt(pwdStr string) (pwdHash string, err error) {
pwd := []byte(pwdStr)
hash, err := bcrypt.GenerateFromPassword(pwd, bcrypt.MinCost)
if err != nil {
return
}
pwdHash = string(hash)
return
}

// 验证密码
func ComparePasswords(hashedPwd string, plainPwd string) bool {
byteHash := []byte(hashedPwd)
bytePwd := []byte(plainPwd)
err := bcrypt.CompareHashAndPassword(byteHash, bytePwd)
if err != nil {
return false
}
return true
}

一些参数说明

bcrypt.MinCost :你期望的加密最小成本(成本越高,加密的越严实,破解难度就越大,但运算也会越慢)

参考

常见加密算法原理及概念 - 知乎 (zhihu.com)

对称加密与非对称加密 - 知乎 (zhihu.com)

浅谈常见的七种加密算法及实现 - 知乎 (zhihu.com)

老听别人说加密算法,现在给你个机会也了解下 - 知乎 (zhihu.com)

SHA-1_百度百科 (baidu.com)

Golang 获取文件 md5 校验和的方法及效率比较

Bcrypt加密之新认识 - 简书 (jianshu.com)

拓展

密码学原语如何应用?解析单向哈希的妙用 - 知乎 (zhihu.com)