流逝的是岁月,不变的是情怀.
坚持学习,是为了成就更好的自己.
公众号[中关村程序员]

# 01 - 对称加密

# 1.1 基础知识点

  1. 知识点大纲介绍

    • 密码的基础概念
    • 对称加密
    • 非对称加密
    • 单向散列函数
      • 哈希函数
    • 消息认证码
    • 数字签名
    • 证书
    • ssl/tls - https
  2. 为什么要加密, 以及解决方案

    保证数据安全

  3. 加密三要素

    • 明文/密文
    • 秘钥
      • 定长的字符串
      • 需要根据加密算法确定其长度
    • 算法
      • 加密算法
      • 解密算法
      • 加密算法和解密算法有可能是互逆的, 也有可能相同
  4. 常用的两种加密方式

    • 对称加密
      • 秘钥: 加密解密使用的是同一个秘钥, 秘钥有一个
      • 特点
        • 双方向保证机密性
        • 加密效率高, 适合加密大数据, 大文件
        • 加密强度不高, 相对于非对称加密
    • 非对称加密
      • 秘钥: 加密解密使用的不同的秘钥, 秘钥有两个, 需要使用秘钥生成算法, 得到密钥对
        • 公钥 - 可以公开的秘钥
          • 公钥加密数据, 解密需要使用私钥
        • 私钥 - 需要妥善保管的秘钥, 知道的人越少越好
          • 私钥加密, 公钥解密
      • 特点:
        • 数据的机密性只能单方向保证
        • 加密效率低, 适合加密少量数据
        • 加密强度高, 相对于对称加密
  5. 凯撒密码

    恺撒密码(Caesar cipher)是一种相传尤利乌斯·恺撒曾使用过的密码。恺撒于公元前100年左右诞生于古罗马,是一位著名的军事统帅。

    恺撤密码是通过将明文中所使用的字母表按照一定的字数“平移”来进行加密的

    1538906583082

    • 凯撒密码加解密公式

      • 加密

      • 解密

    • 凯撒密码中的加密三要素

      • 明文/密文
        • 明文: 小写字母表中的数据
        • 密文: 大写字母表中的数据
      • 秘钥
        • 按照上图秘钥为3
      • 算法
        • 加密: +3
        • 解密: -3
    • 凯撒密码的安全性

      不安全

  6. 密码信息安全常识

    • 不要使用保密的密码算法(普通公司和个人)
    • 使用低强度的密码比不进行任何加密更危险
    • 任何密码总有一天都会被破解
    • 密码只是信息安全的一部分
  7. 密码信息威胁

    1538907602932

    思考:信息安全处理必须要具备哪些特性?

# 1.2 对称加密

以分组为单位进行处理的密码算法称为分组密码(blockcipher)

  1. 编码的概念

    G = 1024m

    m = 1024kbyte

    byte = 8bit

    bit 0/1

    计算机的操作对象并不是文字,而是由0和1排列而成的比特序列

    将现实世界中的东西映射为比特序列的操作称为编码(encoding)

    加密 -> 编码

    解密 -> 解码

    hello world -> 比特序列

    h -> int 104 ->

  2. DES -- Data Encryption Standard

    • 现在使用DES方式加密,数据还安全吗?
      • 不安全, 已经被破解了
    • 是不是分组密码?
      • 是, 先对数据进行分组, 然后在加密或解密
    • DES的分组长度?
      • 8byte == 64bit
    • DES的秘钥长度?
      • 56bit秘钥长度+8bit错误检测标志位 = 64bit == 8byte
  3. 3DES -- Triple-DES

    • 3DES安全吗?
      • 安全, 但是效率低
    • 算法描述?
      • 进行了3次des加密
    • 是不是分组密码?
    • 3DES分组长度?
      • 8字节
    • 3DES秘钥长度?
      • 24字节, 在算法内部会被平均分成3份
    • 3DES加密过程?
      • 秘钥1 -> 加密, 秘钥2 -> 解密, 秘钥3 -> 加密
    • 3DES解密过程?
      • 秘钥1 -> 解密, 秘钥2 -> 加密, 秘钥3 -> 解密
  4. AES -- Advanced Encryption Standard

    • AES安全吗?
      • 安全, 效率高, 推荐使用的
    • 是不是分组密码?
    • AES分组长度?
      • 128bit = 16字节
    • AES秘钥长度?
      • 128bit = 16字节
      • 192bit = 24字节
      • 256bit = 32字节
      • go中的秘钥长度只能是16字节

