密码学家的工具箱(golang代码实现)

· · 902 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本文参考《图解密码技术》第三版


   密码学家的工具箱,指六种常用加密方式包括 对称密码、非对称密码、单向散列函数、消息认证码、数字签名、伪随机数生成器。堪称六大杀器,现代社会的信息安全皆基于此。本文用go语言代码实现六种工具的具体应用。




对称密码

是指加密解密时用同一个秘钥,下面代码采用AES进行加解密,私钥长度只支持128,192,256位的字节数组。



AES加解密的代码应用:

package main

import (
    "bytes"
    "crypto/aes"
    "crypto/cipher"
    "fmt"
)

func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
    padding := blockSize - len(ciphertext)%blockSize
    padtext := bytes.Repeat([]byte{byte(padding)}, padding)
    return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
    length := len(origData)
    unpadding := int(origData[length-1])
    return origData[:(length - unpadding)]
}

func AesEncrypt(origData, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize()
    origData = PKCS5Padding(origData, blockSize)
    blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}

func AesDecrypt(crypted, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    blockSize := block.BlockSize()
    blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
    origData := make([]byte, len(crypted))
    blockMode.CryptBlocks(origData, crypted)
    origData = PKCS5UnPadding(origData)
    return origData, nil
}

func main() {
    //私钥长度只支持128 192 256位的任意字节数组
    var aeskey = []byte("这就是私钥,自己定义呦.")
    fmt.Println("私钥:", string(aeskey))
    fmt.Printf("私钥长度:%d字节\n", len(aeskey))
    pass := []byte("罗小黑战记大电影今天上映了,真好看,快去看啊")
    fmt.Println("原文:", string(pass))
    xpass, err := AesEncrypt(pass, aeskey)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("==============进行加密==============")
    fmt.Printf("加密信息:%x\n", xpass)
    fmt.Println("===================================")
    tpass, err := AesDecrypt(xpass, aeskey)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("==============进行解密==============")
    fmt.Printf("解密后的原文:%s\n", tpass)
    fmt.Println("===================================")
}

运行结果

私钥: 这就是私钥,自己定义呦.
私钥长度:32字节
原文: 罗小黑战记大电影今天上映了,真好看,快去看啊
==============进行加密==============
加密信息:e6ebbaea8b141561303dffee6ccab859e79d4d73b604ee1eb61c51126c728190723cd3069677f64ee933a94cc0a487838cb74da74411c65f7daae4d1a85163855a4835952e016e9056da8bec98681cc8
===================================
==============进行解密==============
解密后的原文:罗小黑战记大电影今天上映了,真好看,快去看啊
===================================



公钥密码

指秘钥拆分为公私钥,一般公钥加密,私钥进行解密。多用于数字签名,混合加密。


目前世界主流主要使用RSA与椭圆曲线算法(比特币使用此算法)
RSA:参考我之前写的文章
golang 使用RSA生成公私钥,加密,解密,并使用SHA256进行签名,验证
想了解椭圆曲线,看下方数字签名处实现的代码


单向散列函数

单向散列函数,又称单向Hash函数、杂凑函数,就是把任意长的输入消息串变化成固定长的输出串且由输出串难以得到输入串的一种函数。这个输出串称为该消息的散列值。一般用于产生消息摘要,密钥加密等。最重要是的这种过程是不可逆的


package main

import (
    "crypto/sha256"
    "crypto/sha512"
    "fmt"
    "golang.org/x/crypto/ripemd160"
    "golang.org/x/crypto/sha3"
)

