Encryption

Encryption

Encryption

加密,encryption,当我们开发系统或者应用或者传递隐秘信息的时候,我们不希望监听者很方便的看到,这个时候我们引入

Category(分类)

可逆向加密,不可逆加密(单向加密),其中可逆加密算法包括对称加密和非对称加密,不可逆加密包括md5,sha, hmac等

Symmetric key(对称密钥)

the encryption and decryption keys are the same

加密和解密的私钥是一样的,类似电台收到消息用密码本解密,密码本默认是一样的

Symmetric-key algorithm(对称加密算法)
Types(种类)
  • Stream ciphers

根据每个数据流或者字节为单位, eg.ChaCha20

  • Block ciphers

把一定bit位的信息看成一个单位, 对数据信息进行分片 eg.AES(Advanced Encryption Standard) use 128bit blocks.

Implementations(常见的对称加密算法)
  • Twofish
  • Serpent
  • AES (Rijndael)
  • Camellia
  • Salsa20
  • ChaCha20
  • Blowfish
  • CAST5
  • Kuznyechik
  • RC4
  • DES
  • 3DES
  • Skipjack
  • Safer
  • IDEA
Public key also Asymmetric-key(公钥加密)

任何人可以获取到公钥,但是只有拥有私钥的一方才可以进行解密

加密过程

d(c(x)) = x 发送端使用公钥加密x得到c(x),接收端用对应的私钥解密d(c(x))得到x

Implementations(常见的公钥加密算法)
  • RSA
  • ELGamal
  • Rabin(RSA的特例)
  • DSA
  • ECDSA
PKI(公开密钥基础建设)
  • Certificate Authority, CA数字证书认证机构

负责管理证书发放的权威机构,承担公钥体系的合法校验的责任

PKI通过CA把个人身份和公钥密钥链接在一起,链接关系在注册和发布的时候被创建,(我们获取到完整证书通常比较长,有一个证书链的逻辑) 管理这个链的角色叫RA(Registration Authority), RA保证公开密钥和个人身份链接,可以防抵赖。

数字签名

和常见的私钥用来解密不同,数字签名使用私钥加密,公布公钥,接收方用公钥进行解密 为了防止公钥篡改等,证明公钥确实是自己的,需要第三方机构来提供一个大家都认可的公钥系统

https证书
  1. 以购买一个证书为例,通常对方会让我们自己生成一个私钥和CSR(证书请求) openssl req -new -key xxx.private -out xxx.csr
  2. 对方通过ca对CSR进行签名,并发给我们一个认证后的公钥
  3. 代理进行配置

CA CA负责为用户提供证书(包含公钥和私钥),并添加数字签名防止攻击者篡改,网络用户通过验证CA的签名从而信任CA

非对称加密

如何存储密码

建议看这篇文档 https://willh.gitbook.io/build-web-application-with-golang-zhtw/09.0/09.5

单向加密

把明文做单向加密以后保存加密后的字符串,文中主要说了3中算法, md5, sha256, sha1 但是md5已经在1996年后被算法证实可以被碰撞破解,sha1目前也已经被实测可以被破解,所以建议用sha256

golang里面使用aes加解密