# 1.3 分组密码的模式

  1. 按位异或

    • 第一步需要将数据转换为二进制

    • 按位异或操作符: ^

    • 两个标志位进行按位异或操作:

      • 相同为0, 不同为1
    • 举例:

      1 0 0 0   ----> 8
      1 0 1 1   ----> 11
      -----------------------按位异或一次
      0 0 1 1   ---->  3
      1 0 1 1   ----> 11
      -----------------------按位异或两侧
      1 0 0 0   -----> 8
      =================================
      a = 8
      b = 11
      a 和 b按位异或1==> 加密
      得到的结果再次和 b 按位异或 ===> 解密
      
  2. ECB - Electronic Code Book, 电子密码本模式

    • 特点: 简单, 效率高, 密文有规律, 容易被破解
    • 最后一个明文分组必须要填充
      • des/3des -> 最后一个分组填充满8字节
      • aes -> 最后一个分组填充满16字节
    • 不需要初始化向量
  3. CBC - Cipher Block Chaining, 密码块链模式

    • 特点: 密文没有规律, 经常使用的加密方式
    • 最后一个明文分组需要填充
      • des/3des -> 最后一个分组填充满8字节
      • aes -> 最后一个分组填充满16字节
    • 需要一个初始化向量 - 一个数组
      • 数组的长度: 与明文分组相等
      • 数据来源: 负责加密的人的提供的
      • 加解密使用的初始化向量值必须相同
  4. CFB - Cipher FeedBack, 密文反馈模式

    • 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
    • 需要一个初始化向量 - 一个数组
      • 数组的长度: 与明文分组相等
      • 数据来源: 负责加密的人的提供的
      • 加解密使用的初始化向量值必须相同
    • 不需要填充
  5. OFB - Output-Feedback, 输出反馈模式

    • 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
    • 需要一个初始化向量 - 一个数组
      • 数组的长度: 与明文分组相等
      • 数据来源: 负责加密的人的提供的
      • 加解密使用的初始化向量值必须相同
    • 不需要填充
  6. CTR - CounTeR, 计数器模式

    • 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
    • 不需要初始化向量
      • go接口中的iv可以理解为随机数种子, iv的长度 == 明文分组的长度
    • 不需要填充
  7. 最后一个明文分组的填充

    • 使用cbc, ecb需要填充
      • 要求:
        • 明文分组中进行了填充, 然后加密
        • 解密密文得到明文, 需要把填充的字节删除
    • 使用 ofb, cfb, ctr不需要填充
  8. 初始化向量 - IV

    • ecb, ctr模式不需要初始化向量
    • cbc, ofc, cfb需要初始化向量
      • 初始化向量的长度
        • des/3des -> 8字节
        • aes -> 16字节
      • 加解密使用的初始化向量相同

# 1.4 对称加密在go中的实现

  1. des

  2. 3des

  3. aes

    # 加密流程:
    1. 创建一个底层使用des/3des/aes的密码接口
    	"crypto/des"
    	func NewCipher(key []byte) (cipher.Block, error) # -- des
    	func NewTripleDESCipher(key []byte) (cipher.Block, error)	# -- 3des
    	"crypto/aes"
    	func NewCipher(key []byte) (cipher.Block, error) # == aes
    2. 如果使用的是cbc/ecb分组模式需要对明文分组进行填充
    3. 创建一个密码分组模式的接口对象
    	- cbc
    	func NewCBCEncrypter(b Block, iv []byte) BlockMode # 加密
    	- cfb
    	func NewCFBEncrypter(block Block, iv []byte) Stream # 加密
    	- ofb
    	- ctr
    4. 加密, 得到密文
    	
    

# 02 - 非对称加密

# 1. 对称加密的弊端'

  • 秘钥分发困难

  • 可以通过非对称加密完成秘钥的分发

    https

    Alice 和 Bob通信, Alice给bob发送数据, 使用对称加密的方式

    1. 生成一个非对称的秘钥对, bob生成
    2. bob将公钥发送给alice
    3. alice生成一个用于对称加密的秘钥
    4. alice使用bob的公钥就对称加密的秘钥进行加密, 并且发送给bob
    5. bob使用私钥就数据解密, 得到对称加密的秘钥
    6. 通信的双方使用写好的秘钥进行对称加密数据加密

# 2. 非对称加密的秘钥

  • 不存在秘钥分发困难的问题

# 2.1 场景分析

