密码学基础

一:对称加密:

1.1: 对称加密/解密三要素:

  • 1: 对称加密三要素: 明文,加密算法, 秘钥。
  • 2: 对称解密三要素:密文,解密算法,秘钥。

1.2:凯撒密码:

  • 凯撒密码加密流程: 将小写字母转化成大写字母,然后向右移动指定位数。
  • 凯撒密码解密流程:将大写字母,向左移动指定位数,然后转化成小写字母。

密码学概述论_数据

1.3:对称加密的流程与特点:

  • 流程:
    • A使用秘钥将明文加密成密文。
    • B使用秘钥将密文解密成明文。
  • 特点:
    • 加密过程只有一把秘钥。
    • 加密效率比非对称加密高。
    • 安全性比非对称加密低。

1.4:编码与加密:

  • 1: 最小单位: bit
    • 1Byte = 8bit
    • 1K = 1024B
    • 1M = 1024K
    • 1G = 1024M
    • 1T = 1024G
    • 1P = 1024T
  • 编码和解码:
    • 编码: 由字符的形式变成二进制的形式。
    • 解码:由二进制的形式变成字符的形式。
  • 加密与解密:
    • 加密:明文比特—>密文比特。
    • 解密:密文比特序列—>明文比特序列。

1.5:分组:

  • 1:明文分组:加密之前的分组。

  • 2:密文分组:加密之后的分组。

  • 3:明文分组和密文分组是等长的。

  • 4: 五中分组模式:

    • ECB : 电子密码本

      • 原理:先将原来的数据进行分组,然后对于分组的数据进行加密。如果分组最后一块有缺失,则会进行数据补充。

      • 缺点:加密不安全。原因:如果是对于分组进行加密则同一个数据加密的结果是固定的,则如果我们使用计算机对于指定的数据加密,很容易找到规律,进行破解。

      • 例如: AAA加密后是CCC, 我们只要得到CCC数据就知道他就是AAA。

      • 特点:

        • 加密效率高,但是加密不彻底。
        • 需要对数据进行分组填充。
        • 每个分组独立进行加密,解密。
        • 只要有一个分组被破解了,则所有的分组都会被破解了。
        • 不使用,GO语言不支持这种分组模式。
    • CBC: 密文分组链接模式(常用)

      • 与或非概念:

        按位操作符号 逻辑操作符号
        & &&
        | ||
        -
      • 按位操作案例:

        A:8,0000,1000

        B : 9, 0000, 1001

        A & B:(与操作:11才是1,其余全0)

        ​ 0000, 1000

        ​ 0000, 1001

        ​ &

        ​ 0000, 1000

      • 异或加密:(相同为0,不同为1)

        0000, 1000 —>明文

        0000, 1001 ---->秘钥

        ​ XOR ---->异或操作

        0000, 0001 ----->密文

        • 对于密文,再次进行异或操作,则可恢复成明文。
      • CBC模式的流程:

        • 1:先对于明文进行分组
        • 2:使用前一组加密的结果与当前组进行异或操作。
        • 3:将异或操作的结果进行加密。
        • 4:第一组加密的时候需要提供一个初始化向量(与分组长度相同的)

      -密码学概述论_加密算法_02

      • CBC加密的特点:

        • 数据长度根据算法而定。
        • 需要提供初始化向量,要求长度必须和分组长度相同。
        • 每一个密文都是下一次加密操作的输入。
        • 不能并行加密,但是可以并行解密。
        • 加密强度高
        • 如果数据不足,需要进行填充。
    • CFB:密文反馈模式

      • 与CBC差不多,只不过是将异或操作与加密操作的顺序倒置了。
      • 由于没有对明文直接进行加密,所以分组时,不需要填充

    密码学概述论_加密算法_03

    • OFB : 输出反馈模式

      • 流程:

        • 1: 先对初始向量进行加密处理(密码)
        • 2:分组后每组的数据与密码进行异或操作。
      • 特点:

        • 分组长度取决于加密算法。
        • 不断对初始向量的输出进行加密,从而得到数据来源。
        • 不需要进行数据填充。
      • 图示:

      密码学概述论_分组长度_04

    • CTR:计数器模式(常用)

      • 加密算法与分组的关系:

        密码学概述论_数据_05

      • 图示

        密码学概述论_分组长度_06

      • 流程:

        • 1: 首先使用计数器生成一个8位的随机数,然后看看有几个分组,后面的计数器执行+1操作。

        • 2:使用加密算法对计数器进行加密。

        • 3:将加密后的密码与明文进行异或操作生成密文。

      • CTR与OFB的区别:

      密码学概述论_加密算法_07

二:对称加密算法:

