我的专题我爱编程区块链研习社

密码学及golang实现

2018-05-27  本文已影响18人  ccup区块链

本文目的仅为各种加密算法,在日常使用中的调用操作,快速定位所在包。
具体的加密底层数学原理,还需查阅研究底层源码。

Hash哈希家族

Hash散列

作用:大大提高查询遍历时的效率
底层结构:一个数组,数组中每个元素中都存储的是一个链表。
记忆:拉链法、链表的数组

Java1.7对比:在java中HashMap中,数组中链表每次插入新节点时,是头插法,插入到最面!为什么这么做呢?(因为每次插入到最前面可以直接插入,而如果插入到最后面时,则还需要进行循环遍历!所以插入到最前面提高效率)

md5加密

package main
//包
import(
    "fmt"
    "crypto/md5"
    "encoding/hex"
)

func main(){
    data := []byte("hello world")
    s := fmt.Sprintf("%x", md5.Sum(data))
    fmt.Println(s)

    // 也可以用这种方式
    h := md5.New()
    h.Write(data)
    s = hex.EncodeToString(h.Sum(nil))
    fmt.Println(s)
}

Sha256加密

package main

import(
    "fmt"
    "crypto/sha256"
    "io"
    "log"
    "os"
)

func main() {

    // 第一种调用方法
    sum := sha256.Sum256([]byte("hello world\n"))
    fmt.Printf("%x\n", sum)

    // 第二种调用方法
    h := sha256.New()
    h.Write([]byte("hello world\n"))
    fmt.Printf("%x\n", h.Sum(nil))
}

Sha256文件哈希值

    // 对文件加密
    f, err := os.Open("test.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    h = sha256.New()
    if _, err := io.Copy(h, f); err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%x\n", h.Sum(nil))

Ripemd160加密

package main

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

func main() {
    hasher := ripemd160.New()
    hasher.Write([]byte("The quick brown fox jumps over the lazy dog"))
    hashBytes := hasher.Sum(nil)
    hashString := fmt.Sprintf("%x", hashBytes)
    fmt.Println(hashString)
}

对称加密和解密

模式、初始向量、填充

Des

key密钥长度8Byte

des加密

使用DES加密 (des.NewCipher) ,加密模式为CBC (cipher.NewCBCEncrypter(block, key)),填充方式 PKCS5Padding
密钥是8byte

import (
    "bytes"
    "crypto/cipher"
    "crypto/des"
    "encoding/base64"
    "fmt"
)
//加密函数:

func DesEncrypt(origData, key []byte) ([]byte, error) {

    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }
    origData = PKCS5Padding(origData, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, key)
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}

des解密

//解密函数:
func DesDecrypt(crypted, key []byte) ([]byte, error) {
    block, err := des.NewCipher(key)
    if err != nil {
        return nil, err
    }
    blockMode := cipher.NewCBCDecrypter(block, key)
    origData := make([]byte, len(crypted))
    blockMode.CryptBlocks(origData, crypted)
    origData = PKCS5UnPadding(origData)
    return origData, nil
}

3DES

Key密钥长度24Byte

对比DES,发现只是换了NewTripleDESCipher。不过,需要注意的是,密钥长度必须24byte,否则直接返回错误。

3des加密

//3DES加密
func TripleDesEncrypt(origData, key []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }
    origData = PKCS5Padding(origData, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, key[:8])
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}

3des解密

// 3DES解密
func TripleDesDecrypt(crypted, key []byte) ([]byte, error) {
    block, err := des.NewTripleDESCipher(key)
    if err != nil {
        return nil, err
    }
    blockMode := cipher.NewCBCDecrypter(block, key[:8])
    origData := make([]byte, len(crypted))
    blockMode.CryptBlocks(origData, crypted)
    origData = PKCS5UnPadding(origData)
    return origData, nil
}


AES

AES密钥长度规定16Byte、24Byte、32Byte:

引自golang源码注释
// NewCipher creates and returns a new cipher.Block.
// The key argument should be the AES key,
// either 16, 24, or 32 bytes to select
// AES-128, AES-192, or AES-256.

//golang源码
func NewCipher(key []byte) (cipher.Block, error) {
    k := len(key)
    switch k {
    default:
        return nil, KeySizeError(k)
    case 16, 24, 32:
        break
    }
    return newCipher(key)
}


AES加密

使用CBC模式+PKCS7 填充方式实现AES的加密和解密

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

//aes加密
func AesEncrypt(origData, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }
    blockSize := block.BlockSize()
    origData = PKCS7Padding(origData, blockSize)
    blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
    crypted := make([]byte, len(origData))
    blockMode.CryptBlocks(crypted, origData)
    return crypted, nil
}