数据对谁更重要, 谁就拿私钥

  • 直观上看: 私钥比公钥长
  • 使用第三方工具生成密钥对: 公钥文件xxx.pub xxx
  1. 通信流程, 信息加密 (A写数据, 发送给B, 信息只允许B读)

    A: 公钥

    B: 私钥

  2. 登录认证 (客户端要登录, 连接服务器, 向服务器请求个人数据)

    客户端: 私钥

    服务器: 公钥

  3. 数字签名(表明信息没有受到伪造,确实是信息拥有者发出来的,附在信息原文的后面)

    • 发送信息的人: 私钥
    • 收到信息的人: 公钥
  4. 网银U盾

    • 个人: 私钥
    • 银行拿公钥

# 3. 使用RSA非对称加密通信流程

要求: Alice 给 bob发送数据, 保证数据信息只有bob能看到

# 4. 生成RSA的秘钥对

# 4.1 一些概念

  1. x509证书规范、pem、base64
    • pem编码规范 - 数据加密
    • base64 - 对数据编码, 可逆
      • 不管原始数据是什么, 将原始数据使用64个字符来替代
        • a-z A-Z 0-9 + /
  2. ASN.1抽象语法标记
  3. PKCS1标准

# 4.2 密钥对生成流程

  • 生成私钥操作流程概述

    1. 使用rsa中的GenerateKey方法生成私钥

      func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error)

      • rand.Reader -> import "crypto/rand"
      • 1024 的整数倍 - 建议
    2. 通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串

      func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte

    3. 将私钥字符串设置到pem格式块中

      初始化一个pem.Block块

      type Block struct {
          Type    string            // 得自前言的类型(如"RSA PRIVATE KEY")
          Headers map[string]string // 可选的头项
          Bytes   []byte            // 内容解码后的数据,一般是DER编码的ASN.1结构
      }
      
    4. 通过pem将设置好的数据进行编码, 并写入磁盘文件中

      func Encode(out io.Writer, b *Block) error

      • out - 准备一个文件指针
  • 生成公钥操作流程

    1. 从得到的私钥对象中将公钥信息取出

      type PrivateKey struct {
          PublicKey            // 公钥
          D         *big.Int   // 私有的指数
          Primes    []*big.Int // N的素因子,至少有两个
          // 包含预先计算好的值,可在某些情况下加速私钥的操作
          Precomputed PrecomputedValues
      }
      
    2. 通过x509标准将得到 的rsa公钥序列化为字符串

      func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)
      
    3. 将公钥字符串设置到pem格式块中

      type Block struct { Type string // 得自前言的类型(如"RSA PRIVATE KEY") Headers map[string]string // 可选的头项 Bytes []byte // 内容解码后的数据,一般是DER编码的ASN.1结构 }

    4. 通过pem将设置好的数据进行编码, 并写入磁盘文件

      func Encode(out io.Writer, b *Block) error

# 5. RSA加解密

# 5.1 RSA加密

  1. 将公钥文件中的公钥读出, 得到使用pem编码的字符串

    -- 读文件

  2. 将得到的字符串解码

    -- pem.Decode

  3. 使用x509将编码之后的公钥解析出来

    -- func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)

  4. 使用得到的公钥通过rsa进行数据加密

# 5.2 RSA解密

  1. 将私钥文件中的私钥读出, 得到使用pem编码的字符串
  2. 将得到的字符串解码
  3. 使用x509将编码之后的私钥解析出来
  4. 使用得到的私钥通过rsa进行数据解密

# 6. 哈希算法

# 6.1 概念

称谓: 单向散列函数, 哈希函数, 杂凑函数, 消息摘要函数

接收的输入: 原像

输出: 散列值, 哈希值, 指纹, 摘要