2.1:DES算法:

  • DES算法介绍:

    • 将64位比特的明文加密成64位比特密文的对称加密算法。
    • 秘钥长度是56比特,少8位的原因是最后一位是校验位。
    • 特点:加密的时候先对数据进行分组,每64位 分一组。
  • 代码:使用DES算法+CBC分组模式:

    • DES算法要求:秘钥:8bytes, 分组长度:8bytes

    • CBC分组模式要求: 初始化向量,长度与密码长度相同:8bytes

    • 实现加密函数的代码:

       package main
      
      import (
      	"bytes"
      	"crypto/cipher"
      	"crypto/des"
      	"fmt"
      )
      
      /*
      需求:使用DES算法和CBC分组模式进行加密
      DES算法:秘钥8bytes,分组长度:8bytes
      CBC分组:提供初始化向量:长度与分组长度相同。
      */
      
      // 加密分析:
      /*
      1: 创建一个实现DES算法的cipher.Block接口, 这个接口返回一个cipter.Block
      	- func NewCipter(key []byte)(cipter.Block, error)
      	- 包名: des
      	- 参数: 秘钥, 8bytes
      	- 返回值:
      
      	type Bolck interface {
      		// 返回加密字节快的大小
      		BlockSize() int
      		// 加密src的第一块数据并写入dst, src和dst是指向同一块内存地址的。
      		Encrypt(dst, src []byte)
      		// 解密src的第一块数据写入dst, src和dst是指向同一块内存地址的。
      		Decrypt(dst, src []byte)
      }
      
      2: 进行数据填充:
      
      3: 引入CBC模式接口,而这个接口返回一个密码分组链接模式的, 底层使用b加密的BlockMode接口,初始化向量iv的长度必须等于b的块尺寸。
      func NewCBCEncrypter(b Block, iv []byte) BlockMode
      	- 包名: cipter
      	- 参数1: cipter.Block
      	- 参数2: 初始化向量: initialize vector
      	- 返回值:分组模式, 里面提供加密解密算法。
      	type BolckMode interface {
      		// 返回加密字节快的大小
      		BlockSize() int
      		// 加密或者解密数据块, src和dst是指向同一块内存地址的。
      		CryptBlocks(dst, src []byte)
      	}
       */
      
      // 输入明文和秘钥, 输出密文
      func desCBCEncrypt(src , key []byte) []byte{
      	fmt.Printf("加密开始, 输入的数据是: %s\n", src)
      	// 1: 创建并返回一个使用DES进行加密的cipter.Block接口
      	block, err := des.NewCipher(key)
      	if err != nil{
      		panic(err)
      	}
      	// 2: 进行数据填充
      	src = paddingInfo(src, block.BlockSize())
      
      	// 3: 引入CBC模式:
      	//iv := []byte("12345678")
      	iv := bytes.Repeat([]byte("1"),block.BlockSize())
      	blockMode := cipher.NewCBCEncrypter(block, iv)
      
      	// 4: 加密操作:
      	blockMode.CryptBlocks(src/* 加密后的密文*/, src/* 明文*/)
      	fmt.Printf("加密结束, 加密之后的密文是:%x\n",src)
      	return src
      }
      
    • 实现填充:

      填充逻辑:

      • 加密:假设分组位数是8位,则如果剩余一位,则填充7个7, 如果剩余两位,则填充6个6, 如果没有剩余,则填充8个8。
      • 解密:首先判断最后一位是多少,如果是8则截取8后八位去掉,如果是7则截取后七位去掉。
    • 实现填充代码:

      • 1: 增加这样的一个函数:
      // 填充函数,输入明文, 分组长度,输出填充后的数据
      func paddingInfo(src []byte , blockSize int) []byte{
      	// 1: 得到明文长度
      	length := len(src)
      	// 2: 需要填充的数量
      remains := length % blockSize
      	paddingNumber := blockSize - remains
      	// 3:把填充的数值转换成字符
      	s1 := byte(paddingNumber)
      	// 4: 把字符拼接成数组
      	s2 := bytes.Repeat([]byte{s1}, paddingNumber)
      	// 5: 把拼接的数组追加到src的后面
      	srcNew := append(src, s2...)
      	// 6: 返回新的数组
      	return srcNew
      }
      
      • 2: 在上面的 数据填充位置进行填充数据
      // 2: 进行数据填充
      	src = paddingInfo(src, block.BlockSize())
      
    • 解密函数:

      • 1:解密函数编写:

        func desCBCDecrypt(cipherData, key []byte) []byte {
        	// 1: 创建并返回一个使用DES进行加密的cipter.Block接口
        	block, err := des.NewCipher(key)
        	if err != nil{
        		panic(err)
        	}
        	// 2: 引入CBC模式:
        	//iv := []byte("12345678")
        	iv := bytes.Repeat([]byte("1"),block.BlockSize())
        	blockMode := cipher.NewCBCDecrypter(block, iv)
        	// 3: 进行解密操作:
        	blockMode.CryptBlocks(cipherData/* 解密后的明文*/, cipherData/* 解密前的密文*/)
        	fmt.Printf("解密结束, 解密之后的明文是:%x\n",cipherData)
        	// 4: 去掉最后面的几位
        	cipherData = unpaddingInfo(cipherData)
        	return cipherData
        }
        
      • 2:去掉最后的多余数据:

        func unpaddingInfo(plainText []byte) []byte{
        	// 1: 获取长度
        	length := len(plainText)
        	if length == 0{
        		return []byte{}
        	}
        	// 2: 获取最后一个字符:
        	lastByte := plainText[length - 1]
        	// 3: 将字符串转换成数字
        	unpaddingNumber := int(lastByte)
        
        	// 4: 切片获取想要的数据
        	return plainText[ : length - unpaddingNumber]
        }
        
    • 整体的DES-CBC加密与解密:

      package main
      
      import (
      	"bytes"
      	"crypto/cipher"
      	"crypto/des"
      	"fmt"
      )
      
      /*
      需求:使用DES算法和CBC分组模式进行加密
      DES算法:秘钥8bytes,分组长度:8bytes
      CBC分组:提供初始化向量:长度与分组长度相同。
      */
      
      // 加密分析:
      /*
      1: 创建一个实现DES算法的cipher.Block接口, 这个接口返回一个cipter.Block
      	- func NewCipter(key []byte)(cipter.Block, error)
      	- 包名: des
      	- 参数: 秘钥, 8bytes
      	- 返回值:
      
      	type Bolck interface {
      		// 返回加密字节快的大小
      		BlockSize() int
      		// 加密src的第一块数据并写入dst, src和dst是指向同一块内存地址的。
      		Encrypt(dst, src []byte)
      		// 解密src的第一块数据写入dst, src和dst是指向同一块内存地址的。
      		Decrypt(dst, src []byte)
      }
      
      2: 进行数据填充:
      
      3: 引入CBC模式接口,而这个接口返回一个密码分组链接模式的, 底层使用b加密的BlockMode接口,初始化向量iv的长度必须等于b的块尺寸。
      func NewCBCEncrypter(b Block, iv []byte) BlockMode
      	- 包名: cipter
      	- 参数1: cipter.Block
      	- 参数2: 初始化向量: initialize vector
      	- 返回值:分组模式, 里面提供加密解密算法。
      	type BolckMode interface {
      		// 返回加密字节快的大小
      		BlockSize() int
      		// 加密或者解密数据块, src和dst是指向同一块内存地址的。
      		CryptBlocks(dst, src []byte)
      	}
       */
      
      // 输入明文和秘钥, 输出密文
      func desCBCEncrypt(src , key []byte) []byte{
      	fmt.Printf("加密开始, 输入的数据是: %s\n", src)
      	// 1: 创建并返回一个使用DES进行加密的cipter.Block接口
      	block, err := des.NewCipher(key)
      	if err != nil{
      		panic(err)
      	}
      	// 2: 进行数据填充
      	src = paddingInfo(src, block.BlockSize())
      
      	// 3: 引入CBC模式:
      	//iv := []byte("12345678")
      	iv := bytes.Repeat([]byte("1"),block.BlockSize())
      	blockMode := cipher.NewCBCEncrypter(block, iv)
      
      	// 4: 加密操作:
      	blockMode.CryptBlocks(src/* 加密后的密文*/, src/* 明文*/)
      	fmt.Printf("加密结束, 加密之后的密文是:%x\n",src)
      	return src
      }
      
      // 填充函数,输入明文, 分组长度,输出填充后的数据
      func paddingInfo(src []byte , blockSize int) []byte{
      	// 1: 得到明文长度
      	length := len(src)
      	// 2: 需要填充的数量
      	remains := length % blockSize
      	paddingNumber := blockSize - remains
      	// 3:把填充的数值转换成字符
      	s1 := byte(paddingNumber)
      	// 4: 把字符拼接成数组
      	s2 := bytes.Repeat([]byte{s1}, paddingNumber)
      	// 5: 把拼接的数组追加到src的后面
      	srcNew := append(src, s2...)
      	// 6: 返回新的数组
      	return srcNew
      }
      
      
      /*
      解密分析:
      1: 创建一个实现DES算法的cipher.Block接口, 这个接口返回一个cipter.Block
      
      2: 引入CBC模式接口
      
      3: 进行解密操作
      
      4: 去除填充
      */
      
      func desCBCDecrypt(cipherData, key []byte) []byte {
      	// 1: 创建并返回一个使用DES进行加密的cipter.Block接口
      	block, err := des.NewCipher(key)
      	if err != nil{
      		panic(err)
      	}
      	// 2: 引入CBC模式:
      	//iv := []byte("12345678")
      	iv := bytes.Repeat([]byte("1"),block.BlockSize())
      	blockMode := cipher.NewCBCDecrypter(block, iv)
      	// 3: 进行解密操作:
      	blockMode.CryptBlocks(cipherData/* 解密后的明文*/, cipherData/* 解密前的密文*/)
      	fmt.Printf("解密结束, 解密之后的明文是:%x\n",cipherData)
      	// 4: 去掉最后面的几位
      	cipherData = unpaddingInfo(cipherData)
      	return cipherData
      }
      
      func unpaddingInfo(plainText []byte) []byte{
      	// 1: 获取长度
      	length := len(plainText)
      	if length == 0{
      		return []byte{}
      	}
      	// 2: 获取最后一个字符:
      	lastByte := plainText[length - 1]
      	// 3: 将字符串转换成数字
      	unpaddingNumber := int(lastByte)
      
      	// 4: 切片获取想要的数据
      	return plainText[ : length - unpaddingNumber]
      }
      
      
      
      
      func main(){
      	src := []byte("12345678asdadsa")
      	key := []byte("12345678")
      	cipherData := desCBCEncrypt(src, key)
      	fmt.Printf("cipherData: %x\n", cipherData)
      	plainText := desCBCDecrypt(cipherData, key)
      	fmt.Printf("解密后的数据是:%s\n", plainText)
      }
      
      