建议看下这篇文档 https://willh.gitbook.io/build-web-application-with-golang-zhtw/09.0/09.6 和这篇https://www.thepolyglotdeveloper.com/2018/02/encrypt-decrypt-data-golang-application-crypto-packages/ (主要参考这篇)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
  package main

  import (
          "fmt"
          "crypto/md5"
          "crypto/aes"
          "crypto/cipher"
          "crypto/rand"
          "io"
          "encoding/hex"
  )

  func createHash(key string) string {
          hashstr := md5.New()
          hashstr.Write([]byte(key))
          return hex.EncodeToString(hashstr.Sum(nil))
  }

  func encrypt(data []byte, passphrase string) []byte {
          block, _ := aes.NewCipher([]byte(createHash(passphrase)))
          gcm, err := cipher.NewGCM(block)

          if err != nil {
                  panic(err.Error())
          }

          nonce := make([]byte, gcm.NonceSize())
          if _, err = io.ReadFull(rand.Reader, nonce);err != nil {
                  panic(err.Error())
          }

          ciphertext := gcm.Seal(nonce, nonce, data, nil)
          return ciphertext
  }

  func decrypt(data []byte, passphrase string) []byte {
          key := []byte(createHash(passphrase))
          block, err := aes.NewCipher(key)
          if err != nil {
                  panic(err.Error())
          }

          gcm, err := cipher.NewGCM(block)
          if err != nil {
                  panic(err.Error())
          }

          nonceSize := gcm.NonceSize()
          nonce, ciphertext := data[:nonceSize], data[nonceSize:]

          plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
          if err != nil {
                  panic(err.Error())
          }

          return plaintext
  }

  func main() {
          encryptr := encrypt([]byte("hello, liuliancao!"),"liuliancao@liuliancao.com")
          fmt.Printf("encrypt strings: %x\n", encryptr)
          plaintext := decrypt([]byte(encryptr), "liuliancao@liuliancao.com")
          fmt.Printf("decrypt strings: %s\n", plaintext)
  }
encrypt strings: c8d88f80e58381377e4b199c507ea35dc0608834289b552e02426b67dbbd38fa30e96d5e5f0f1ca465cc1da5e56f
decrypt strings: hello, liuliancao!

golang使用scrypt加密, 类似sha256

golang里面使用sha256和md5获得单向结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
  package main

  import (
          "crypto/sha256"
          "crypto/md5"
          "fmt"
  )

  func main() {
          h := sha256.New()
          h.Write([]byte("wagent@"))

          m := md5.New()
          m.Write([]byte("guarder"))
          fmt.Printf("%x,%d\n", h.Sum(nil),len(h.Sum(nil)))
          fmt.Printf("%x,%d\n", m.Sum(nil),len(m.Sum(nil)))
  }
0495583f3c1eba0aa3c57f644a63f96bbc16f3e920c765afa51e7c947b3581b3,32
277f1c4abd678b6512518f5824bc5d58,16

我找了几个在线解密网站发现,对于全字符串的,都保存了字典,所以如果一旦被发现,非常容易撞库, 比较好的办法是网络上加密比如通过隧道,或者应用层比如通过https证书,或者额外编写加密协议,然 后密码强制要求使用一些特殊字符,这样破解难度会非常大

ssh连接过程

首次连接的时候密钥交换

一个典型的场景是ssh密钥第一次交换的时候

客户端和服务端都需要生成自己的公私钥对,服务端authorized_keys里面放了客户端的公钥

  • 客户端对服务端发起ssh请求
  • 服务端返回自己的公钥和一个会话ID,客户端得到服务端公钥
  • 客户端生成密钥对
  • 客户端用自己的公钥异或会话ID, 计算出一个值Res,并且用服务端公钥加密
  • 客户端发送Res给服务端,服务端用自己私钥解密得到Res
  • 服务端用Res值和ID异或得到客户端的公钥

客户端给服务端发送消息通过服务端的公钥进行加密,服务端用自己的私钥进行解密

服务端返回给客户端的消息通过客户端的公钥进行加密, 客户端用自己的私钥进行解密

基于用户名密码的认证方式

  • 客户端连接服务端ssh 22端口,申请连接
  • 服务端返回自己的公钥
  • 客户端用服务端的公钥加密自己的密码用户名
  • 加密后的消息回传给服务器,服务器使用自己的私钥进行解密,密码正确则登录成功

基于密钥认证

  • 客户端生成自己的公私钥对
  • 客户端通过ssh-copy-id或者服务端把客户端公钥加到authorized_keys
  • 服务端收到用户的请求会到authorized_keys里面查找,如果有对应的ip和用户,就会生成一个字符串x
  • 服务端使用用户的公钥进行加密字符串x
  • 客户端使用私钥进行解密,并发给服务端
  • 服务端收到客户端的字符串比较,一致则允许此次免密登录