第一章 密码学
第一节 密码介绍
一、为什么要加密?
保证数据的安全
小明–>小红:
二、常见的几种加密算法
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"
)
var b58 = []byte("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
func Base58Encoding(src string) (string) {
src_byte := []byte(src)
i := big.NewInt(0).SetBytes(src_byte)
var mod_slice []byte
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()])
}
for _,s := range src_byte {
if s != 0 {
break
}
mod_slice = append(mod_slice, byte('1'))
}
ret_mod_slice := ReverseByteArr2(mod_slice)
fmt.Println(ret_mod_slice)
return string(ret_mod_slice)
}
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
}
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
}
- 解码
func Base58Decoding(src string) string{
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_new := md4.New()
md4Bytes := md4_new.Sum(src_byte)
md4String := hex.EncodeToString(md4Bytes)
四、go实现MD5加密
src_byte := []byte(src)
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"
)
var key []byte = []byte("hallenhallenhall")
func PadPwd(src_byte []byte,block_size int) []byte{
pad_num := block_size - len(src_byte) % block_size
ret := bytes.Repeat([]byte{byte(pad_num)},pad_num)
src_byte = append(src_byte, ret...)
return src_byte
}
func AesEncoding(src string) (string,error) {
src_byte := []byte(src)
block,err := aes.NewCipher(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)
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,_ = 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"
)
var des_key []byte = []byte("hallenha")
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)
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"
)
var tdes_key []byte = []byte("hallenhallenhallenhallen")
func TDesEncoding(src string) (string,error) {
src_byte := []byte(src)
block ,err := des.**NewTripleDESCipher**(tdes_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)
pwd := base64.StdEncoding.EncodeToString(dst)
return pwd,nil
}
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)
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,err := rsa.GenerateKey(rand.Reader,bits)
if err != nil {
fmt.Println(err)
return err
}
publicKey := privateKey.PublicKey
x509_privete := x509.MarshalPKCS1PrivateKey(privateKey)
x509_public := x509.MarshalPKCS1PublicKey(&publicKey)
block_private := pem.Block{Type:"private key",Bytes:x509_privete}
block_public := pem.Block{Type:"public key",Bytes:x509_public}
privateFile,err_pri := os.Create("privateKey.pem")
if err_pri != nil {
return err_pri
}
defer privateFile.Close()
pem.Encode(privateFile,&block_private)
publicFile,err_pub := os.Create("publicKey.pem")
if err_pub != nil {
return err_pub
}
defer publicFile.Close()
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)
block,_ := pem.Decode(key_bytes)
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)
block,_ := pem.Decode(key_bytes)
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, err := rsa.GenerateKey(rand.Reader, bits)
if err!=nil{
return err
}
publicKey := privateKey.PublicKey
x509_privete := x509.MarshalPKCS1PrivateKey(privateKey)
x509_public := x509.MarshalPKCS1PublicKey(&publicKey)
block_private := pem.Block{Type:"private key",Bytes:x509_privete}
block_public := pem.Block{Type:"public key",Bytes:x509_public}
privateFile,err_pri := os.Create("privateKey.pem")
if err_pri != nil {
return err_pri
}
defer privateFile.Close()
pem.Encode(privateFile,&block_private)
publicFile,err_pub := os.Create("publicKey.pem")
if err_pub != nil {
return err_pub
}
defer publicFile.Close()
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)
block,_ := pem.Decode(key_bytes)
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)
block,_ := pem.Decode(key_bytes)
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
}
|