2.2:3DES算法:

  • 1: DES后来被破解的时间越来越短,因此有一种思路就是进行三次加密,提高被破解的时间。

  • 2:加密与解密过程:

    • 加密:明文—>加密—>解密—>加密—>密文。
      • 中间使用解密的原因是为了兼容之前的DES。
      • 加密过程是以解密的方式进行加密,整体还是三次加密。
      • 秘钥长度:8B * 3 = 24B = 24 * 8 = 192b
      • 秘钥的分组:与DES算法相同。
    • 解密:密文—>解密—>加密—>解密—>明文。
    • 3个秘钥:
      • 如果秘钥1等于秘钥2,或者秘钥2等于秘钥三,则3DES其实就是DES。
      • 如果秘钥1与秘钥3相同,相当于两个秘钥。专业名词:3des-EDE2
      • 如果三个秘钥都不相同,则专业名词:3des-EDE3。

2.3:AES算法:

  • 3DES存在的问题:

    • 加密效率很低。
  • AES特点:

    • 秘钥长度可以选择:16b, 24b, 32b。
    • 分组长度:16b(是DES的一倍)
    • 效率高。
  • AES-CTR加密代码:

    • 加密解密分析:

      • AES秘钥可以选择长度,我们选择16,则秘钥长度也是16。

      • CTR不需要提供向量,但是需要提供数字。

    • 加密代码:

      package main
      
      import (
      	"bytes"
      	"crypto/aes"
      	"crypto/cipher"
      	"fmt"
      )
      
      /*
      加密分析:
      1: 创建一个cipter.Block接口
      
      2: 选择一个分组模式: CTR
      
      3: 加密
      
      4: 返回加密数据
      */
      
      func aesCTREncrypt(src, key []byte) []byte {
      	// 1: 创建一个cipter.Block接口
      	block, err := aes.NewCipher(key)
      	if err != nil{
      		panic(err)
      	}
      
      	// 2: 选择分组模式:
      	iv := bytes.Repeat([]byte("1"), block.BlockSize())
      	stream := cipher.NewCTR(block, iv)
      
      	// 3: 进行加密操作:
      	stream.XORKeyStream(src/*密文*/, src/*明文*/)
      
      	// 4: 返回加密的数据
      	return src
      }
      
      
      func main(){
      	// 1: 准备加密数据
      	src := []byte("任稻草")
      	// 2: 准备加密秘钥: 秘钥必须16
      	key := []byte("1234567812345678")
      	// 3: 调用加密函数,得到密文
      	cipherData := aesCTREncrypt(src, key)
      	fmt.Printf("加密后的数据是:%x\n", cipherData)
      }
      
    • 解密代码:

      func aesCTRDecrypt(cipherData, key []byte) []byte {
      	// 1: 创建一个cipter.Block接口
      	block, err := aes.NewCipher(key)
      	if err != nil{
      		panic(err)
      	}
      
      	// 2: 选择分组模式:
      	iv := bytes.Repeat([]byte("1"), block.BlockSize())
      	stream := cipher.NewCTR(block, iv)
      
      	// 3: 进行解密操作:
      	stream.XORKeyStream(cipherData/*明文*/, cipherData/*密文*/)
      
      	// 4: 返回加密的数据
      	return cipherData
      }
      
      
      
      
      func main(){
      	// 1: 准备加密数据
      	src := []byte("任稻草")
      	// 2: 准备加密秘钥: 秘钥必须16
      	key := []byte("1234567812345678")
      	// 3: 调用加密函数,得到密文
      	cipherData := aesCTREncrypt(src, key)
      	fmt.Printf("加密后的数据是:%x\n", cipherData)
      	// 4: 调用解密函数进行解密
      	plainText := aesCTRDecrypt(cipherData, key)
      	fmt.Printf("解密后的数据是:%s\n", plainText)
      }
      