func main() {
    value1 := []byte("我是一个粉刷匠粉刷本领强")

    sha256Byetes := sha256.Sum256(value1)
    hash512 := sha512.New()
    hash512.Write(value1)
    sha512Bytes := hash512.Sum(nil)

    hash3256 := sha3.New256()
    hash3256.Write(value1)
    sha3256bytes := hash3256.Sum(nil)

    hash3512 := sha3.New512()
    hash3512.Write(value1)
    sha3512bytes := hash3512.Sum(nil)

    rp := ripemd160.New()
    rp.Write(value1)
    ripemd160Bytes := rp.Sum(nil)

    fmt.Printf("原文:%s\n", value1)
    fmt.Println("--------------------------------------------------------------")
    fmt.Printf("ripedmd160散列:%x\n", ripemd160Bytes)
    fmt.Println("--------------------------------------------------------------")
    fmt.Printf("sha2加密:\n")
    fmt.Printf("sha256散列:%x\n", sha256Byetes)
    fmt.Printf("sha512散列:%x\n", sha512Bytes)
    fmt.Println("--------------------------------------------------------------")
    fmt.Printf("sha3加密:\n")
    fmt.Printf("sha256散列:%x\n", sha3256bytes)
    fmt.Printf("sha512散列:%x\n", sha3512bytes)
    fmt.Println("--------------------------------------------------------------")
}

运行结果:

原文:我是一个粉刷匠粉刷本领强
--------------------------------------------------------------
ripedmd160散列:ea4ce20570817f22b9cb02cb18695aedfb57a57b
--------------------------------------------------------------
sha2加密:
sha256散列:dacdb6d53965d2797ddceaafe763a1ddcc4de9c525fa369ce281f57b90411c1b
sha512散列:c9369f6ab4c097c7806dc5721a4bcc1c6cdfac3f080a497ef750e12e19c55e52ebef11c28ef2730eeef657db788c00819703cc97683ff68bdc6bae8fe99efc90
--------------------------------------------------------------
sha3加密:
sha256散列:d750995c194e4876d2328206c0fe78f18eb4cb49f3393216f1b6bd4483982c14
sha512散列:7109e9ed9127e964aaea5fb68b9b6576bd10a9cc33adee3f77c576f9a05d62ca1bea319b10e8baeac6f9d359bc7ab72ff506cf91883dd44332662a2251c4be8d
--------------------------------------------------------------



消息认证码

         消息认证码是一种确认完整性并进行认证的技术。
  在发送数据之前,发送方首先使用通信双方协商好的散列函数计算其摘要值。在双方共享的会话密钥作用下,由摘要值获得消息验证码。之后,它和数据一起被发送。接收方收到报文后,首先利用会话密钥还原摘要值,同时利用散列函数在本地计算所收到数据的摘要值,并将这两个数据进行比对。若两者相等,则报文通过认证。


package main

import (
    "bytes"
    "crypto/sha256"
    "fmt"
)

func main() {
    aliceStrOriginal := []byte("Alice向Bob转账100BTC")
    aliceSecretKey := []byte("abcdefj123456789")
    aliceXpass, err := AesEncrypt(aliceStrOriginal, aliceSecretKey)
    if err != nil {
        fmt.Println(err)
        return
    }
    aliceHash256 := sha256.Sum256(aliceXpass)

    strBobGetOriginal := []byte("Alice向Bob转账100BTC")
    bobSecretKey := []byte("abcdefj123456789")
    bobxpass, err := AesEncrypt(strBobGetOriginal, bobSecretKey)
    if err != nil {
        fmt.Println(err)
        return
    }
    bobHash256 := sha256.Sum256(bobxpass)
    isEqual := bytes.Equal(aliceHash256[:], bobHash256[:])

    fmt.Printf("原文:%s\n", aliceStrOriginal)
    fmt.Println("Alice使用秘钥对原文进行对称加密:")
    fmt.Printf("加密后的密文:%x\n", aliceXpass)
    fmt.Println("Alice对密文进行散列:")
    fmt.Printf("获取到消息认证码:%x\n", aliceHash256)
    fmt.Println("--------------------------------")
    fmt.Println("Alice将原文与消息认证码发送给Bob")
    fmt.Println("--------------------------------")
    fmt.Printf("Bob获取到原文:%s\n", strBobGetOriginal)
    fmt.Printf("Bob获取到消息认证码:%x\n", aliceHash256)
    fmt.Println("Bob开始进行验证")
    fmt.Println("Bob使用秘钥对原文进行对称加密:")
    fmt.Printf("加密后的密文:%x\n", bobxpass)
    fmt.Println("Bob对密文进行散列:")
    fmt.Printf("sha256的散列值:%x\n", bobHash256)
    fmt.Printf("消息认证码与Bob对密文hash后的散列值是否相等%v\n:", isEqual)
}