AES解密

//AES解密
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 = PKCS7UnPadding(origData)
    return origData, nil
}



PKCS5Padding填充方式实现

//PKCS5Padding填充方式
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 次
    unpadding := int(origData[length-1])
    return origData[:(length - unpadding)]
}

PKCS7Padding填充方式实现

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

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

非对称加密和解密

RSA加密和解密

openssl生成私钥

openssl genrsa -out rsa_private_key.pem 1024  

openssl生成公钥

openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem  

实现 RSA 加密解密

package main  
  
import (  
    "crypto/rand"  
    "crypto/rsa"  
    "crypto/x509"  
    "encoding/base64"  
    "encoding/pem"  
    "errors"  
    "fmt"  
)  
  
// 可通过openssl产生  
//openssl genrsa -out rsa_private_key.pem 1024  
var privateKey = []byte(`  
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDfw1/P15GQzGGYvNwVmXIGGxea8Pb2wJcF7ZW7tmFdLSjOItn9
kvUsbQgS5yxx+f2sAv1ocxbPTsFdRc6yUTJdeQolDOkEzNP0B8XKm+Lxy4giwwR5
LJQTANkqe4w/d9u129bRhTu/SUzSUIr65zZ/s6TUGQD6QzKY1Y8xS+FoQQIDAQAB
AoGAbSNg7wHomORm0dWDzvEpwTqjl8nh2tZyksyf1I+PC6BEH8613k04UfPYFUg1
0F2rUaOfr7s6q+BwxaqPtz+NPUotMjeVrEmmYM4rrYkrnd0lRiAxmkQUBlLrCBiF
u+bluDkHXF7+TUfJm4AZAvbtR2wO5DUAOZ244FfJueYyZHECQQD+V5/WrgKkBlYy
XhioQBXff7TLCrmMlUziJcQ295kIn8n1GaKzunJkhreoMbiRe0hpIIgPYb9E57tT
/mP/MoYtAkEA4Ti6XiOXgxzV5gcB+fhJyb8PJCVkgP2wg0OQp2DKPp+5xsmRuUXv
720oExv92jv6X65x631VGjDmfJNb99wq5QJBAMSHUKrBqqizfMdOjh7z5fLc6wY5
M0a91rqoFAWlLErNrXAGbwIRf3LN5fvA76z6ZelViczY6sKDjOxKFVqL38ECQG0S
pxdOT2M9BM45GJjxyPJ+qBuOTGU391Mq1pRpCKlZe4QtPHioyTGAAMd4Z/FX2MKb
3in48c0UX5t3VjPsmY0CQQCc1jmEoB83JmTHYByvDpc8kzsD8+GmiPVrausrjj4p
y2DQpGmUic2zqCxl6qXMpBGtFEhrUbKhOiVOJbRNGvWW
-----END RSA PRIVATE KEY-----
`)  
  
//openssl  
//openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem  
var publicKey = []byte(`  
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDfw1/P15GQzGGYvNwVmXIGGxea
8Pb2wJcF7ZW7tmFdLSjOItn9kvUsbQgS5yxx+f2sAv1ocxbPTsFdRc6yUTJdeQol
DOkEzNP0B8XKm+Lxy4giwwR5LJQTANkqe4w/d9u129bRhTu/SUzSUIr65zZ/s6TU
GQD6QzKY1Y8xS+FoQQIDAQAB
-----END PUBLIC KEY-----    
`)  
  
// 加密  
func RsaEncrypt(origData []byte) ([]byte, error) {  
    //解密pem格式的公钥  
    block, _ := pem.Decode(publicKey)  
    if block == nil {  
        return nil, errors.New("public key error")  
    }  
    // 解析公钥  
    pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)  
    if err != nil {  
        return nil, err  
    }  
    // 类型断言  
    pub := pubInterface.(*rsa.PublicKey)  
    //加密  
    return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)  
}  
  
// 解密  
func RsaDecrypt(ciphertext []byte) ([]byte, error) {  
    //解密  
    block, _ := pem.Decode(privateKey)  
    if block == nil {  
        return nil, errors.New("private key error!")  
    }  
    //解析PKCS1格式的私钥  
    priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)  
    if err != nil {  
        return nil, err  
    }  
    // 解密  
    return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)  
}  
  
func main() {  
    data, _ := RsaEncrypt([]byte("hello world"))  
    fmt.Println(base64.StdEncoding.EncodeToString(data))  
    origData, _ := RsaDecrypt(data)  
    fmt.Println(string(origData))  
} 

数字签名算法

dsa