三:非对称加密:

3.1:非对称加密流程与特点:

  • 流程:
    • A,B分别产生自己的公钥和私钥。
    • A, B双方将自己的公钥发送给对方。
    • A与B进行通信:A使用B的公钥进行加密,B使用自己的私钥进行解密。
    • B与A进行通信:B使用A的公钥进行加密,A使用自己的私钥进行解密。
  • 特点:
    • 通信双方,都有自己的公钥,私钥。
    • 安全性高。
    • 加密效率低。

3.2: 对称加密存在的问题:

  • 1: 秘钥管理困难:

    对于一个对称加密:A要跟100个分进行通信,则需要管理100个秘钥。我要找到哪个才是咱俩进行通信的才能加解密。而对于非对称加密,我保存的是我自己的私钥和你传给我的公钥,无论我要跟谁发送消息,我只需要使用自己私钥进行加密,而谁跟我交流,我用对应人的公钥解密就可以了。

  • 2: 秘钥分发困难:

    对于对称加密,我们要保证传输过程中,秘钥不被窃取。而非对称加密,只有窃取双方的公钥,第三个人才只是拥有看信息的权利,也无法进行交流。只有将双方的私钥还要拿到,才能进行加密,伪造信息。

3.3: 非对称加密的使用场景:

  • 1:通信加密

  • 2:https

  • 3:网银的U盾

  • 4:github ssh登录

  • 5: 签名(哈希 + 非对称加密)

3.4:OpenSSL生成非对称加密秘钥:

  • openssl下载:

    • x下载地址:http://slproweb.com/products/Win32OpenSSL.html

    密码学概述论_数据_08

    • 将bin目录加入到windows全局配置来。
  • 使用openssl生成公钥和私钥:

    • 切换到存储公钥和私钥的文件夹。

    • windows终端中输入:openssl进入 OpenSSL终端。

    • 执行命令生成自己的私钥: genrsa -out rsa_private_key.pem

    • 执行命令根据私钥生成公钥: rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

密码学概述论_数据_09

四:非对称加密算法:

4.1:RSA生成公钥和私钥:

  • 1:加密过程

    • 1:对加密的明文字符根据字符对应表转化成数字值。
    • 2:对数值依次进行E次方处理。
    • 3:对N取模。
    • 由E和N组成了公钥。
      • E: 根据特定的规则,限定了一个区间, 在这个区间内随意选用的。
      • N: 素数的乘积
  • 2: 解密过程:

    • 1:对密文进行D次方处理
    • 2:对N进行取模
    • 3:根据字符表恢复成明文。
    • D和N组成我们的私钥:
      • D: 如何获取D的值才是最难的,因为只有知道是哪两个大素数才能计算出D。
      • N:素数的乘积。
  • 3:RSA安全的原因:

    • 基于对大素数进行因式分解(世界公认难题)
  • 4:Go创建RSA公钥和私钥

    • 生成私钥分析:

      /*
      需求: 生成并保存公钥私钥和公钥:
      
      生成私钥分析:
      1:使用GenerateKey函数使用随机数生成器random生成一对具有指定字位数的RSA秘钥
      func GenerateKey(random io.Reader, bits int)(priv *PrivateKey, err error)
      - 参数1: 随机数
      - 参数2: 秘钥长度
      - 返回值: 私钥
      
      2:对于生成的私钥进行编码处理, x509规则,按照这个规则,进行序列化处理, 生成der编码的数据。
      MarshalPKIXPublicKey将公钥序列化成PKIX格式DER编码。
      func MarshalPKCSPublicKey(pub interface{})([]byte, error)
      
      3:创建一个Block结构,并填入私钥。(目的是生成上面虚线,下面虚线的格式)
      type Block struct {
      	Type string
      	Headers map[string]string
      	Bytes []byte
      }
      
      4: 将Pem Block格式写入到磁盘文件中。
      */
      
    • 生成公钥分析:

      /*
      	1: 通过私钥获取公钥
      	2:对生成的公钥进行der
      	3:创建Block结构,将Block结构加入到der编码中
      	4:将Pem Block数据写入到磁盘文件中。
      	*/
      
    • 生成私钥和公钥代码:

      package main
      
      import (
      	"crypto/rand"
      	"crypto/rsa"
      	"crypto/x509"
      	"encoding/pem"
      	"fmt"
      
      	"os"
      )
      
      const PrivateKeyFile = "./privateKey.pem"
      const PublicKeyFile  = "./PublicKey.pem"
      
      func generateKeyPair(bits int) error{
      	// 1: GenerateKey函数使用随机数生成器random生成RSA私钥
      	privateKey, err := rsa.GenerateKey(rand.Reader, bits) // 注意这里导包需要导入:"crypto/rand", 不能导入math/rand
      	if err != nil{
      		return err
      	}
      	// 2: 对于生成的私钥进行编码处理,生成der编码后的数据
      	priDerText := x509.MarshalPKCS1PrivateKey(privateKey)
      	// 3: 创建一个Block, 把der编码后的数据加入到Block中
      	block := pem.Block{
      		Type:    "RSA PRIVATE KEY",
      		Headers: nil,
      		Bytes:   priDerText,
      	}
      	// 4: 将Block写入到磁盘文件中
      	filehandler1, err := os.Create(PrivateKeyFile)
      	defer filehandler1.Close()
      	err = pem.Encode(filehandler1, &block)
      	if err != nil{
      		return err
      	}
      	/*
      	1: 通过私钥获取公钥
      	2:对生成的公钥进行der
      	3:创建Block结构,将Block结构加入到der编码中
      	4:将Pem Block数据写入到磁盘文件中。
      	*/
      	// 1: 通过私钥获取公钥,得到的是对象
      	pubKey := privateKey.PublicKey
      
      	// 2: 对公钥进行der编码
      	pubKeyDerText := x509.MarshalPKCS1PublicKey(&pubKey)
      
      	// 3: 将der后的结构,放入到Block中
      	block1 := pem.Block{
      		Type:    "RSA Public Key",
      		Headers: nil,
      		Bytes:   pubKeyDerText,
      	}
      
      	// 4: 写入磁盘:
      	filehandler2, err := os.Create(PublicKeyFile)
      	err = pem.Encode(filehandler2, &block1)
      	defer filehandler2.Close()
      	if err != nil{
      		return err
      	}
      
      	return nil
      }
      
      
      
      func main(){
      	// 1: 生成私钥
      	fmt.Printf("生成的私钥")
      	err := generateKeyPair(1024)
      	if err != nil{
      		fmt.Printf("生成私钥出错了!!")
      	}else{
      		fmt.Printf("生成私钥成功了!!")
      	}
      }
      

4.2:RSA进行加密和解密

  • 公钥加密流程:

    package main
    
    import (
    	"crypto/rand"
    	"crypto/rsa"
    	"crypto/x509"
    	"encoding/pem"
    	"fmt"
    	"io/ioutil"
    )
    const PrivateKeyFile = "./privateKey.pem"
    const PublicKeyFile  = "./PublicKey.pem"
    
    
    func rsaPubEncrypt(filename string, plainText []byte) (error, []byte) {
    	// 1: 通过公钥文件读取公钥信息
    	info , err := ioutil.ReadFile(filename)
    	if err != nil{
    		return err, []byte{}
    	}
    	// 2: 在读到的Block中获取中间的内容
    	block , _ := pem.Decode(info)
    	// 返回值1:pem.Block
    	// 返回值2:rest参加是没有没有解码完的数据,存储在这里
    
    	// 3: 解码der, 得到公钥
    	derText := block.Bytes
    	publicKey, err := x509.ParsePKCS1PublicKey(derText)
    	if err != nil{
    		return err, []byte{}
    	}
    	// 4: 使用公钥进行加密
    	// func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error)
    	cipherData, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
    	if err != nil{
    		return err, []byte{}
    	}
    	return nil, cipherData
    }
    
    
    
    func main(){
    	src := []byte("稻草任")
    	err, cipherData := rsaPubEncrypt(PublicKeyFile, src)
    	if err != nil{
    		fmt.Println("公钥加密失败")
    	}
    	fmt.Printf("公钥加密结果是:%x\n", cipherData)
    }
    
  • 私钥解密流程:

    func rsaPriKeyDecrypt(filename string, cipherData[]byte) (error, []byte) {
    	// 1: 通过私钥文件读取私钥信息
    	info , err := ioutil.ReadFile(filename)
    	if err != nil{
    		return err, []byte{}
    	}
    	// 2: 在读到的Block中获取中间的内容
    	block , _ := pem.Decode(info)
    	// 返回值1:pem.Block
    	// 返回值2:rest参加是没有没有解码完的数据,存储在这里
    
    	// 3: 解码der, 得到私钥
    	derText := block.Bytes
    	privateKey, err := x509.ParsePKCS1PrivateKey(derText)
    	if err != nil{
    		return err, []byte{}
    	}
    	// 4: 使用私钥进行解密
    	// func EncryptPKCS1v15(rand io.Reader, pub *PublicKey, msg []byte) ([]byte, error)
    	plainText, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherData)
    	if err != nil{
    		return err, []byte{}
    	}
    	return nil, plainText
    }
    
    func main(){
    	src := []byte("稻草任")
    	err, cipherData := rsaPubEncrypt(PublicKeyFile, src)
    	if err != nil{
    		fmt.Println("公钥加密失败")
    	}
    	fmt.Printf("公钥加密结果是:%x\n", cipherData)
    
    	err, plainText := rsaPriKeyDecrypt(PrivateKeyFile, cipherData)
    	if err != nil{
    		fmt.Println("私钥解密失败")
    	}
    	fmt.Printf("私钥解密结果是:%s\n", plainText)
    
    }
    