# 6.2 单向散列函数特性

  1. 将任意长度的数据转换成固定长度的数据

  2. 很强的抗碰撞性

  3. 不可逆

  4. MD4/MD5

    • 不安全
    • 散列值长度: 128bit == 16byte
  5. sha1

    • 不安全
    • 散列值长度: 160bit == 20byte
  6. sha2 - 安全

    • sha224
      • 散列值长度: 224bit == 28byte
    • sha256
      • 散列值长度: 256== 32byte
    • sha384
      • 散列值长度: 384bit == 48byte
    • sha512
      • 散列值长度: 512bit == 64byte

    # 6.3 go中使用单向散列函数

    // 第一种方式, 直接调用sum
    // 适用于数据量比较小的情况
    func Sum(data []byte) [Size]byte
    
    // 第二种方式
    // 1. 创建哈希接口对象
    func New() hash.Hash
    type Hash interface {
        // 通过嵌入的匿名io.Writer接口的Write方法向hash中添加更多数据,永远不返回错误
        io.Writer
        // 返回添加b到当前的hash值后的新切片,不会改变底层的hash状态
        Sum(b []byte) []byte
        // 重设hash为无数据输入的状态
        Reset()
        // 返回Sum会返回的切片的长度
        Size() int
        // 返回hash底层的块大小;Write方法可以接受任何大小的数据,
        // 但提供的数据是块大小的倍数时效率更高
        BlockSize() int
    }
    type Writer interface {
        Write(p []byte) (n int, err error)
    }
    // 2. 往创建出的哈希对象中添加数据
    hash.Hash.Write([]byte("添加的数据..."))
    hash.Hash.Write([]byte("添加的数据..."))
    hash.Hash.Write([]byte("添加的数据..."))
    hash.Hash.Write([]byte("添加的数据..."))
    // 3. 计算结果, md5就是散列值
    md5 := hash.Sum(nil);
    // 散列值一般是一个二进制的字符串, 有些字符不可见, 需要格式化
    // 格式化为16进制的数字串 - 0-9, a-f
    func EncodeToString(src []byte) string
    // 数据转换完成之后, 长度是原来的2倍
    
    
    1. 计算一个大文件比如1G文件的散列值
    2. 使用udp的方式分发秘钥, 进行一个对称加密的通信
      • 服务器
        • 生成密钥对
        • 公钥发送给客户端
      • 客户端
        • 客户端收到了公钥
        • 生成一个秘钥 - 用于对称加密
        • 使用公钥加密, 发送给服务器

# 对称加密和非对称加密总结

  1. 概念

    • 加密三要素

      • 明文/密文
      • 秘钥
      • 算法
    • 对称加密和非对称加密

      • 对称加密: 加解密使用同一个秘钥, 1个
        • 效率高
      • 非...: 密钥对
        • 公钥加密, 私钥解密
        • 私钥加密, 公钥解密
    • 对称加密中的公开的加密算法

      • des
        • 分组长度: 8字节
        • 秘钥长度: 8字节
      • 3des
        • 分组长度: 8字节
        • 秘钥长度: 24byte
      • aes
        • 分组长度: 16字节
        • 秘钥长度: 16字节, 24字节, 32字节
          • 在go的api中只能使用16字节
    • 对称加密的分组模式

      • EBC - 不推荐使用

      • CBC - 常用的方式

        • 准备的数据:

          • 初始化向量iv - 字符数组
          • 长度 == 明文分组长度
          • 加解密初始化向量值必须相同
          • 秘钥
            • 根据加密算法定

        ===========================

      • OFB - 不推荐使用

      • CFB - 不推荐使用

      • CTR - 推荐使用, 效率最高

# 03 - 消息认证码

# 1.1 消息认证

消息认证码(message authentication code)是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为MAC。

1539176665517

  • 思考改进方案?

    从哈希函数入手

    需要将要发送的数据进行哈希运算, 将哈希值和原始数据一并发送

    需要在进行哈希运算的时候引入加密的步骤

    • 在alice对数据进行哈希运算的时候引入一个秘钥, 让其参与哈希运算, 生成散列值
    • bob对数据校验
      • bob收到原始和散列值之后,
        • 处理原始数据: 通过秘钥和哈希算法对原始数据生成散列值
        • 散列值比较: 生成的散列值 和 接收到的散列值进行比对

# 1.2 消息认证码的使用步骤

1539177690828

  1. 前提条件:
    • 在消息认证码生成的一方和校验的一方, 必须有一个秘钥
    • 双方约定好使用同样的哈希函数对数据进行运算
  2. 流程:
    • 发送者:
      • 发送原始法消息
      • 将原始消息生成消息认证码
        • ((原始消息) + 秘钥) * 函数函数 = 散列值(消息认证码)
      • 将消息认证码发送给对方
    • 接收者:
      • 接收原始数据
      • 接收消息认证码
      • 校验:
        • ( 接收的消息 + 秘钥 ) * 哈希函数 = 新的散列值
        • 通过新的散列值和接收的散列值进行比较

# 1.3 go中对消息认证码的使用

有一个包: crypto/hmac

func New(h func() hash.Hash, key []byte) hash.Hash
- 返回值: hash接口
- 参数1: 函数函数的函数名
	sha1.new
	md5.new
	sha256.new
- 参数2: 秘钥

第二步: 添加数据
type Hash interface {
    // 通过嵌入的匿名io.Writer接口的Write方法向hash中添加更多数据,永远不返回错误
    io.Writer
    // 返回添加b到当前的hash值后的新切片,不会改变底层的hash状态
    Sum(b []byte) []byte
    // 重设hash为无数据输入的状态
    Reset()
    // 返回Sum会返回的切片的长度
    Size() int
    // 返回hash底层的块大小;Write方法可以接受任何大小的数据,
    // 但提供的数据是块大小的倍数时效率更高
    BlockSize() int
}
type Writer interface {
    Write(p []byte) (n int, err error)
}
第三步: 计算散列值

