第一章 密码学
第一节 密码介绍
一、为什么要加密?
保证数据的安全
小明–>小红:
-
原文:放学后,学校后门小树林见
-
密钥:+2
-
密文:让他放引入学恩替后,润如学请问校嗯嗯后学校门抽成小版本树版本林你能见
-
密钥(密码):-2
-
解密:让他放引入学恩替后,润如学请问校嗯嗯后学校门抽成小版本树版本林你能见
-
- 放学后,学校后门小树林见
二、常见的几种加密算法
1.编码解码
2.哈希加密
3.对称加密
4.非对称加密
5.数字签名
三、加密三要素
1.明文/密文
2.密钥
3.加密算法/解密算法
第二节 解码编码
一、常见的编码
-
base64:26个小写字母、26个大写字母、10个数字、/、+
-
base58(区块链):去掉6个容易混淆的,去掉0,大写O、大写I、小写L、/、+(64位-6位,还剩58位)
-
- /、+影响双击选择
二、go实现base64编码、解码
- 编码
使用:encoding/base64
input := []byte(“hello world”)
encodeString := base64.StdEncoding.EncodeToString(input)
在url中使用:
uEnc := base64.URLEncoding.EncodeToString([]byte(input))
- 解码
decodeBytes, err := base64.StdEncoding.DecodeString(encodeString)
在url中使用:
uDec, err := base64.URLEncoding.DecodeString(uEnc)
三、go实现base58编码解码、解码
base58编码表
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xwtlfxQk-1633784349826)(file:///C:/Users/ZHOUWE~1/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg)]
字符1代表0,字符2代表1,…,字符z代表57
-
编码
-
流程:
-
- 将字符串的每个字节换算成ASCII**(256进制,2^8(二进制)**),字符串实际上就是256进制的数字组合
- 将256进制的数字转换成10进制数字
- 将10进制数字转换成58进制数字(对比10进制转2进制,不断取余,0用编码1来代表)
- 将58进制数字对照58编码表找到对应的字符
-
256进制转十进制:
-
- h:104
- a:97
- 104*256+97=26721
-
package utils import ( "fmt" "math/big" "bytes" ) //58进制规则 var b58 = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") // base58编码 func Base58Encoding(src string) (string) { // he : 104 101 --> 104*256 + 101 = 26725 // 26725 / 58 = 15 16 17 //1.将字符串换算成ASCII码 src_byte := []byte(src) // 转成十进制 i := big.NewInt(0).SetBytes(src_byte) var mod_slice []byte // 循环取余 //for i.Cmp(big.NewInt(0)) != 0 { for i.Cmp(big.NewInt(0)) > 0 { mod := big.NewInt(0) i58 := big.NewInt(58) // 取余 i.DivMod(i,i58,mod) // 将余数添加到数组中 mod_slice = append(mod_slice, b58[mod.Int64()]) } // 把0使用字节'1'代替 for _,s := range src_byte { if s != 0 { break } mod_slice = append(mod_slice, byte('1')) } // 反转byte数组 //ret_mod_slice := ReverseByteArr(mod_slice) ret_mod_slice := ReverseByteArr2(mod_slice) fmt.Println(ret_mod_slice) return string(ret_mod_slice) } // byte数组进行反转方式 func ReverseByteArr(b []byte) []byte{ for i:=0; i<len(b)/2;i++ { b[i],b[len(b)-1-i] = b[len(b)-1-i],b[i] } return b } // byte数组进行反转方式2 func ReverseByteArr2(b []byte) []byte{ for i,j:=0,len(b)-1;i<j;i,j = i+1,j-1{ b[i] ,b[j] = b[j],b[i] } return b } - 解码 // base58解码(将编码流程反过来) func Base58Decoding(src string) string{ // 转成byte数组 src_byte := []byte(src) // 这里得到的是十进制 ret := big.NewInt(0) for _, b := range src_byte { i := bytes.IndexByte(b58,b) fmt.Println(i) // 乘回去 ret.Mul(ret,big.NewInt(58)) // 相加 ret.Add(ret,big.NewInt(int64(i))) } return string(ret.Bytes()) }
第三节 哈希算法
一、特点
**不可逆:**无法从一个哈希值恢复原始数据,哈希并不加密
**唯一性:**对于特定的数据 只能有一个哈希 并且这个哈希是唯一的
防篡改:改变输入数据中的一个字节 导致输出一个完全不同的哈希值
注册:账号,密码 存到数据库中的密码是md5加密后的
登录:将密码经过md5编码比对数据库中的密码
二、常用的哈希算法
- MD4
- MD5
- hash1
- sha224
- sha256
- sha384
- sha512
性能 : md4>md5>sha224>sha256>sha384>sha512
内存消耗:md5>md4>sha512>sha384>sha256=sha224
建议平常使用sha256即可,安全性可靠且消耗资源不高。
三、go实现MD4加密
src_byte := []byte(src)
//md4计算
md4_new := md4.New()
md4Bytes := md4_new.Sum(src_byte)
md4String := hex.EncodeToString(md4Bytes)
四、go实现MD5加密
src_byte := []byte(src)
//md5计算
md5_new := md5.New()
md5Bytes := md5_new.Sum(src_byte)
md5String := hex.EncodeToString(md5Bytes)
五、go实现SHA256加密
func GenSha256(src string) string {
src_byte := []byte(src)
hash := sha256.New()
sha256Bytes := hash.Sum(src_byte)
sha256String := hex.EncodeToString(sha256Bytes[:])
return sha256String
}
第四节 对称加密
一、特点
加密和解密使用的是同一个密钥
数据私密性双向保证,也就是加密和解密都不能泄露密码
二、优点
加密效率高,适合大些的数据加密
三、缺点
安全性相对非对称低
四、具体的加密方式
1. AES加密
- AES-128加密:key长度16字节
- AES-192加密:key长度24字节
- AES-256加密:key长度32字节
package utils
import (
"crypto/aes"
"fmt"
"bytes"
"encoding/base64"
"errors"
)
//key可以为16,24,32位的
var key []byte = []byte("hallenhallenhall") //key首字母小写,其他包不能引用(这里就是密钥)
// 填充密码长度,填充依据为,block.BlockSize(),密码长度必须为block.BlockSize()的整数倍
func PadPwd(src_byte []byte,block_size int) []byte{
// 假设密码长度16,区块长度 13 需要填充个数16-13 = 10
pad_num := block_size - len(src_byte) % block_size
ret := bytes.Repeat([]byte{byte(pad_num)},pad_num) //ret是要填充的数组成的数组
src_byte = append(src_byte, ret...) //拼接 ret...就是取数组中所有的值
return src_byte
}
// 加密
func AesEncoding(src string) (string,error) {
src_byte := []byte(src)
// safer
block,err := aes.NewCipher(key) //block是对象,里面是很多块
if err != nil {
return src,err
}
// 密码填充,加密的字符串不够block.BlockSize整数倍,要进行填充
new_src_byte := PadPwd(src_byte,block.BlockSize())
dst := make([]byte,len(new_src_byte)) //用于存放加密后的结果,这里使用切片
block.Encrypt(dst,new_src_byte) //加密dst存放结果,其为byte数组
// base64编码
pwd := base64.StdEncoding.EncodeToString(dst)
return pwd,nil
}
// 解密
func AesDecoding(pwd string) (string,error) {
pwd_byte := []byte(pwd)
pwd_byte,err := base64.StdEncoding.DecodeString(pwd)
if err != nil {
return pwd,err
}
block,err_block := aes.NewCipher(key)
if err_block != nil {
return pwd,err_block
}
dst := make([]byte,len(pwd_byte))
block.Decrypt(dst,pwd_byte) //解密 dst存放结果,其为byte数组
// 填充的要去掉
dst,_ = UnPadPwd(dst)
return string(dst),nil
}
//用于解密 去掉填充的部分
func UnPadPwd(dst []byte) ([]byte,error) {
if len(dst) <= 0 {
return dst,errors.New("长度有误")
}
// 去掉的长度 填充的值就是要填充的长度,所有取最好一个值,他肯定就是填充的长度
unpad_num := int(dst[len(dst)-1])
return dst[:(len(dst)-unpad_num)],nil //切片
}
2. des 加密:支持字节长度是8
package utils
import (
"crypto/des"
"encoding/base64"
)
// 只支持8字节的长度
var des_key []byte = []byte("hallenha")//key首字母小写,其他包不能引用(这里就是密钥)
// 加密
func DesEncoding(src string) (string,error) {
src_byte := []byte(src)
block ,err := des.NewCiphe(des_key)
if err != nil {
return src,err
}
// 密码填充
new_src_byte := PadPwd(src_byte,block.BlockSize())
dst := make([]byte,len(new_src_byte))
block.Encrypt(dst,new_src_byte)
// base64编码
pwd := base64.StdEncoding.EncodeToString(dst)
return pwd,nil
}
// 解密
func DesDecoding(pwd string) (string,error) {
pwd_byte,err := base64.StdEncoding.DecodeString(pwd)
if err != nil {
return pwd,err
}
block,err_block := des.NewCipher(des_key)
if err_block != nil {
return pwd,err_block
}
dst := make([]byte,len(pwd_byte))
block.Decrypt(dst,pwd_byte)
// 填充的要去掉
dst,_ = UnPadPwd(dst)
return string(dst),nil
}
3. 3des加密——CBC模式,key长度必须为24
package utils
import (
"crypto/des"
"encoding/base64"
)
// 3des的key,长度是24
var tdes_key []byte = []byte("hallenhallenhallenhallen")
// 3des加密
func TDesEncoding(src string) (string,error) {
src_byte := []byte(src)
block ,err := des.**NewTripleDESCipher**(tdes_key) // 和des的区别
if err != nil {
return src,err
}
// 密码填充
new_src_byte := PadPwd(src_byte,block.BlockSize())
dst := make([]byte,len(new_src_byte))
block.Encrypt(dst,new_src_byte)
// base64编码
pwd := base64.StdEncoding.EncodeToString(dst)
return pwd,nil
}
// 3des解密
func TDesDecoding(pwd string) (string,error) {
pwd_byte,err := base64.StdEncoding.DecodeString(pwd)
if err != nil {
return pwd,err
}
block,err_block := des.**NewTripleDESCipher**(tdes_key) // 和des的区别
if err_block != nil {
return pwd,err_block
}
dst := make([]byte,len(pwd_byte))
block.Decrypt(dst,pwd_byte)
// 填充的要去掉
dst,_ = UnPadPwd(dst)
return string(dst),nil
}
第五节 非对称加密
一、特点
加密和解密的密钥不同,有两个密钥(公钥和私钥)
公钥:可以公开的密钥;公钥加密,私钥解密
私钥:私密的密钥;私钥加密,公钥解密
私密单方向保证,只要有一方不泄露就没问题
二、优点
安全性相对对称加密高
三、缺点
加密效率低,适合小数据加密
四、场景分析
对哪一方更重要,哪一方就拿私钥
- 登录认证
私钥在客户端,个人信息在客户端保存
公钥在服务器,获取个人数据
假如:不成立的
公钥在用户手里
私钥在服务端
- A和B两个人,信息只允许B阅读
公钥在A
私钥在B
假如:所有人都可以阅读
公钥在B
私钥在A
五、具体的加密方式
RSA:三位作者的首字母
消息发送方利用对方的公钥进行加密,消息接受方收到密文时使用自己的私钥进行解密
注意:
- 公钥和密钥生成的时候要有一种关联
- 要把密钥和公钥保存起来
package utils
import (
"crypto/rsa"
"crypto/rand"
"fmt"
"crypto/x509"
"encoding/pem"
"os"
)
// 保存生成的公钥和密钥
func SaveRsaKey(bits int) (error){
//privateKey是指针结构
privateKey,err := rsa.GenerateKey(rand.Reader,bits)
if err != nil {
fmt.Println(err)
return err
}
//公钥要和私钥产生关联,//privateKey是一个结构体
publicKey := privateKey.PublicKey
// 使用x509标准对私钥进行编码,AsN.1编码字符串
x509_privete := x509.MarshalPKCS1PrivateKey(privateKey)
// 使用x509标准对公钥进行编码,AsN.1编码字符串
x509_public := x509.MarshalPKCS1PublicKey(&publicKey)//这里参数为指针类型
// 对私钥封装block 结构数据
block_private := pem.Block{Type:"private key",Bytes:x509_privete}
// 对公钥封装block 结构数据
block_public := pem.Block{Type:"public key",Bytes:x509_public}
// 创建存放私钥的文件,文件名后缀为.pem
privateFile,err_pri := os.Create("privateKey.pem")
if err_pri != nil {
return err_pri
}
//defer释放资源,将文件关闭
defer privateFile.Close()
//将私钥写入文件,block_private为指针类型,取地址符获得其内容
pem.Encode(privateFile,&block_private)
// 创建存放公钥的文件,文件名后缀为.pem
publicFile,err_pub := os.Create("publicKey.pem")
if err_pub != nil {
return err_pub
}
//defer释放资源,将文件关闭
defer publicFile.Close()
//将公钥写入文件,block_public为指针类型,取地址符获得其内容
pem.Encode(publicFile,&block_public)
return nil
}
// 加密
func RsaEncoding(src ,file_path string) ([]byte,error){
src_byte := []byte(src)
// 打开文件
file,err := os.Open(file_path)
if err != nil {
return src_byte,err
}
// 获取文件信息
file_info,err_info := file.Stat()
if err_info != nil {
return src_byte,err_info
}
// 存储读取文件内容所用的切片
key_bytes := make([]byte,file_info.Size())
// 读取内容到容器里面
file.Read(key_bytes)
// pem解码
block,_ := pem.Decode(key_bytes)
// x509解码
publicKey ,err_pb := x509.ParsePKCS1PublicKey(block.Bytes)
if err_pb != nil {
return src_byte,err_pb
}
// 使用公钥对明文进行加密
ret_byte,err_ret := rsa.EncryptPKCS1v15(rand.Reader,publicKey,src_byte)
if err_ret != nil {
return src_byte,err_ret
}
return ret_byte,nil
}
// 解密
func RsaDecoding(src_byte []byte,file_path string) ([]byte,error) {
// 打开文件
file,err := os.Open(file_path)
if err != nil {
return src_byte,err
}
// 获取文件信息
file_info,err_info := file.Stat()
if err_info != nil {
return src_byte,err_info
}
// 读取文件内容
key_bytes := make([]byte,file_info.Size())
// 读取内容到容器里面
file.Read(key_bytes)
// pem解码
block,_ := pem.Decode(key_bytes)
// x509解码
privateKey ,err_pb := x509.ParsePKCS1PrivateKey(block.Bytes)
if err_pb != nil {
return src_byte,err_pb
}
// 进行解密
ret_byte, err_ret := rsa.DecryptPKCS1v15(rand.Reader,privateKey,src_byte)
if err_ret != nil {
return src_byte,err_ret
}
return ret_byte,nil
}
第六节 数字签名
一、数字签名介绍
数字签名相当于纸质合同的签字章盖
防篡改、防伪装、防否认
rsa
- 公钥加密
- 私钥解密
rsa数字签名:(数字签名与rsa加密正好相反,更具作用的目的不同)
- 私钥签名
- 公钥验证签名
二、go实现数字签名算法
package utils
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"os"
)
//保存生成的公钥和密钥
func SaveRsaSignKey(bits int)error{
//privateKey是指针结构
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err!=nil{
return err
}
//privateKey是一个结构体,公钥要和私钥产生关联
publicKey := privateKey.PublicKey
// 使用x509标准对私钥进行编码,AsN.1编码字符串
x509_privete := x509.MarshalPKCS1PrivateKey(privateKey)
// 使用x509标准对公钥进行编码,AsN.1编码字符串,参数为指针
x509_public := x509.MarshalPKCS1PublicKey(&publicKey)
// 对私钥封装block 结构数据
block_private := pem.Block{Type:"private key",Bytes:x509_privete}
// 对公钥封装block 结构数据
block_public := pem.Block{Type:"public key",Bytes:x509_public}
// 创建存放私钥的文件,文件名后缀为.pem
privateFile,err_pri := os.Create("privateKey.pem")
if err_pri != nil {
return err_pri
}
//defer释放资源,将文件关闭
defer privateFile.Close()
//将私钥写入文件,block_private为指针类型,取地址符获得其内容
pem.Encode(privateFile,&block_private)
// 创建存放公钥的文件,文件名后缀为.pem
publicFile,err_pub := os.Create("publicKey.pem")
if err_pub != nil {
return err_pub
}
//defer释放资源,将文件关闭
defer publicFile.Close()
//将公钥写入文件,block_public为指针类型,取地址符获得其内容
pem.Encode(publicFile,&block_public)
return nil
}
//获取私钥
func GetPivateKey(file_path string)(*rsa.PrivateKey,error) {
// 打开文件
file,err := os.Open(file_path)
if err != nil {
return &rsa.PrivateKey{},err
}
// 获取文件信息
file_info,err_info := file.Stat()
if err_info != nil {
return &rsa.PrivateKey{},err_info
}
// 读取文件内容
key_bytes := make([]byte,file_info.Size())
// 读取内容到容器里面
file.Read(key_bytes)
// pem解码
block,_ := pem.Decode(key_bytes)
// x509解码
privateKey ,err_pb := x509.ParsePKCS1PrivateKey(block.Bytes)
if err_pb != nil {
return &rsa.PrivateKey{},err_pb
}
return privateKey,nil
}
//获取公钥
func GetPublicKey(file_path string)(*rsa.PublicKey,error){
//打开文件
file, err := os.Open(file_path)
if err!=nil{
return &rsa.PublicKey{},err
}
// 获取文件信息
file_info, err_info := file.Stat()
if err_info != nil {
return &rsa.PublicKey{},err_info
}
// 存储读取文件内容所用的切片
key_bytes := make([]byte,file_info.Size())
// 读取内容到容器里面
file.Read(key_bytes)
// pem解码
block,_ := pem.Decode(key_bytes)
// x509解码
publicKey ,err_pb := x509.ParsePKCS1PublicKey(block.Bytes)
if err_pb != nil {
return &rsa.PublicKey{},err_pb
}
return publicKey,nil
}
//数组签名,使用私钥
func RsaGetSign(file_path string,src string)([]byte,error){
//拿到私钥
privateKey, err := GetPivateKey(file_path)
if err!=nil{
return []byte{},err
}
hash := sha256.New()
src_byte := []byte(src)
hash.Write(src_byte)
sha_bytes := hash.Sum(nil)
//签名
signPKCS1v15, err_sign := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.*SHA256*, sha_bytes)
if err!=nil{
return []byte{},err_sign
}
return signPKCS1v15,nil
}
//验证签名,使用的是公钥
func RsaVarifySign(sign []byte,file_path string,src string)(bool,error){
publicKey, err := GetPublicKey(file_path)
if err!=nil{
return false,nil
}
hash := sha256.New()
src_byte := []byte(src)
hash.Write(src_byte)
sha_bytes := hash.Sum(nil)
err_verify := rsa.VerifyPKCS1v15(publicKey, crypto.*SHA256*, sha_bytes, sign)
if err_verify!=nil{
return false,nil
}
return true,nil
}