4.3: Base64进行编解码:

  • Base64编码原理:

    • 编码规则: 先将传入的字符---->转换成ASCII码---->每个ASCII码是8位---->以6位为一组得到一个数字---->在编码表中查找对应值,得到编码后的数。

    • 如果最后不足怎么办?

      一般来说是将三个字节转换成四个字节,但是过程中可能出现剩余情况,此时要补零,并且空余字节用等号代替。所以编码最后如果存在剩余,要么一个等号,要么两个等号。

      密码学概述论_非对称加密_10

    • 存在一个编码表:

      • 普通的字符集(A-Z, a-z, 0-9, +,/)
      • url专用字符集(A-Z, a-z, 0-9, _,-)

密码学概述论_非对称加密_11

  • GO语言实现Base64编解码:

    • 标准的Base64编码:

    • Url的Base64编码:

      package main
      
      import (
      	"encoding/base64"
      	"fmt"
      )
      
      func main(){
      	info := []byte("我是一夜奈何梁山")
      	// 1: 进行Base64编码
      	encodeInfo := base64.StdEncoding.EncodeToString(info)
      	fmt.Printf("使用Base64进行编码后的结果是:%s\n",encodeInfo )
      	// 进行解码:
      	decode_info , err := base64.StdEncoding.DecodeString(encodeInfo)
      	if err != nil{
      		panic(err)
      	}
      	fmt.Printf("使用Base64进行解码后的结果是:%s\n", decode_info)
      
      	// 2: 使用Base64url方式进行编码
      	info2 := []byte("https://www.bilibili.com/video/BV1e4411H7vq?p=49")
      	encodeInfo2 := base64.URLEncoding.EncodeToString(info2)
      	fmt.Printf("使用Base64进行Url编码的结果是:%s\n", encodeInfo2)
      	// 进行解码
      	decode_info2 , err := base64.URLEncoding.DecodeString(encodeInfo2)
      	fmt.Printf("使用Base64进行Url解码的结果是:%s\n", decode_info2)
      	
      }
      

4.4:哈希(单向散列函数):

  • 哈希: 可以对输入的内容生成一个唯一的数值。

    • 输入的内容不变,输出的内容不变。

    • 输入的内容改变一点,输出的内容千差万别。

    • 无论输入的内容多大,生成的哈希长度是相等的。

    • 哈希运算是对输入的内容进行摘要(指纹), 无法根据哈希值推断出原文。

  • 哈希的应用场景:

    • 1: 检测软件是否篡改。

      对于一个软件的下载,我们官方一般提供一个哈希值,当我们下载完成后,可以对安装的exe文件进行哈希运算,得到的值如果跟官网的一致,则传输过程中没有被篡改。

    • 2:消息认证码:

    • 3:伪随机数生成器

    • 4:一次性口令:

    • 5:密码存储:

      为了方式密码被盗,数据库一般存储密文(哈希加密后的值)

    • 6:数字签名:

4.5:MD5加密(哈希):

  • 数据量多的时候进行哈希处理:

    package main
    
    import (
    	"crypto/md5"
    	"fmt"
    	"io"
    )
    
    func main(){
    	// 对少量数据进行哈希运算
    	// 1: 创建一个哈希器
    	hasher := md5.New()
    	// 2:向哈希中加入数据
    	io.WriteString(hasher, "hello")
    	io.WriteString(hasher, "word")
    	// 2: 执行SUm操作,得到哈希值
    	hash := hasher.Sum(nil)
    	fmt.Printf("生成的哈希值是:%x\n", hash)
    	//生成的哈希值是:59284aa85709ddaf3bd246030060f6a2
    }
    
    如果 hasher.Sum(nil),中的nil是其他的字节数据例如0x,则会加入到哈希值的前面,根本不影响哈希值。
    
  • 数据量少的时候:

    package main
    
    import (
    	"crypto/md5"
    	"fmt"
    )
    
    func main(){
    	// 1:准备数据
    	src := []byte("我是一页奈何梁山")
        // 2:进行哈希运算:返回的是数组类型
    	hash := md5.Sum(src)
    	fmt.Printf("MD5加密后的哈希值是:%x\n", hash)
    	// MD5加密后的哈希值是:61d6b87120ce5fd3ee27f746ee1a4c90
    }
    
    

4.6:sha运算(哈希):

  • sha类的哈希运算分为两类: sha1, sah2
  • 最常用的是sha256(比特币,以太坊)

