加密技术和网络安全
1. 对称加密和非对称加密
1.1 对称加密
明文和密文是可逆的
加密和解密用的是同一组密钥
解密方必须实现知道密钥,并且密钥不能泄漏
典型的对称加密算法:
DES ( Data Encryption Standard )
AES ( Advanced Encryption Standard ) 在 DES 之后出的,更复杂,安全性更高
实际的加密过程中会进行分组加密:
对原始数据(明文)进行分组,每组 64 位,最后一组不足 64 位时按照移动规则填充,每一组上单独施加 DES 算法
对每组明文分别加密形成密文,最后拼接
CBC 密文分组链接模式:将当前明文与前一个密文分组进行异或运算,然后再进行加密,为了提高安全性
1.2 非对称加密
使用公钥加密,使用私钥解密
一般是先生成私钥,然后用私钥通过某种操作生成公钥
公钥是公开的,私钥只有自己保存
相比于非对称加密算法:运算速度非常慢
典型的非对称加密算法:
RSA ( Ron Rivest,Adi Shamir,Leonard Adleman )
ECC ( Elliptic Curve Cryptography ) 椭圆曲线加密算法
1.3 文件加密解密代码
分组加密细节
比如以 8 个字节为一组, 分完组之后最后一组剩下 3 个字节,那么填充的规则就是要填充九个字节就填充那个数字,需要填充 5 个字节,就可以剩下全部填充 5
如果刚好够 8 的倍数也要填充,否则最后一个字节如果是 3,那个误以为最后 3 个字节需要删掉,所以应该填充 8 个 8
// 文件加密
func FileEncryption(infile string, outfile string, algo int, key []byte) error {
// 打开输入文件
fin, err := os.Open(infile)
if err != nil {
return err
}
defer fin.Close()
// 打开输出文件
fout, err := os.OpenFile(outfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer fout.Close()
// 读出文件内容,对content进行加密
content, err := io.ReadAll(fin)
if err != nil {
return err
}
// 通过函数中定义的algo选择加密算法
// NewCipher根据加密算法的类型返回一个新的block
var block cipher.Block
switch algo {
case AES:
block, err = aes.NewCipher(key)
case DES:
block, err = des.NewCipher(key)
default:
return fmt.Errorf("unsurported encrypt algo %d", algo)
}
if err != nil {
return err
}
// blockSize返回block的大小
blockSize := block.BlockSize()
// 填充内容,返回填充后的内容
src := PKCS5.Padding(content, blockSize) // 加密算法的输入必须是blockSize的整倍数
dest := make([]byte, len(src)) // 密文跟明文长度相同
encrypter := cipher.NewCBCEncrypter(block, key) // CBC分组模式加密
encrypter.CryptBlocks(dest, src) // 加密
fout.Write(dest)
return nil
}
// 文件解密
func FileDecryption(infile string, outfile string, algo int, key []byte) error {
// 打开加密后的文件和目标文件
fin, err := os.Open(infile)
if err != nil {
return err
}
defer fin.Close()
fout, err := os.OpenFile(outfile, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer fout.Close()
// 读出加密后的文件内容
content, err := io.ReadAll(fin)
if err != nil {
return err
}
var block cipher.Block
switch algo {
case AES:
block, err = aes.NewCipher(key)
case DES:
block, err = des.NewCipher(key)
default:
return fmt.Errorf("unsurported encrypt algo %d", algo)
}
if err != nil {
return err
}
decrypted := make([]byte, len(content)) //密文跟明文长度相同
decrypter := cipher.NewCBCDecrypter(block, key) //CBC分组模式解密
decrypter.CryptBlocks(decrypted, content) //解密
out, err := PKCS5.Unpadding(decrypted, block.BlockSize())
if err != nil {
return err
}
fout.Write(out)
return nil
}
func main() {
keyAES := []byte("ir489u58ir489u54") //AES算法key必须是长度为16的byte数组(128bit)。对称加密,加密的解密使用相同的key
plainFile := "Go/FileEncryption/Data/Student.txt"
// encryptFile 表示加密后的文件
encryptFileAES := "Go/FileEncryption/Data/加密后.aes"
// plainFile 表示对 encryptFIleAES 解密后的文件
plainFileAES := "Go/FileEncryption/Data/解密后.txt"
if err := FileEncryption(plainFile, encryptFileAES, AES, keyAES); err != nil {
fmt.Println(err)
} else {
if err = FileDecryption(encryptFileAES, plainFileAES, AES, keyAES); err != nil {
fmt.Println(err)
}
}
//keyDES := []byte("ir489u58") // DES算法key必须是长度为8的byte数组(64bit)
//encryptFileDES := util.RootPath + "/data/学生信息表.des"
//plainFileDES := util.RootPath + "/data/学生信息表(解密des).xlsx"
//if err := FileEncryption(plainFile, encryptFileDES, DES, keyDES); err != nil {
// fmt.Println(err)
//} else {
// if err = FileDecryption(encryptFileDES, plainFileDES, DES, keyDES); err != nil {
// fmt.Println(err)
// }
//}
}
var (
ErrPaddingSize = errors.New("padding size error")
)
// pkcs7padding和pkcs5padding的填充方式相同,填充字节的值都等于填充字节的个数。例如需要填充4个字节,则填充的值为"4 4 4 4"。
var (
PKCS5 = &pkcs5{}
)
var (
// difference with pkcs5 only block must be 8
PKCS7 = &pkcs5{}
)
// pkcs5Padding is a pkcs5 padding struct.
type pkcs5 struct{}
// Padding 计算填充字节数,并追加到src后面
func (p *pkcs5) Padding(src []byte, blockSize int) []byte {
srcLen := len(src)
// padLen 表示填充字节的个数
// blockSize 表示分组大小
padLen := blockSize - (srcLen % blockSize)
padText := bytes.Repeat([]byte{byte(padLen)}, padLen)
return append(src, padText...)
}
// Unpadding 加密完成之后,需要去除填充字节
func (p *pkcs5) Unpadding(src []byte, blockSize int) ([]byte, error) {
srcLen := len(src)
paddingLen := int(src[srcLen-1])
if paddingLen >= srcLen || paddingLen > blockSize {
return nil, ErrPaddingSize
}
return src[:srcLen-paddingLen], nil
}
2. 数字签名
3. 数字证书和 PKI 体系
4. https 和 TLS 的实现原理
http 作为一个应用层协议,应用层的数据是没有经过加密就直接丢给了下一层传输层 ( TCP )
所以在 http 和 TCP 中件就多了一层负责加密应用层数据,TLS ( Transport Layer Security , 安全传输层 ) 将应用层的报文包括 ( url ) 进行加密后再交由 TCP 进行传输
TLS 在 SSL v3.0 的基础上,提供了一次增强功能,两者差别很小
保密性:信息传输加密 ( 对称加密 )
完整性:MAC 检验 ( 散列函数 )
网站上的安装包旁边的哈希码,下载安装包之后通过对应的 Hash 函数对安装包进行一次 Hash 算法,对比两个 Hash 码值
认证:双方都可以配备证书,防止身份被冒充
握手阶段
客户端在发送请求之前先生成一个 AESKey ( 对称加密 ),然后使用 RSA ( 服务端公钥 ) 加密这个 AESKey 生成密文,发送出去
服务端拿到密文之后通过服务端私钥进行解密,获得客户端的AESKey
数据传输阶段
客户端通过 AESKey 加密请求形成密文,通过网络发送给服务端
服务端通过收到密文之后用得到的 AESKsy 解密密文获得请求
服务端不主动发送的原因在于:
http 协议中服务端无法主动向用户发动请求,服务端只能被动响应
websocket 是可以由服务端向客户端发送数据
6. jwt 鉴权算法及 go 代码实现
6.1 jwt 基本介绍
用户输入用户名,密码进行登录
服务端校验登录成功后用自己的密钥生成一个 Token ( 一个看似随机的字符串,里面实际上包含用户的登录信息 ),并返回
客户端后续的所有请求都带上 Token,以向服务端证明自已经登录过了
服务端使用自己的密钥来验证 Token 的合法性
6.3 jwt 的生成和验证
jwt 的生成包含三部分
验证 token 的合法性质
payload 以明文形式在网络上传输,Jwt 一般配合 https 使用
6.4 Jwt go代码实现
//JWT: Json Web Token
const (
JWT_SECRET = "0000" //密钥必须在server端保管好,绝对不能外泄
)
var (
DefautHeader = JwtHeader{
Algo: "HS256",
Type: "JWT",
}
)
type JwtHeader struct {
Algo string `json:"alg"` //哈希算法,默认为HMAC SHA256(写为 HS256)
Type string `json:"typ"` //令牌(token)类型,统一写为JWT
}
type JwtPayload struct {
ID string `json:"jti"` //JWT ID用于标识该JWT
Issue string `json:"iss"` //发行人。比如微信
Audience string `json:"aud"` //受众人。比如王者荣耀
Subject string `json:"sub"` //主题
IssueAt int64 `json:"iat"` //发布时间,精确到秒
NotBefore int64 `json:"nbf"` //在此之前不可用,精确到秒
Expiration int64 `json:"exp"` //到期时间,精确到秒
UserDefined map[string]any `json:"ud"` //用户自定义的其他字段
}
func GenJWT(header JwtHeader, payload JwtPayload) (string, error) {
var part1, part2, signature string
//header转成json,然后进行Base64编码
if bs1, err := json.Marshal(header); err != nil {
return "", err
} else {
part1 = base64.RawURLEncoding.EncodeToString(bs1) //这里没有使用StdEncoding,RawURLEncoding的结果中不会包含=+/等url中的特殊字符
}
//payload转成json,然后进行Base64编码
if bs2, err := json.Marshal(payload); err != nil {
return "", err
} else {
part2 = base64.RawURLEncoding.EncodeToString(bs2)
}
//基于sha256的哈希认证算法。任意长度的字符串,经过sha256之后长度都变成了256 bits
h := hmac.New(sha256.New, []byte(JWT_SECRET))
//signature = HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
h.Write([]byte(part1 + "." + part2))
signature = base64.RawURLEncoding.EncodeToString(h.Sum(nil))
return part1 + "." + part2 + "." + signature, nil
}
func VerifyJwt(token string) (*JwtHeader, *JwtPayload, error) {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, nil, fmt.Errorf("token是%d部分", len(parts))
}
//进行哈希签名的验证
h := hmac.New(sha256.New, []byte(JWT_SECRET))
h.Write([]byte(parts[0] + "." + parts[1]))
signature := base64.RawURLEncoding.EncodeToString(h.Sum(nil))
if signature != parts[2] { //验证失败
return nil, nil, fmt.Errorf("验证失败")
}
var part1, part2 []byte
var err error
if part1, err = base64.RawURLEncoding.DecodeString(parts[0]); err != nil {
return nil, nil, fmt.Errorf("header Base64反解失败")
}
if part2, err = base64.RawURLEncoding.DecodeString(parts[1]); err != nil {
return nil, nil, fmt.Errorf("payload Base64反解失败")
}
var header JwtHeader
var payload JwtPayload
if err = json.Unmarshal(part1, &header); err != nil {
return nil, nil, fmt.Errorf("header json反解失败")
}
if err = json.Unmarshal(part2, &payload); err != nil {
return nil, nil, fmt.Errorf("payload json反解失败")
}
return &header, &payload, nil
}
7. 区块链核心算法
区块链是一个去中心化的、分布式存储(不仅仅是记账)系统
只要有足够的机器资源(截止2023年6月,比特币区块链大小接近500G),每个人都可以参与到区块链当中,成为它的一个存储节点
钱包是 web3 的入口,它帮我们把口令转为以太坊账户。以太坊账户是公开的,口令需要自己保管(建议刻在铁牌上,防火),钱包系统不保管我们的口令
数据被分割成很多块( 在比特币中一个区块最大是1M ),这些区块的序链接起来构成完整的账本一一 区块链
7.1 记录的生成
对于新产生的一条记录,区块链上的所有节点都要验证它的真实性,然后记录在自己的账本
A 向 B 发送 10 个比特币,这个数据通过 A 的私钥进行加密,其他节点收到数据,通过A的公钥解密,验证是否真实性
7.3 区块的生成
7.4 区块链特点
真实性:通过数据签名技术,保证交易记录的真实性
有效性:即A向B转1OBTC,需要保证 A 的账户余额上至少有 10 BTC。由于每个节点都记录的完整的区块链账本,通过回溯可以确定 A 的账户余额够不够 10 BTC。
不可篡改:如果一个区块里的交易记录被篡改,那下一个区块里记录的哈希值就对不上。
评论区