目 录CONTENT

文章目录
Go

加密技术和网络安全

Sakura
2023-11-02 / 0 评论 / 0 点赞 / 34 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于405天前,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

加密技术和网络安全

1. 对称加密和非对称加密

1.1 对称加密

  • 明文和密文是可逆的

  • 加密和解密用的是同一组密钥

解密方必须实现知道密钥,并且密钥不能泄漏

典型的对称加密算法:

  1. DES ( Data Encryption Standard )

  2. AES ( Advanced Encryption Standard ) 在 DES 之后出的,更复杂,安全性更高

实际的加密过程中会进行分组加密:

对原始数据(明文)进行分组,每组 64 位,最后一组不足 64 位时按照移动规则填充,每一组上单独施加 DES 算法

对每组明文分别加密形成密文,最后拼接

  • CBC 密文分组链接模式:将当前明文与前一个密文分组进行异或运算,然后再进行加密,为了提高安全性

1.2 非对称加密

  • 使用公钥加密,使用私钥解密

一般是先生成私钥,然后用私钥通过某种操作生成公钥

公钥是公开的,私钥只有自己保存

相比于非对称加密算法:运算速度非常慢

典型的非对称加密算法:

  1. RSA ( Ron Rivest,Adi Shamir,Leonard Adleman )

  2. ECC ( Elliptic Curve Cryptography ) 椭圆曲线加密算法

1.3 文件加密解密代码

  1. 分组加密细节

  • 比如以 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 码值

  • 认证:双方都可以配备证书,防止身份被冒充

  • 握手阶段

  1. 客户端在发送请求之前先生成一个 AESKey ( 对称加密 ),然后使用 RSA ( 服务端公钥 ) 加密这个 AESKey 生成密文,发送出去

  2. 服务端拿到密文之后通过服务端私钥进行解密,获得客户端的AESKey

  • 数据传输阶段

  1. 客户端通过 AESKey 加密请求形成密文,通过网络发送给服务端

  2. 服务端通过收到密文之后用得到的 AESKsy 解密密文获得请求

服务端不主动发送的原因在于:

http 协议中服务端无法主动向用户发动请求,服务端只能被动响应

websocket 是可以由服务端向客户端发送数据

6. jwt 鉴权算法及 go 代码实现

6.1 jwt 基本介绍

  1. 用户输入用户名,密码进行登录

  2. 服务端校验登录成功后用自己的密钥生成一个 Token ( 一个看似随机的字符串,里面实际上包含用户的登录信息 ),并返回

  3. 客户端后续的所有请求都带上 Token,以向服务端证明自已经登录过了

  4. 服务端使用自己的密钥来验证 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。

  • 不可篡改:如果一个区块里的交易记录被篡改,那下一个区块里记录的哈希值就对不上。

0

评论区