4.7:消息认证码(MAC):

  • 场景分析: A与B进行通信,假设A向B发送了一个消息,B如何才能判断是A发送过来的,并且如何判断A发送过来的信息没有被篡改呢?

    • 思考: 使用对称加密可以吗?

      A明文----->加密---->密文----->发送给B---->B解密---->明文

      假设发送的是“你好”, 没有篡改解密是“你好”, 假设篡改后解密是“**XX()”乱码,因为是乱码,我们判断被篡改了。但是如果发送的本身就是乱码,解密出来,我如何判断是不是被篡改了呢?我无法进行判断。因此这就需要消息认证(哈希)

  • 消息认证码如何判断没有消息没有被篡改?

    • 1: A与B之间协商消息认证码秘钥。
    • 2:A将消息经过对称加密秘钥加密,得到密文。
    • 3: A将密文经过消息认证码秘钥计算出哈希值
    • 4:A将这个哈希值与密文同时发送给B。
    • 5:B拿到密文,使用消息验证码秘钥对密文进行哈希得到哈希值,
    • 6:对比两个哈希值是否相同,判断信息是否被篡改。
    • 7:如果哈希值一直说明没有篡改,则使用对称加密秘钥进行解密。

    图示:

密码学概述论_非对称加密_12

  • 消息认证码的用处:

    • SWIFT:
    • HTTPS:(重点)
    • IPSec:
  • HMAC函数(哈希MAC)

    package main
    
    import (
    	"crypto/hmac"
    	"crypto/sha512"
    	"fmt"
    )
    
    // 生成hmac(消息认证码的函数)
    // 传入字节类型的数据--->返回哈希处理后的结果
    func generateHMAC(src []byte, key []byte) []byte {
    	// 1: 创建哈希器
    	hasher := hmac.New(sha512.New, key)
    	// 2: 将数据加入哈希器中
    	hasher.Write(src)
    	// 2: 生成哈希值
    	mac := hasher.Sum(nil)
    	return mac
    }
    
    // 消息认证码的认证:
    func verifyHMC(src , key, mac1 []byte) bool {
    	// 1: B接收到的数据---src
    	// 2: B接收到的mac1 ---mac1
    	// 3: 对B到的数据进行哈希处理,得到mac2
    	mac2 := generateHMAC(src, key)
    	// 4: 对比两个mac值是否相同。
    	return hmac.Equal(mac1, mac2)
    }
    
    
    func main(){
    	// 1: 生成明文和秘钥
    	src := []byte("我是一夜奈何梁山")
    	key := []byte("1234567890")
    	// 2: 生成mac1
    	mac1 := generateHMAC(src, key)
    	// 3: 判断消息认证码是否相同
    	isEqual := verifyHMC(src, key, mac1)
    	if isEqual{
    		fmt.Println("验证通过,没有被修改")
    	}else{
    		fmt.Println("验证失败,中间有人被修改了")
    	}
    }
    

4.8:数字签名:

  • 消息认证存在的问题:

    • 1:消息认证码的秘钥配送问题。
    • 2:无法进行第三方证明。
    • 3:无法防止发送方否认。
  • 数字签名的流程:

    • 1:对原文进行哈希运算得到一个哈希值。
    • 2:使用私钥对这个哈希值进行处理,得到一个签名。
    • 3:将原文和签名一起发送给对方。
  • 数字签名的认证流程:

    • 1:接收到原文,对原文进行哈希处理,得到一个哈希值。
    • 2:使用公钥对签名进行解密,得到另外一个哈希值。
    • 3:对比两个哈希值,可以知道是否被篡改了。
  • 图示:

密码学概述论_非对称加密_13

  • 解决消息认证的问题:
    • 无法有效配送秘钥—>数字签名中,不需要协商秘钥,因此没有配送问题。
    • 无法进行第三方证明----->任何持有公钥的,都能帮助认证。
    • 无法防止发送方否认----->私钥只有发送方有,无法进行抵赖。