# 1.4 消息认证码的问题

  1. 弊端
    • 有秘钥分发困难的问题
  2. 无法解决的问题
    • 不能进行第三方证明
    • 不能防止否认

# 04 - 数字签名

# 2.1 签名的生成和验证

  1. 签名
    • 有原始数据对其进行哈希运算 -> 散列值
    • 使用非对称加密的私钥对散列值加密 -> 签名
    • 将原始数据和签名一并发送给对方
  2. 验证
    • 接收数据
      • 原始数据
      • 数字签名
    • 数字签名, 需要使用公钥解密, 得到散列值
    • 对原始数据进行哈希运算得到新的散列值

# 2.2 非对称加密和数字签名

总结:

  1. 数据通信
    • 公钥加密, 私钥解密
  2. 数字签名:
- 私钥加密, 公钥解密

# 2.3 数字签名的方法

1539177860475

# 2.4 使用RSA进行数字签名

  1. 使用rsa生成密钥对

    1. 生成密钥对
    2. 序列化
    3. 保存到磁盘文件
  2. 使用私钥进行数字签名

    1. 打开磁盘的私钥文件

    2. 将私钥文件中的内容读出

    3. 使用pem对数据解码, 得到了pem.Block结构体变量

    4. x509将数据解析成私钥结构体 -> 得到了私钥

    5. 创建一个哈希对象 -> md5/sha1

    6. 给哈希对象添加数据

    7. 计算哈希值

    8. 使用rsa中的函数对散列值签名

      func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err error)
      参数1: rand.Reader
      参数2: 非对称加密的私钥
      参数3: 使用的哈希算法
      	crypto.sha1
      	crypto.md5
      参数4: 数据计算之后得到的散列值
      返回值: 
      - s: 得到的签名数据
      - err: 错误信息
      
  3. 使用公钥进行签名认证

    1. 打开公钥文件, 将文件内容读出 - []byte

    2. 使用pem解码 -> 得到pem.Block结构体变量

    3. 使用x509对pem.Block中的Bytes变量中的数据进行解析 -> 得到一接口

    4. 进行类型断言 -> 得到了公钥结构体

    5. 对原始消息进行哈希运算(和签名使用的哈希算法一致) -> 散列值

      1. 创建哈希接口
      2. 添加数据
      3. 哈希运算
    6. 签名认证 - rsa中的函数

      func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error)
      参数1: 公钥
      参数2: 哈希算法 -> 与签名使用的哈希算法一致
      参数3: 将原始数据进行哈希原始得到的散列值
      参数4: 签名的字符串
      返回值: 
      	- nil -> 验证成功
      	- !=nil -> 失败
      

# 2.5 使用椭圆曲线进行数字签名

椭圆曲线在go中对应的包: import "crypto/elliptic"

使用椭圆曲线在go中进行数字签名: import "crypto/ecdsa"

美国FIPS186-2标准, 推荐使用5个素域上的椭圆曲线, 这5个素数模分别是:

P~192~ = 2^192^ - 2^64^ - 1

P~224~ = 2^224^ - 2^96^ + 1

P~256~ = 2^256^ - 2^224^ + 2^192^ - 2^96^ -1

P~384~ = 2^384^ - 2^128^ - 2^96^ + 2^32^ -1

P~512~ = 2^512^ - 1

  1. 秘钥对称的生成, 并保存到磁盘

    1. 使用ecdsa生成密钥对

      func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
      
    2. 将私钥写入磁盘

      • 使用x509进行序列化

        func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error)
        
      • 将得到的切片字符串放入pem.Block结构体中

        block := pem.Block{

        Type : "描述....",

        Bytes : MarshalECPrivateKey返回值中的切片字符串,

        }

      • 使用pem编码

        pem.Encode();

    3. 将公钥写入磁盘

      • 从私钥中得到公钥

      • 使用x509进行序列化

        func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)
        
      • 将得到的切片字符串放入pem.Block结构体中

        block := pem.Block{

        Type : "描述....",

        Bytes : MarshalECPrivateKey返回值中的切片字符串,

        }

      • 使用pem编码

        pem.Encode();

  2. 使用私钥进行数字签名

    1. 打开私钥文件, 将内容读出来 ->[]byte

    2. 使用pem进行数据解码 -> pem.Decode()

    3. 使用x509, 对私钥进行还原

      func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error)
      
    4. 对原始数据进行哈希运算 -> 散列值

    5. 进行数字签名

      func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)
      - 得到的r和s不能直接使用, 因为这是指针
      	应该将这两块内存中的数据进行序列化 -> []byte
      	func (z *Int) MarshalText() (text []byte, err error)
      
  3. 使用公钥验证数字签名

    1. 打开公钥文件, 将里边的内容读出 -> []byte

    2. pem解码 -> pem.Decode()

    3. 使用x509对公钥还原

      func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)
      
    4. 将接口 -> 公钥

    5. 对原始数据进行哈希运算 -> 得到散列值

    6. 签名的认证 - > ecdsa

      func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool
      - 参数1: 公钥
      - 参数2: 原始数据生成的散列值
      - 参数3,4: 通过签名得到的连个点
      	func (z *Int) UnmarshalText(text []byte) error
      

