区块链 - 密码学

第一章 密码学

第一节 密码介绍

一、为什么要加密?

保证数据的安全

小明–>小红:

  • 原文:放学后,学校后门小树林见

  • 密钥:+2

  • 密文:让他放引入学恩替后,润如学请问校嗯嗯后学校门抽成小版本树版本林你能见

  • 密钥(密码):-2

  • 解密:让他放引入学恩替后,润如学请问校嗯嗯后学校门抽成小版本树版本林你能见

    • 放学后,学校后门小树林见

二、常见的几种加密算法

1.编码解码

2.哈希加密

3.对称加密

4.非对称加密

5.数字签名

三、加密三要素

1.明文/密文

2.密钥

3.加密算法/解密算法

第二节 解码编码

一、常见的编码

  1. base64:26个小写字母、26个大写字母、10个数字、/、+

  2. 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
}

第五节 非对称加密

一、特点

加密和解密的密钥不同,有两个密钥(公钥和私钥)

公钥:可以公开的密钥;公钥加密,私钥解密

私钥:私密的密钥;私钥加密,公钥解密

私密单方向保证,只要有一方不泄露就没问题

二、优点

安全性相对对称加密高

三、缺点

加密效率低,适合小数据加密

四、场景分析

对哪一方更重要,哪一方就拿私钥

  1. 登录认证

私钥在客户端,个人信息在客户端保存

公钥在服务器,获取个人数据

假如:不成立的

​ 公钥在用户手里

​ 私钥在服务端

  1. 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
 }

上一篇:【51单片机】考试例程|汇编实现


下一篇:程序员的算法趣题Q65: 图形的一笔画