# 01 - 对称加密
# 1.1 基础知识点
知识点大纲介绍
- 密码的基础概念
- 对称加密
- 非对称加密
- 单向散列函数
- 哈希函数
- 消息认证码
- 数字签名
- 证书
- ssl/tls - https
为什么要加密, 以及解决方案
保证数据安全
加密三要素
- 明文/密文
- 秘钥
- 定长的字符串
- 需要根据加密算法确定其长度
- 算法
- 加密算法
- 解密算法
- 加密算法和解密算法有可能是互逆的, 也有可能相同
常用的两种加密方式
- 对称加密
- 秘钥: 加密解密使用的是同一个秘钥, 秘钥有一个
- 特点
- 双方向保证机密性
- 加密效率高, 适合加密大数据, 大文件
- 加密强度不高, 相对于非对称加密
- 非对称加密
- 秘钥: 加密解密使用的不同的秘钥, 秘钥有两个, 需要使用秘钥生成算法, 得到密钥对
- 公钥 - 可以公开的秘钥
- 公钥加密数据, 解密需要使用私钥
- 私钥 - 需要妥善保管的秘钥, 知道的人越少越好
- 私钥加密, 公钥解密
- 公钥 - 可以公开的秘钥
- 特点:
- 数据的机密性只能单方向保证
- 加密效率低, 适合加密少量数据
- 加密强度高, 相对于对称加密
- 秘钥: 加密解密使用的不同的秘钥, 秘钥有两个, 需要使用秘钥生成算法, 得到密钥对
- 对称加密
凯撒密码
恺撒密码(Caesar cipher)是一种相传尤利乌斯·恺撒曾使用过的密码。恺撒于公元前100年左右诞生于古罗马,是一位著名的军事统帅。
恺撤密码是通过将明文中所使用的字母表按照一定的字数“平移”来进行加密的。
凯撒密码加解密公式
- 加密
解密
凯撒密码中的加密三要素
- 明文/密文
- 明文: 小写字母表中的数据
- 密文: 大写字母表中的数据
- 秘钥
- 按照上图秘钥为3
- 算法
- 加密: +3
- 解密: -3
- 明文/密文
凯撒密码的安全性
不安全
密码信息安全常识
- 不要使用保密的密码算法(普通公司和个人)
- 使用低强度的密码比不进行任何加密更危险
- 任何密码总有一天都会被破解
- 密码只是信息安全的一部分
密码信息威胁
思考:信息安全处理必须要具备哪些特性?
# 1.2 对称加密
以分组为单位进行处理的密码算法称为分组密码(blockcipher)
编码的概念
G = 1024m
m = 1024kbyte
byte = 8bit
bit 0/1
计算机的操作对象并不是文字,而是由0和1排列而成的比特序列。
将现实世界中的东西映射为比特序列的操作称为编码(encoding)。
加密 -> 编码
解密 -> 解码
hello world -> 比特序列
h -> int 104 ->
DES -- Data Encryption Standard
- 现在使用DES方式加密,数据还安全吗?
- 不安全, 已经被破解了
- 是不是分组密码?
- 是, 先对数据进行分组, 然后在加密或解密
- DES的分组长度?
- 8byte == 64bit
- DES的秘钥长度?
- 56bit秘钥长度+8bit错误检测标志位 = 64bit == 8byte
- 现在使用DES方式加密,数据还安全吗?
3DES -- Triple-DES
- 3DES安全吗?
- 安全, 但是效率低
- 算法描述?
- 进行了3次des加密
- 是不是分组密码?
- 是
- 3DES分组长度?
- 8字节
- 3DES秘钥长度?
- 24字节, 在算法内部会被平均分成3份
- 3DES加密过程?
- 秘钥1 -> 加密, 秘钥2 -> 解密, 秘钥3 -> 加密
- 3DES解密过程?
- 秘钥1 -> 解密, 秘钥2 -> 加密, 秘钥3 -> 解密
- 3DES安全吗?
AES -- Advanced Encryption Standard
- AES安全吗?
- 安全, 效率高, 推荐使用的
- 是不是分组密码?
- 是
- AES分组长度?
- 128bit = 16字节
- AES秘钥长度?
- 128bit = 16字节
- 192bit = 24字节
- 256bit = 32字节
- go中的秘钥长度只能是16字节
- AES安全吗?
# 1.3 分组密码的模式
按位异或
第一步需要将数据转换为二进制
按位异或操作符: ^
两个标志位进行按位异或操作:
- 相同为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 按位异或 ===> 解密
ECB - Electronic Code Book, 电子密码本模式
- 特点: 简单, 效率高, 密文有规律, 容易被破解
- 最后一个明文分组必须要填充
- des/3des -> 最后一个分组填充满8字节
- aes -> 最后一个分组填充满16字节
- 不需要初始化向量
CBC - Cipher Block Chaining, 密码块链模式
- 特点: 密文没有规律, 经常使用的加密方式
- 最后一个明文分组需要填充
- des/3des -> 最后一个分组填充满8字节
- aes -> 最后一个分组填充满16字节
- 需要一个初始化向量 - 一个数组
- 数组的长度: 与明文分组相等
- 数据来源: 负责加密的人的提供的
- 加解密使用的初始化向量值必须相同
CFB - Cipher FeedBack, 密文反馈模式
- 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
- 需要一个初始化向量 - 一个数组
- 数组的长度: 与明文分组相等
- 数据来源: 负责加密的人的提供的
- 加解密使用的初始化向量值必须相同
- 不需要填充
OFB - Output-Feedback, 输出反馈模式
- 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
- 需要一个初始化向量 - 一个数组
- 数组的长度: 与明文分组相等
- 数据来源: 负责加密的人的提供的
- 加解密使用的初始化向量值必须相同
- 不需要填充
CTR - CounTeR, 计数器模式
- 特点: 密文没有规律, 明文分组是和一个数据流进行的按位异或操作, 最终生成了密文
- 不需要初始化向量
- go接口中的iv可以理解为随机数种子, iv的长度 == 明文分组的长度
- 不需要填充
最后一个明文分组的填充
- 使用cbc, ecb需要填充
- 要求:
- 明文分组中进行了填充, 然后加密
- 解密密文得到明文, 需要把填充的字节删除
- 要求:
- 使用 ofb, cfb, ctr不需要填充
- 使用cbc, ecb需要填充
初始化向量 - IV
- ecb, ctr模式不需要初始化向量
- cbc, ofc, cfb需要初始化向量
- 初始化向量的长度
- des/3des -> 8字节
- aes -> 16字节
- 加解密使用的初始化向量相同
- 初始化向量的长度
# 1.4 对称加密在go中的实现
des
3des
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发送数据, 使用对称加密的方式
- 生成一个非对称的秘钥对, bob生成
- bob将公钥发送给alice
- alice生成一个用于对称加密的秘钥
- alice使用bob的公钥就对称加密的秘钥进行加密, 并且发送给bob
- bob使用私钥就数据解密, 得到对称加密的秘钥
- 通信的双方使用写好的秘钥进行对称加密数据加密
# 2. 非对称加密的秘钥
- 不存在秘钥分发困难的问题
# 2.1 场景分析
数据对谁更重要, 谁就拿私钥
- 直观上看: 私钥比公钥长
- 使用第三方工具生成密钥对: 公钥文件xxx.pub xxx
通信流程, 信息加密 (A写数据, 发送给B, 信息只允许B读)
A: 公钥
B: 私钥
登录认证 (客户端要登录, 连接服务器, 向服务器请求个人数据)
客户端: 私钥
服务器: 公钥
数字签名(表明信息没有受到伪造,确实是信息拥有者发出来的,附在信息原文的后面)
- 发送信息的人: 私钥
- 收到信息的人: 公钥
网银U盾
- 个人: 私钥
- 银行拿公钥
# 3. 使用RSA非对称加密通信流程
要求: Alice 给 bob发送数据, 保证数据信息只有bob能看到
# 4. 生成RSA的秘钥对
# 4.1 一些概念
- x509证书规范、pem、base64
- pem编码规范 - 数据加密
- base64 - 对数据编码, 可逆
- 不管原始数据是什么, 将原始数据使用64个字符来替代
- a-z A-Z 0-9 + /
- 不管原始数据是什么, 将原始数据使用64个字符来替代
- ASN.1抽象语法标记
- PKCS1标准
# 4.2 密钥对生成流程
生成私钥操作流程概述
使用rsa中的GenerateKey方法生成私钥
func GenerateKey(random io.Reader, bits int) (priv *PrivateKey, err error)
- rand.Reader -> import "crypto/rand"
- 1024 的整数倍 - 建议
通过x509标准将得到的ras私钥序列化为ASN.1 的 DER编码字符串
func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte
将私钥字符串设置到pem格式块中
初始化一个pem.Block块
type Block struct { Type string // 得自前言的类型(如"RSA PRIVATE KEY") Headers map[string]string // 可选的头项 Bytes []byte // 内容解码后的数据,一般是DER编码的ASN.1结构 }
通过pem将设置好的数据进行编码, 并写入磁盘文件中
func Encode(out io.Writer, b *Block) error
- out - 准备一个文件指针
生成公钥操作流程
从得到的私钥对象中将公钥信息取出
type PrivateKey struct { PublicKey // 公钥 D *big.Int // 私有的指数 Primes []*big.Int // N的素因子,至少有两个 // 包含预先计算好的值,可在某些情况下加速私钥的操作 Precomputed PrecomputedValues }
通过x509标准将得到 的rsa公钥序列化为字符串
func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)
将公钥字符串设置到pem格式块中
type Block struct { Type string // 得自前言的类型(如"RSA PRIVATE KEY") Headers map[string]string // 可选的头项 Bytes []byte // 内容解码后的数据,一般是DER编码的ASN.1结构 }
通过pem将设置好的数据进行编码, 并写入磁盘文件
func Encode(out io.Writer, b *Block) error
# 5. RSA加解密
# 5.1 RSA加密
将公钥文件中的公钥读出, 得到使用pem编码的字符串
-- 读文件
将得到的字符串解码
-- pem.Decode
使用x509将编码之后的公钥解析出来
-- func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err error)
使用得到的公钥通过rsa进行数据加密
# 5.2 RSA解密
- 将私钥文件中的私钥读出, 得到使用pem编码的字符串
- 将得到的字符串解码
- 使用x509将编码之后的私钥解析出来
- 使用得到的私钥通过rsa进行数据解密
# 6. 哈希算法
# 6.1 概念
称谓: 单向散列函数, 哈希函数, 杂凑函数, 消息摘要函数
接收的输入: 原像
输出: 散列值, 哈希值, 指纹, 摘要
# 6.2 单向散列函数特性
将任意长度的数据转换成固定长度的数据
很强的抗碰撞性
不可逆
MD4/MD5
- 不安全
- 散列值长度: 128bit == 16byte
sha1
- 不安全
- 散列值长度: 160bit == 20byte
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倍
- 计算一个大文件比如1G文件的散列值
- 使用udp的方式分发秘钥, 进行一个对称加密的通信
- 服务器
- 生成密钥对
- 公钥发送给客户端
- 客户端
- 客户端收到了公钥
- 生成一个秘钥 - 用于对称加密
- 使用公钥加密, 发送给服务器
- 服务器
- sha224
# 对称加密和非对称加密总结
概念
加密三要素
- 明文/密文
- 秘钥
- 算法
对称加密和非对称加密
- 对称加密: 加解密使用同一个秘钥, 1个
- 效率高
- 非...: 密钥对
- 公钥加密, 私钥解密
- 私钥加密, 公钥解密
- 对称加密: 加解密使用同一个秘钥, 1个
对称加密中的公开的加密算法
- des
- 分组长度: 8字节
- 秘钥长度: 8字节
- 3des
- 分组长度: 8字节
- 秘钥长度: 24byte
- aes
- 分组长度: 16字节
- 秘钥长度: 16字节, 24字节, 32字节
- 在go的api中只能使用16字节
- des
对称加密的分组模式
EBC - 不推荐使用
CBC - 常用的方式
准备的数据:
- 初始化向量iv - 字符数组
- 长度 == 明文分组长度
- 加解密初始化向量值必须相同
- 秘钥
- 根据加密算法定
===========================
OFB - 不推荐使用
CFB - 不推荐使用
CTR - 推荐使用, 效率最高
# 03 - 消息认证码
# 1.1 消息认证
消息认证码(message authentication code)是一种确认完整性并进行认证的技术,取三个单词的首字母,简称为MAC。
思考改进方案?
从哈希函数入手
需要将要发送的数据进行哈希运算, 将哈希值和原始数据一并发送
需要在进行哈希运算的时候引入加密的步骤
- 在alice对数据进行哈希运算的时候引入一个秘钥, 让其参与哈希运算, 生成散列值
- bob对数据校验
- bob收到原始和散列值之后,
- 处理原始数据: 通过秘钥和哈希算法对原始数据生成散列值
- 散列值比较: 生成的散列值 和 接收到的散列值进行比对
- bob收到原始和散列值之后,
# 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 消息认证码的问题
- 弊端
- 有秘钥分发困难的问题
- 无法解决的问题
- 不能进行第三方证明
- 不能防止否认
# 04 - 数字签名
# 2.1 签名的生成和验证
- 签名
- 有原始数据对其进行哈希运算 -> 散列值
- 使用非对称加密的私钥对散列值加密 -> 签名
- 将原始数据和签名一并发送给对方
- 验证
- 接收数据
- 原始数据
- 数字签名
- 数字签名, 需要使用公钥解密, 得到散列值
- 对原始数据进行哈希运算得到新的散列值
# 2.2 非对称加密和数字签名
总结:
- 数据通信
- 公钥加密, 私钥解密
- 数字签名:
- 私钥加密, 公钥解密
# 2.3 数字签名的方法
# 2.4 使用RSA进行数字签名
使用rsa生成密钥对
- 生成密钥对
- 序列化
- 保存到磁盘文件
使用私钥进行数字签名
打开磁盘的私钥文件
将私钥文件中的内容读出
使用pem对数据解码, 得到了pem.Block结构体变量
x509将数据解析成私钥结构体 -> 得到了私钥
创建一个哈希对象 -> md5/sha1
给哈希对象添加数据
计算哈希值
使用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: 错误信息
使用公钥进行签名认证
打开公钥文件, 将文件内容读出 - []byte
使用pem解码 -> 得到pem.Block结构体变量
使用x509对pem.Block中的Bytes变量中的数据进行解析 -> 得到一接口
进行类型断言 -> 得到了公钥结构体
对原始消息进行哈希运算(和签名使用的哈希算法一致) -> 散列值
- 创建哈希接口
- 添加数据
- 哈希运算
签名认证 - 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
秘钥对称的生成, 并保存到磁盘
使用ecdsa生成密钥对
func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
将私钥写入磁盘
使用x509进行序列化
func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error)
将得到的切片字符串放入pem.Block结构体中
block := pem.Block{
Type : "描述....",
Bytes : MarshalECPrivateKey返回值中的切片字符串,
}
使用pem编码
pem.Encode();
将公钥写入磁盘
从私钥中得到公钥
使用x509进行序列化
func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)
将得到的切片字符串放入pem.Block结构体中
block := pem.Block{
Type : "描述....",
Bytes : MarshalECPrivateKey返回值中的切片字符串,
}
使用pem编码
pem.Encode();
使用私钥进行数字签名
打开私钥文件, 将内容读出来 ->[]byte
使用pem进行数据解码 -> pem.Decode()
使用x509, 对私钥进行还原
func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error)
对原始数据进行哈希运算 -> 散列值
进行数字签名
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)
使用公钥验证数字签名
打开公钥文件, 将里边的内容读出 -> []byte
pem解码 -> pem.Decode()
使用x509对公钥还原
func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)
将接口 -> 公钥
对原始数据进行哈希运算 -> 得到散列值
签名的认证 - > 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 数字签名无法解决的问题
# 05 - 证书
公钥证书(Public-Key Certificate,PKC)其实和驾照很相似,里面记有姓名、组织、邮箱地址等个人信息,以及属于此人的公钥, 并由认证机构(Certification Authority、Certifying Authority, CA)施加数字签名。只要看到公钥证书,我们就可以知道认证机构认定该公钥的确属于此人。公钥证书也简称为证书(certificate)。
# 1.1 证书的应用场景
- Bob生成密钥对
- 可以将bob看成百度, 提供是web服务器
- 生成一个密钥对
- 公钥 -> 分发
- 私钥 -> 百度留着
- Bob在认证机构Trent注册自己的公钥
- 百度找了一大家都信赖的机构, 来证明这个公钥是百度的
- 认证机构会生成一个证书, 写明了公钥属于百度
- 认证机构也有一个非对称加密的密钥对
- 认证机构使用自己的私钥对百度的公钥进行签名, 生成了证书
- 认证机构将证书发送给百度
- 认证机构Trent用自己的私钥对Bob的公钥施加数字签名并生成证书
- Alice得到带有认证机构Trent的数字签名的Bob的公钥(证书)
- alice可以看做一个客户 -> 浏览器
- 客户端访问的百度 -> 得到了百度的证书
- 证书中有百度的公钥
- 客户端需要使用认证机构的公钥对证书进行验证
- 客户端怎么会有认证机构的公钥
- window会预装, 或者用户自己安装
- Alice使用认证机构Trent的公钥验证数字签名,确认Bob的公钥的合法性
- 使用认证机构的公钥解除百度证书中签名的数据
- 百度的公钥
- 百度的域名
- 百度证书的有效期
- Alice用Bob的公钥加密消息并发送给Bob
- 非对称加密
- 使用公钥加密 -> 对称加密秘钥分发
- 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证书了。
证书的获取和身份的认证
客户端与服务端需要经过一个握手的过程才能完成身份认证,建立一个安全的连接。握手的过程如下:
客户端访问服务器(比如:https://www.12306.cn),发送ssl版本、客户端支持的加密算法等消息。
服务器向客户端发送ssl版本、加密算法、证书(证书出现了)等消息。
客户端收到消息后,判断证书是否可信, 若可信,则继续通信,发送消息:
客户端生成一个随机数,从证书中获取服务器端的公钥,对随机数加密;
随后信息都将使用双方协定的加密方法和密钥发送, 客户端握手结束。
服务器端对数据解密得到随机数, 使用协商好的加密算法和秘钥进行通信
客户端如何验证CA证书是可信任的?
查看证书的方式:
Internet选项 -> 内容 -> 证书, 打开证书窗口查看已经安装的证书
只要电脑上安装了该证书, 就说明该证书是受信任的。使用https协议访问时,服务器发送证书向浏览器时,首先查找该证书是否已在信任列表中,然后对证书进行校验,校验成功,那么就证明证书是可信的。
下图中
受信任的根证书颁发机构
下的证书都是根证书。证书验证的机制是只要根证书是受信任的,那么它的子证书都是可信的。比如说,我们使用https协议访问了需要百度证书的网站,即使我们不安装百度证书,那么网站也不会提示证书不安全,因为,生成百度证书的根证书
Globalsign Root CA - R1
证书,在受信任的证书列表中。如果一个证书的根证书是不可信的,那么这个证书肯定也是不可信任的。由以上可知,根证书在证书验证中极其重要,而且,根证书是无条件信任的,只要我们将根证书安装上,就说明我们对根证书是信任的。比如我们安装12306的根证书,是出于我们对国家的信任,对网站的信任,我们才放心安装这个根证书。对于一些不安全的网站的证书,一定要慎重安装。
另外需要知道的是,【
受信任的根证书颁发机构
】中的证书是windows预先安装的一些证书,都是国际上很有权威的证书机构,他们证书的生成都有很严格的流程,因此他们的证书被认为是安全,就像我们相信银行是安全,所以把钱存入到银行。
证书的颁发机构 -> CA
- 发布根证书
- 中间证书
- 个人
证书的信任链 -> 证书签发机构的信任链
A是一个可信赖证书签发机构, A信任B, B就有资格去签发证书
从等级上A比B高一级
有哪些CA机构?
世界上较早的数字认证中心是美国的verisign
威瑞信
公司,在windows的证书窗口中可以看到好多verisign公司生成的证书, 美国的DigiCert另外还有加拿大的ENTRUST公司,也是很著名的证书机构。
中国的安全认证体系分为金融CA和非金融CA。
- 在金融CA方面,根证书由中国人民银行管理,
- 非金融CA方面,由中国电信负责。
- 行业性CA
- 中国金融认证中心
- 中国电信认证中心
- 区域性CA, 区域性CA主要是以政府为背景,以企业机制运行
- 广东CA中心
- 上海CA中心
- 行业性CA
沃通 -- www.wosign.com/products/ssl.htm
# 1.4 公钥基础设施 - PKI
- PKI组成的要素
- 用户
- 申请证书的人 -> web服务器端
- 申请证书
- 生成密钥对 , 或者委托ca生成
- 将公钥发送给CA
- ca使用自己的私钥对得到公钥签名
- 将证书发送给用户
- 发送证书
- 当客户端访问服务器的时候发送证书给客户端
- 注销证书
- 当发现私钥泄露之后
- 申请证书
- 使用证书的人 -> 客户端
- 接收证书
- 验证对方的身份信息
- 申请证书的人 -> web服务器端
- 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协议提供的服务主要有:
- 认证用户和服务器,确保数据发送到正确的客户机和服务器;
- 加密数据以防止数据中途被窃取;
- 维护数据的完整性,确保数据在传输过程中不被改变。
描述的是客户端和服务器刚建立连接之后做的事情
第一次
- 客户端连接服务器
- 客户端使用的ssl版本, 客户端支持的加密算法
- 服务器
- 先将自己支持ssl版本和客户端的支持的版本比较
- 支持的不一样, 连接断开
- 支持的一样, 继续
- 根据得到的客户端支持 的加密算法, 找一个服务器端也同样支持算法, 发送给客户端
- 需要发送服务器的证书给客户端
- 先将自己支持ssl版本和客户端的支持的版本比较
第二次:
客户端:
- 接收服务器的证书
- 校验证书的信息
- 校验证书的签发机构
- 证书的有效期
- 证书中支持 的域名和访问的域名是否一致
- 校验有问题, 浏览器会给提示
- 客户端连接服务器
# 3. https -> 单向认证
- 服务器要准备的
- 生成密钥对
- 将公钥发送给ca, 由ca签发证书
- 将ca签发的证书和非对称加密的私钥部署到当前的web服务器
- 通信流程
- 客户端连接服务器, 通过一个域名
- 域名和IP地址的关系
- 域名要绑定IP地址
- 一个域名只能绑定一个IP地址
- IP地址需要被域名绑定
- 一个IP地址可以被多个域名绑定
- 域名要绑定IP地址
- 客户端访问的域名会别解析成IP地址, 通过IP地址访问web服务器
- 域名和IP地址的关系
- 服务器收到了客户端的请求
- 服务器将CA签发的证书发送给浏览器(客户端)
- 客户端拿到了服务器的公钥证书
- 读这个公钥 证书
- 验证域名
- 有效期
- ca签发机构
- 服务器的公钥
- 读这个公钥 证书
- 客户会生成一个随机数 (作为对称加密的秘钥来使用的)
- 使用服务器的公钥就这个随机数进行加密
- 将这个加密之后 秘钥发送给服务器
- 服务器对收到的密文解密
- 使用服务器的是要解密, 得到对称加密的秘钥
- 数据的传输
- 使用对称加密的方式对数据进行加密
- 客户端连接服务器, 通过一个域名
# 4. 自签名证书
使用openssl生成自签名证书
创建一个目录如Mytest, 进入该目录, 在该目录下打开命令行窗口
启动openssl
openssl # 执行该命令即可
使用openssl工具生成一个RSA私钥, 注意:生成私钥,需要提供一个至少4位的密码。
genrsa -des3 -out server.key 2048 - des3: 使用3des对私钥进行加密
生成CSR(证书签名请求)
req -new -key server.key -out server.csr
删除私钥中的密码, 第一步给私钥文件设置密码是必须要做的, 如果不想要可以删掉
rsa -in server.key -out server.key -out 参数后的文件名可以随意起
生成自签名证书
x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
# 证书总结
消息认证码
- 是什么?
- 散列值
- 能干什么?
- 保证数据的完整性, 一致性
- 怎么生成?
- 准备的条件: Hmac
- 原始数据
- 共享秘钥 -> 认证的另一方需要有同样的秘钥
- 哈希算法
- 准备的条件: Hmac
- 弊端:
- 秘钥分发困难
- 使用非对称加密
- 不能第三方认证
- 不能防止否认
- 秘钥分发困难
- 是什么?
数字签名
是什么?
- 签名
- 签名的人生成非对称加密的密钥对
- 签名的人将公钥进行分发
- 签名的人将原始数据进行哈希运算 -> 散列值
- 签名的人使用自己的私钥对散列值进行非对称加密 -> 最终得到的数据就是签名
- 校验:
- 接收签名人的公钥
- 接收签名人发送的数据和签名数据
- 对原始数据进行哈希运算 -> 散列值
- 使用公钥对签名数据解密
- 将解密出的数据和散列值进行比较
- 相等 == 成功
- 不.. == 失败
- 签名
干什么?
- 保证数据的一致性
- 进行第三方认证
- 可以防止否认
能解决消息认证的弊端吗?
- 可以
怎么进行签名
RSA
椭圆曲线签名 -> ecdsa
- 生成密钥对, 保存到文件中
- 对公钥进行分发
- 签名的人
- 将私钥从磁盘读出
- pem解码
- x509解码 -> 私钥结构体
- 对原始数据进行哈希运算 -> 散列值
- 签名
- 验证签名的人
- 将公钥从磁盘读出
- pem解码
- x509解码 -> 公钥
- 生成原始数据的散列值
- 签名认证
数字签名的缺陷?
- 验证签名的一方没有办法判断得到的公钥到底属于谁