运行结果:

原文:Alice向Bob转账100BTC
Alice使用秘钥对原文进行对称加密:
加密后的密文:cc07345ae592c35ae0031d26920b6762aca834bfb5ac19dc9b581c4b557b6a4c
Alice对密文进行散列:
获取到消息认证码:6c6590e5f4a1c2dbc246adf3595fcf9edd5b3bf8f94c8f682b4cd08524c2697d
--------------------------------
Alice将原文与消息认证码发送给Bob
--------------------------------
Bob获取到原文:Alice向Bob转账100BTC
Bob获取到消息认证码:6c6590e5f4a1c2dbc246adf3595fcf9edd5b3bf8f94c8f682b4cd08524c2697d
Bob开始进行验证
Bob使用秘钥对原文进行对称加密:
加密后的密文:cc07345ae592c35ae0031d26920b6762aca834bfb5ac19dc9b581c4b557b6a4c
Bob对密文进行散列:
sha256的散列值:6c6590e5f4a1c2dbc246adf3595fcf9edd5b3bf8f94c8f682b4cd08524c2697d
消息认证码与Bob对密文hash后的散列值是否相等true



数字签名

数字签名就是将公钥密码反过来使用。签名者将讯息用私钥加密(这是一种反用,因为通常公钥密码中私钥用于解密),然后公布公钥;验证者使用公钥将加密讯息解密并比对消息。其中会用到Hash来对讯息进行散列以缩小公钥解密时间


RSA:参考我之前写的文章
golang 使用RSA生成公私钥,加密,解密,并使用SHA256进行签名,验证

下面是用椭圆曲线进行的数字签名模拟:

package main

import (
    "crypto/ecdsa"
    "crypto/elliptic"
    "crypto/rand"
    "crypto/sha256"
    "fmt"
    "log"
    "math/big"
)

type Keys struct {
    PrivateKey *ecdsa.PrivateKey
    PublicKey  []byte
}

func GetKeys() *Keys {
    k := &Keys{nil, nil}
    k.newKeyPair()
    return k
}

func (k *Keys) newKeyPair() {
    //曲线,设置位数
    curve := elliptic.P256()
    var err error
    k.PrivateKey, err = ecdsa.GenerateKey(curve, rand.Reader)
    if err != nil {
        log.Panic(err)
    }
    k.PublicKey = append(k.PrivateKey.PublicKey.X.Bytes(), k.PrivateKey.PublicKey.Y.Bytes()...)
}

//数字签名
func EllipticCurveSign(privKey *ecdsa.PrivateKey, hash []byte) []byte {
    r, s, err := ecdsa.Sign(rand.Reader, privKey, hash)
    if err != nil {
        log.Panic("EllipticCurveSign:", err)
    }
    signature := append(r.Bytes(), s.Bytes()...)
    return signature
}

//验证
func EllipticCurveVerify(pubKey []byte, signature []byte, hash []byte) bool {
    //拆分签名 得到 r,s
    r := big.Int{}
    s := big.Int{}
    sigLen := len(signature)
    r.SetBytes(signature[:(sigLen / 2)])
    s.SetBytes(signature[(sigLen / 2):])
    //拆分公钥字节数组,得到公钥对象
    x := big.Int{}
    y := big.Int{}
    keyLen := len(pubKey)
    x.SetBytes(pubKey[:(keyLen / 2)])
    y.SetBytes(pubKey[(keyLen / 2):])
    curve := elliptic.P256()
    rawPubKey := ecdsa.PublicKey{curve, &x, &y}
    //传入公钥,要验证的信息,以及签名
    if ecdsa.Verify(&rawPubKey, hash, &r, &s) == false {
        return false
    }
    return true
}