# 2.6 数字签名无法解决的问题

1539178819165

# 05 - 证书

公钥证书(Public-Key Certificate,PKC)其实和驾照很相似,里面记有姓名、组织、邮箱地址等个人信息,以及属于此人的公钥, 并由认证机构(Certification Authority、Certifying Authority, CA)施加数字签名。只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书也简称为证书(certificate)。

# 1.1 证书的应用场景

1539265302304

  1. Bob生成密钥对
    • 可以将bob看成百度, 提供是web服务器
    • 生成一个密钥对
      • 公钥 -> 分发
      • 私钥 -> 百度留着
  2. Bob在认证机构Trent注册自己的公钥
    • 百度找了一大家都信赖的机构, 来证明这个公钥是百度的
    • 认证机构会生成一个证书, 写明了公钥属于百度
      • 认证机构也有一个非对称加密的密钥对
      • 认证机构使用自己的私钥对百度的公钥进行签名, 生成了证书
      • 认证机构将证书发送给百度
  3. 认证机构Trent用自己的私钥对Bob的公钥施加数字签名并生成证书
  4. Alice得到带有认证机构Trent的数字签名的Bob的公钥(证书)
    • alice可以看做一个客户 -> 浏览器
    • 客户端访问的百度 -> 得到了百度的证书
      • 证书中有百度的公钥
    • 客户端需要使用认证机构的公钥对证书进行验证
      • 客户端怎么会有认证机构的公钥
        • window会预装, 或者用户自己安装
  5. Alice使用认证机构Trent的公钥验证数字签名,确认Bob的公钥的合法性
    • 使用认证机构的公钥解除百度证书中签名的数据
      • 百度的公钥
      • 百度的域名
      • 百度证书的有效期
  6. Alice用Bob的公钥加密消息并发送给Bob
    • 非对称加密
    • 使用公钥加密 -> 对称加密秘钥分发
  7. Bob用自己的私钥解密密文得到Alice的消息
    • 服务器使用私钥解密 -> 得到对称加密的秘钥

# 1.2 证书规范和格式 -- x509

X.509是一种非常通用的证书格式。所有的证书都符合ITU-T X.509国际标准,因此(理论上)为一种应用创建的证书可以用于任何其他符合X.509标准的应用。X.509证书的结构是用ASN1(Abstract Syntax Notation One)进行描述数据结构,并使用ASN.1语法进行编码。

X.509规范中一般推荐使用PEM(Privacy Enhanced Mail)格式来存储证书相关的文件。

  • 证书文件的文件名后缀一般为 .crt 或 .cer
  • 对应私钥文件的文件名后缀一般为 .key
  • 证书请求文件的文件名后綴为 .csr
  • 有时候也统一用pem作为文件名后缀。

# 1.3 CA证书