4.9:ECC椭圆曲线:

  • 使用ECC椭圆曲线生成秘钥特点:

  • 1: ECC164位的密钥产生一个安全级,相当于RSA 1024位密钥提供的保密强度 。

    • 2: 计算量较小,处理速度更快,存储空间和传输带宽占用较少。
  • ECC椭圆曲线生成秘钥的用途:

    • 1: 居民二代身份证。
    • 2:比特币
  • 使用ECC椭圆曲线生成公钥和私钥:

    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/elliptic"
    	"crypto/rand"
    	"crypto/x509"
    	"encoding/pem"
    	"os"
    )
    
    const EccPrivateKeyFile = "./EccPrivateKey.pem"
    const EccPublicKeyFile = "./EccPublicKey.pem"
    
    
    func generateEccKeypair(){
    	// 1: 选择一个椭圆曲线
    	curve := elliptic.P256()
    	// 2: 使用ecdsa包,创建私钥
    	privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
    	if err != nil{
    		panic(err)
    	}
    	// 3:使用x509进行编码,序列化成DER编码
    	derText , err:= x509.MarshalECPrivateKey(privateKey)
    	if err != nil{
    		panic(err)
    	}
    	// 4: 将编码后的DER编码写入Block中
    	block1 := pem.Block{
    		Type:    "ECC PRIVATE KEY",
    		Headers: nil,
    		Bytes:   derText,
    	}
    	// 5:将私钥写入到文件中
    	fileHander, err := os.Create(EccPrivateKeyFile) // 填充数据
    	if err != nil{
    		panic(err)
    	}
    	// 关闭上下文
    	defer fileHander.Close()
    	// 写入到文件中
    	err = pem.Encode(fileHander, &block1)
    	if err != nil{
    		panic(err)
    	}
    
    	// 1: 根据私钥生成公钥
    	publicKey := privateKey.PublicKey
    
    	// 2:进行编码
    	derText1, err := x509.MarshalPKIXPublicKey(&publicKey)
    	if err != nil{
    		panic(err)
    	}
    	// 4: 将编码后的DER编码写入Block中
    	block2 := pem.Block{
    		Type:    "ECC Pubclic KEY",
    		Headers: nil,
    		Bytes:   derText1,
    	}
    	// 5:将私钥写入到文件中
    	fileHander2, err := os.Create(EccPublicKeyFile) // 填充数据
    	if err != nil{
    		panic(err)
    	}
    	// 关闭上下文
    	defer fileHander2.Close()
    	// 写入到文件中
    	err = pem.Encode(fileHander2, &block2)
    	if err != nil{
    		panic(err)
    	}
    
    }
    
    func main(){
    	generateEccKeypair()
    }
    
  • 使用ECC椭圆曲线: 私钥进行签名,公钥进行认证:

    • 在GO语言中无法使用ECC进行加解密, 但是支持ECC进行签名。
    package main
    
    import (
    	"crypto/ecdsa"
    	"crypto/rand"
    	"crypto/sha256"
    	"crypto/x509"
    	"encoding/pem"
    	"errors"
    	"fmt"
    	"io/ioutil"
    	"math/big"
    )
    const EccPrivateKeyFile = "./EccPrivateKey.pem"
    const EccPublicKeyFile = "./EccPublicKey.pem"
    
    
    /* 使用私钥进行签名
    1:读取私钥,解码
    
    2: 对原文进行哈希,得到哈希值
    
    3:对哈希值使用私钥进行签名
    
    */
    
    // 自定义签名结构:
    type Signature struct {
    	r *big.Int
    	s *big.Int
    }
    
    // 私钥签名:
    func eccSignData(filename string, src []byte) (Signature, error) {
    	// 1: 读取私钥,解码
    	info , err := ioutil.ReadFile(filename)
    	if err != nil{
    		return Signature{} , err
    	}
    	block, _ := pem.Decode(info)
    	derText := block.Bytes
    	privateKey , err := x509.ParseECPrivateKey(derText)
    	if err != nil {
    		return Signature{} , err
    	}
    	// 2: 对原文进行哈希,得到哈希值
    	hash := sha256.Sum256(src)
    
    	// 3: 使用私钥对哈希值进行签名
    	r, s, err := ecdsa.Sign(rand.Reader, privateKey, hash[:])
    	if err != nil{
    		return Signature{}, err
    	}
    	sig := Signature{r, s}
    	return sig, nil
    }
    
    
    /*
    1: 读取公钥,解码
    
    2:对原文生成哈希
    
    3:使用公钥进行验证
    
    */
    
    // 公钥认证
    func eccVerifySig(filename string, src []byte, sig Signature) error{
    	// 1:读取公钥
    	info , err := ioutil.ReadFile(filename)
    	if err != nil {
    		return nil
    	}
    	// 2: 在block中获取被der编码的数据
    	block , _ := pem.Decode(info)
    	// 3: 解码der,拿到公钥
    	derText := block.Bytes
    	publicKeyInterface , err := x509.ParsePKIXPublicKey(derText)
    	if err != nil{
    		return err
    	}
    	publicKey, ok := publicKeyInterface.(*ecdsa.PublicKey)
    	if !ok {
    		return errors.New("断言失败,不是ECDS公钥")
    	}
    	// 4: 对原文进行哈希处理
    	hash := sha256.Sum256(src)
    
    	// 5: 使用公钥验证哈希值和两个大数r,s,返回签名是否合法。
    	isValid := ecdsa.Verify(publicKey, hash[:], sig.r, sig.s)
    	if !isValid{
    		return errors.New("校验失败")
    	}else{
    		return nil
    	}
    }
    
    
    
    func main(){
    	// 1: 调用私钥签名函数返回签名
    	src := []byte("我是一夜奈何梁山")
    	sig , err := eccSignData(EccPrivateKeyFile, src)
    	if err != nil{
    		panic(err)
    	}
    	fmt.Println("私钥进行的签名是:", sig.r, sig.s)
    	// 2: 进行校验
    	err = eccVerifySig(EccPublicKeyFile, src, sig)
    	if err != nil{
    		fmt.Println(err)
    		return
    	}
    	fmt.Println("校验成功!!")
    }
    

4.11:数字证书:

  • 为什么需要数字证书?

    • 非对称加密首先要进行公钥交换,那么我怎么才能确定,我拿到的是你的公钥,而不是被篡改的公钥呢?

    • 图示:

密码学概述论_对称加密_14

  • 解决方案:引入第三方认证机构CA

4.12:HTTPS流程分析:

  • HTTPS = HTTP +SSL

  • SSL :

  • CA颁发过程:

    • 1: 服务器提供者生成公钥和私钥,将公钥发送给CA机构。
    • 2:CA机构也会有自己的私钥和公钥,CA使用自己的私钥对服务器的公钥进行数字签名。
    • 3:CA机构将数字证书发送给服务器。
  • CA认证过程:

    • 1:浏览器给服务器发送请求, 服务器将数字证书发送给浏览器。
    • 2:客户端的浏览器安装了知名CA机构的根证书,包含了CA机构的公钥, 浏览器对服务器的证书进行验证。
    • 3:如果验证成功,则表示网站可信,如果认证失败则提示警告。
  • HTTPS通信的过程:

    • 1:CA认证过程
    • 2:如果证书有效,则浏览器将自己支持的加密算法发送给服务器,同时生成一个对称加密秘钥,使用服务器的公钥,对对称加密秘钥进行加密,然后发送给服务器。
    • 3:服务器使用自己的私钥,对密文进行解密,得到对称加密秘钥。
    • 4:以后双方通信都使用对称加密秘钥进行加密与解密。
  • windows下查看CA证书:

    • 过程图示:

密码学概述论_非对称加密_15
密码学概述论_非对称加密_16