func main() {
    k := GetKeys()
    aliceMessage := []byte("马上国庆节了")
    aliceMessageHash := sha256.Sum256(aliceMessage)
    signInfo := EllipticCurveSign(k.PrivateKey, aliceMessageHash[:])
    fmt.Printf("alice准备传送的消息: %s\n", aliceMessage)
    fmt.Printf("消息散列值: %x\n", aliceMessageHash[:])
    fmt.Printf("签名消息: %x\n", signInfo)
    fmt.Println("--------------------------------------------")
    fmt.Println("alice将原文以及签名信息发送给bob")
    fmt.Println("--------------------------------------------")
    bobGetMessage := []byte("马上国庆节了")
    bobHash := sha256.Sum256(bobGetMessage)
    isOk := EllipticCurveVerify(k.PublicKey, signInfo, bobHash[:])
    fmt.Printf("Bob接收到的消息: %s\n", bobGetMessage)
    fmt.Printf("Bob的消息散列值: %x\n", bobHash)
    fmt.Printf("Bob接收到的签名信息: %x\n", signInfo)
    fmt.Printf("传入公钥对签名进行验证,验证是否通过: %v\n", isOk)
}

运行结果:

alice准备传送的消息: 马上国庆节了
消息散列值: f8ca3e62161180b7aa54780497a3b0794eca2fcab6e0a29ec5d6d4e04fc106fd
签名消息: 2e86ff8f6c8fba83b6eb5ac2dd025d8e090fbc6b9e9fdee433efc5828486407e392515caf7a4843233fdc48affddddbb79f399240887be3770e0ed117d72c257
--------------------------------------------
alice将原文以及签名信息发送给bob
--------------------------------------------
Bob接收到的消息: 马上国庆节了
Bob的消息散列值: f8ca3e62161180b7aa54780497a3b0794eca2fcab6e0a29ec5d6d4e04fc106fd
Bob接收到的签名信息: 2e86ff8f6c8fba83b6eb5ac2dd025d8e090fbc6b9e9fdee433efc5828486407e392515caf7a4843233fdc48affddddbb79f399240887be3770e0ed117d72c257
传入公钥对签名进行验证,验证是否通过: true



伪随机数生成器

随机数的三个阶段(随机安全性一阶更比一阶强):
随机性---不存在统计学偏差,是完全杂乱的数列
不可预测性---不能从过去的数列推测出下一个出现的数
不可重现性---除非将数列本身保存下来,否则不能重现相同的数列


package main

import (
    crand "crypto/rand"
    "fmt"
    "math/big"
    "math/rand"
    "time"
)

func main() {
    var seed int64 = 123456
    rand.Seed(seed)
    num1 := rand.Int()
    rand.Seed(time.Now().UnixNano())
    num2 := rand.Int() //[0,100)

    num3, err := crand.Int(crand.Reader, big.NewInt(1000000000000000000))
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println("仅满足随机数三大性质之一的随机性:")
    fmt.Println(num1)
    fmt.Println("-----------------------------------------")
    fmt.Println("满足随机数三大性质之一的随机性与不可预测性:")
    fmt.Println("虽然加入了当前时间作为种子,但是不可预测性非常弱")
    fmt.Println(num2)
    fmt.Println("go官方提供的更安全的随机数生成器,不可预测性较强,建议日常开发使用")
    fmt.Println(num3)
}

运行结果:

仅满足随机数三大性质之一的随机性:
3550330175404308690
-----------------------------------------
满足随机数三大性质之一的随机性与不可预测性:
虽然加入了当前时间作为种子,但是不可预测性非常弱
8849672050420766465
go官方提供的更安全的随机数生成器,不可预测性较强,建议日常开发使用
208239724264280855

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:

查看原文:密码学家的工具箱(golang代码实现)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

902 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传