CA证书顾名思义就是由CA(Certification Authority)机构发布的数字证书。要对CA证书完全理解及其作用,首先要理解SSL。SSL(security sockets layer,安全套接层)是为网络通信提供安全及数据完整性的一种安全协议。SSL3.0版本以后又被称为TLS。SSL位于TCP与各应用层之间,是操作系统向外提供的API。SSL如何保证网络通信的安全和数据的完整性呢?就是采用了两种手段:身份认证和数据加密。首先身份认证就需要用到CA证书了。

  1. 证书的获取和身份的认证

    客户端与服务端需要经过一个握手的过程才能完成身份认证,建立一个安全的连接。握手的过程如下:

    1. 客户端访问服务器(比如:https://www.12306.cn),发送ssl版本、客户端支持的加密算法等消息。

    2. 服务器向客户端发送ssl版本、加密算法、证书(证书出现了)等消息。

    3. 客户端收到消息后,判断证书是否可信, 若可信,则继续通信,发送消息:

      客户端生成一个随机数,从证书中获取服务器端的公钥,对随机数加密;

      随后信息都将使用双方协定的加密方法和密钥发送, 客户端握手结束。

    4. 服务器端对数据解密得到随机数, 使用协商好的加密算法和秘钥进行通信

  2. 客户端如何验证CA证书是可信任的?

    1. 查看证书的方式:

      Internet选项 -> 内容 -> 证书, 打开证书窗口查看已经安装的证书

      只要电脑上安装了该证书, 就说明该证书是受信任的。使用https协议访问时,服务器发送证书向浏览器时,首先查找该证书是否已在信任列表中,然后对证书进行校验,校验成功,那么就证明证书是可信的。

      下图中受信任的根证书颁发机构下的证书都是根证书。

      证书验证的机制是只要根证书是受信任的,那么它的子证书都是可信的。比如说,我们使用https协议访问了需要百度证书的网站,即使我们不安装百度证书,那么网站也不会提示证书不安全,因为,生成百度证书的根证书Globalsign Root CA - R1证书,在受信任的证书列表中。如果一个证书的根证书是不可信的,那么这个证书肯定也是不可信任的。

      由以上可知,根证书在证书验证中极其重要,而且,根证书是无条件信任的,只要我们将根证书安装上,就说明我们对根证书是信任的。比如我们安装12306的根证书,是出于我们对国家的信任,对网站的信任,我们才放心安装这个根证书。对于一些不安全的网站的证书,一定要慎重安装。

      另外需要知道的是,【受信任的根证书颁发机构】中的证书是windows预先安装的一些证书,都是国际上很有权威的证书机构,他们证书的生成都有很严格的流程,因此他们的证书被认为是安全,就像我们相信银行是安全,所以把钱存入到银行。

    1539272408707

    1. 证书的颁发机构 -> CA

      • 发布根证书
      • 中间证书
      • 个人
    2. 证书的信任链 -> 证书签发机构的信任链

      A是一个可信赖证书签发机构, A信任B, B就有资格去签发证书

      从等级上A比B高一级

  3. 有哪些CA机构?

    世界上较早的数字认证中心是美国的verisign威瑞信公司,在windows的证书窗口中可以看到好多verisign公司生成的证书, 美国的DigiCert

    另外还有加拿大的ENTRUST公司,也是很著名的证书机构。

    中国的安全认证体系分为金融CA和非金融CA。

    • 在金融CA方面,根证书由中国人民银行管理,
    • 非金融CA方面,由中国电信负责。
      • 行业性CA
        • 中国金融认证中心
        • 中国电信认证中心
      • 区域性CA, 区域性CA主要是以政府为背景,以企业机制运行
        • 广东CA中心
        • 上海CA中心

    沃通 -- www.wosign.com/products/ssl.htm

# 1.4 公钥基础设施 - PKI

  1. PKI组成的要素
    • 用户
      • 申请证书的人 -> web服务器端
        • 申请证书
          • 生成密钥对 , 或者委托ca生成
          • 将公钥发送给CA
          • ca使用自己的私钥对得到公钥签名
          • 将证书发送给用户
        • 发送证书
          • 当客户端访问服务器的时候发送证书给客户端
        • 注销证书
          • 当发现私钥泄露之后
      • 使用证书的人 -> 客户端
        • 接收证书
        • 验证对方的身份信息
    • CA认证机构
      • 可以生产密钥对(可选)
      • 对公钥签名
      • 吊销证书
    • 仓库
      • 存储证书 -> 公钥

# 2. SSL/TLS

  • SSL:(Secure Socket Layer,安全套接字层),为Netscape所研发,用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取。当前版本为3.0。它已被广泛地用于Web浏览器与服务器之间的身份认证和加密数据传输。 SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
  • TLS:(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。 TLS 1.0是IETF(Internet Engineering Task Force,Internet工程任务组)制定的一种新的协议,它建立在SSL 3.0协议规范之上,是SSL 3.0的后续版本,可以理解为SSL 3.1,它是写入了 RFC 的。该协议由两层组成: TLS 记录协议(TLS Record)和 TLS 握手协议(TLS Handshake)。较低的层为 TLS 记录协议,位于某个可靠的传输协议(例如 TCP)上面。

SSL/TLS协议提供的服务主要有:

  1. 认证用户和服务器,确保数据发送到正确的客户机和服务器;
  2. 加密数据以防止数据中途被窃取;
  3. 维护数据的完整性,确保数据在传输过程中不被改变。

  1. 描述的是客户端和服务器刚建立连接之后做的事情

    第一次

    • 客户端连接服务器
      • 客户端使用的ssl版本, 客户端支持的加密算法
    • 服务器
      • 先将自己支持ssl版本和客户端的支持的版本比较
        • 支持的不一样, 连接断开
        • 支持的一样, 继续
      • 根据得到的客户端支持 的加密算法, 找一个服务器端也同样支持算法, 发送给客户端
      • 需要发送服务器的证书给客户端

    第二次:

    客户端:

    • 接收服务器的证书
    • 校验证书的信息
      • 校验证书的签发机构
      • 证书的有效期
      • 证书中支持 的域名和访问的域名是否一致
    • 校验有问题, 浏览器会给提示

# 3. https -> 单向认证

  1. 服务器要准备的
    • 生成密钥对
    • 将公钥发送给ca, 由ca签发证书
    • 将ca签发的证书和非对称加密的私钥部署到当前的web服务器
  2. 通信流程
    1. 客户端连接服务器, 通过一个域名
      • 域名和IP地址的关系
        • 域名要绑定IP地址
          • 一个域名只能绑定一个IP地址
        • IP地址需要被域名绑定
          • 一个IP地址可以被多个域名绑定
      • 客户端访问的域名会别解析成IP地址, 通过IP地址访问web服务器
    2. 服务器收到了客户端的请求
      • 服务器将CA签发的证书发送给浏览器(客户端)
    3. 客户端拿到了服务器的公钥证书
      • 读这个公钥 证书
        • 验证域名
        • 有效期
        • ca签发机构
        • 服务器的公钥
    4. 客户会生成一个随机数 (作为对称加密的秘钥来使用的)
      • 使用服务器的公钥就这个随机数进行加密
      • 将这个加密之后 秘钥发送给服务器
    5. 服务器对收到的密文解密
      • 使用服务器的是要解密, 得到对称加密的秘钥
    6. 数据的传输
      • 使用对称加密的方式对数据进行加密

# 4. 自签名证书

  1. 使用openssl生成自签名证书

    1. 创建一个目录如Mytest, 进入该目录, 在该目录下打开命令行窗口

    2. 启动openssl

      openssl    # 执行该命令即可
      
    3. 使用openssl工具生成一个RSA私钥, 注意:生成私钥,需要提供一个至少4位的密码。

      genrsa -des3 -out server.key 2048
      	- des3: 使用3des对私钥进行加密
      
    4. 生成CSR(证书签名请求)

      req -new -key server.key -out server.csr
      
    5. 删除私钥中的密码, 第一步给私钥文件设置密码是必须要做的, 如果不想要可以删掉

      rsa -in server.key -out server.key
      	-out 参数后的文件名可以随意起
      
    6. 生成自签名证书

      x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
      

    # 证书总结

    1. 消息认证码

      • 是什么?
        • 散列值
      • 能干什么?
        • 保证数据的完整性, 一致性
      • 怎么生成?
        • 准备的条件: Hmac
          • 原始数据
          • 共享秘钥 -> 认证的另一方需要有同样的秘钥
          • 哈希算法
      • 弊端:
        • 秘钥分发困难
          • 使用非对称加密
        • 不能第三方认证
        • 不能防止否认
    2. 数字签名

      • 是什么?

        • 签名
          • 签名的人生成非对称加密的密钥对
          • 签名的人将公钥进行分发
          • 签名的人将原始数据进行哈希运算 -> 散列值
          • 签名的人使用自己的私钥对散列值进行非对称加密 -> 最终得到的数据就是签名
        • 校验:
          • 接收签名人的公钥
          • 接收签名人发送的数据和签名数据
          • 对原始数据进行哈希运算 -> 散列值
          • 使用公钥对签名数据解密
          • 将解密出的数据和散列值进行比较
            • 相等 == 成功
            • 不.. == 失败
      • 干什么?

        • 保证数据的一致性
        • 进行第三方认证
        • 可以防止否认
      • 能解决消息认证的弊端吗?

        • 可以
      • 怎么进行签名

        • RSA

        • 椭圆曲线签名 -> ecdsa

          1. 生成密钥对, 保存到文件中
          2. 对公钥进行分发
          3. 签名的人
            • 将私钥从磁盘读出
            • pem解码
            • x509解码 -> 私钥结构体
            • 对原始数据进行哈希运算 -> 散列值
            • 签名
          4. 验证签名的人
            • 将公钥从磁盘读出
            • pem解码
            • x509解码 -> 公钥
            • 生成原始数据的散列值
            • 签名认证

        数字签名的缺陷?

        • 验证签名的一方没有办法判断得到的公钥到底属于谁
上次更新: 6/21/2023, 2:56:12 AM