数字签名算法(DSA,Digital Signature Algorithm),是一种公开密钥算法,不能用于加密,只能用于数字签名。主要用作为接收者验证数字的完整性和数据发送者的身份,DSA算法的安全性基于解离散对数的困难性。

package main

import (
    "crypto/dsa"
    "crypto/rand"
    "fmt"
)

func main() {
    var params dsa.Parameters

    //生成参数 
    if e := dsa.GenerateParameters(&params, rand.Reader, dsa.L1024N160); e != nil {
        fmt.Println(e)
    }

    //生成私钥 
    var priv dsa.PrivateKey

    priv.Parameters = params
    if e := dsa.GenerateKey(&priv, rand.Reader); e != nil {
        fmt.Println(e)
    }

    //根据私钥生成公钥 
    pub := priv.PublicKey

    //消息 
    message := []byte("hello world")

    //使用私钥进行签名,产生整数对(r,s) 
    r, s, e := dsa.Sign(rand.Reader, &priv, message)
    if e != nil {
        fmt.Println(e)
    }

    //认证 
    fmt.Printf("认证 %q (r:%s,s:%s)\n", message, r, s)
    if dsa.Verify(&pub, message, r, s) {
        fmt.Println("认证正确!")
    } else {
        fmt.Println("认证失败!")
    }
}

椭圆曲线Dsa加密算法

椭圆曲线加密算法

ECDSA的全名是Elliptic Curve DSA,即椭圆曲线DSA。它是Digital Signature Algorithm (DSA)应用了椭圆曲线加密算法的变种。椭圆曲线算法的原理很复杂,但是具有很好的公开密钥算法特性,通过公钥无法逆向获得私钥。

  1. 签名过程

    假设要签名的消息是一个字符串:“Hello World!”。DSA签名的第一个步骤是对待签名的消息生成一个消息摘要。不同的签名算法使用不同的消息摘要算法。而ECDSA256使用SHA256生成256比特的摘要。
    摘要生成结束后,应用签名算法对摘要进行签名:
    产生一个随机数k
    利用随机数k,计算出两个大数r和s。将r和s拼在一起就构成了对消息摘要的签名。
    这里需要注意的是,因为随机数k的存在,对于同一条消息,使用同一个算法,产生的签名是不一样的。从函数的角度来理解,签名函数对同样的输入会产生不同的输出。因为函数内部会将随机值混入签名的过程。

  2. 验证过程
    关于验证过程,这里不讨论它的算法细节。从宏观上看,消息的接收方从签名中分离出r和s,然后利用公开的密钥信息和s计算出r。如果计算出的r和接收到的r值相同,则表示验证成功。否则,表示验证失败。

package main

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

func main() {
    //明文 
    message := []byte("Hello world")
    
    key, err := NewSigningKey()
    if err != nil {
        return
    }

    signature, err := Sign(message, key)

    fmt.Printf("签名后:%x\n", signature)
    if err != nil {
        return
    }

    if !Verify(message, signature, &key.PublicKey) {
        fmt.Println("验证失败!")
        return
    }else{
        fmt.Println("验证成功!")
    }
}

func NewSigningKey() (*ecdsa.PrivateKey, error) {
    key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
    return key, err
}

// Sign signs arbitrary data using ECDSA.
func Sign(data []byte, privkey *ecdsa.PrivateKey) ([]byte, error) {
    // hash message
    digest := sha256.Sum256(data)

    // sign the hash
    r, s, err := ecdsa.Sign(rand.Reader, privkey, digest[:])
    if err != nil {
        return nil, err
    }

    // encode the signature {R, S}
    // big.Int.Bytes() will need padding in the case of leading zero bytes
    params := privkey.Curve.Params()
    curveOrderByteSize := params.P.BitLen() / 8
    rBytes, sBytes := r.Bytes(), s.Bytes()
    signature := make([]byte, curveOrderByteSize*2)
    copy(signature[curveOrderByteSize-len(rBytes):], rBytes)
    copy(signature[curveOrderByteSize*2-len(sBytes):], sBytes)

    return signature, nil
}

// Verify checks a raw ECDSA signature.
// Returns true if it's valid and false if not.
func Verify(data, signature []byte, pubkey *ecdsa.PublicKey) bool {
    // hash message
    digest := sha256.Sum256(data)

    curveOrderByteSize := pubkey.Curve.Params().P.BitLen() / 8

    r, s := new(big.Int), new(big.Int)
    r.SetBytes(signature[:curveOrderByteSize])
    s.SetBytes(signature[curveOrderByteSize:])

    return ecdsa.Verify(pubkey, digest[:], r, s)
}
上一篇 下一篇

猜你喜欢

热点阅读