iGO实现之路 —— Security

96
ChainZhang
2018.01.23 18:24 字数 971

本文为转载,原文:iGO实现之路 —— Security

Golang

介绍

在我们写代码的过程中,可能会遇到很多的数据安全问题。比如我们在后端进行http请求的时候,url编码问题;用户登录密码数据库的保存方案;以及一些重要数据保存等。

这些都需要进行不同程度的编码,解码,加密解密。

那么,这些功能在golang中该如何实现呢?

源码

igo-github源码地址

base64

Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法。

base64的编码解码,在golang的官方包里已经有了很好的支持,这里为了统一,也进行了一些简单的封装。

base64编码

/*
* base64编码
*/
func Base64Encode(source string)string{
    buf := []byte(source)
    return base64.StdEncoding.EncodeToString(buf)
}

base64解码

/*
* base64解码
*/
func Base64Decode(source string) (string, error){
    buf, err := base64.StdEncoding.DecodeString(source)
    if err != nil{
        return "", err
    }
    return string(buf), nil
}

Url

因为url对字符是由限制的,一些特殊字符是不能出现先url里面的,比如:@$!#等。所以,在进行http请求的时候,我们需要对其进行url编码,在处理请求的时候也需要相应的url解码

url编码

/*
* Url编码
*/
func UrlEncode(source string)string{
    return url.QueryEscape(source)
}

url解码

/*
* Url解码
*/
func UrlDecode(source string) (string, error){
    return url.QueryUnescape(source)
}

md5

MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。

一般情况下,我们在保存用户密码的时候,都是将用户定义的密码通过md5加密得到的密文保存到数据库的。

当然了,在golang中对于md5的加密也是非常简单的:

/*
* md5加密
*/
func Md5(source string, isUpper bool)string{
    buf := []byte(source)
    has := md5.Sum(buf)
    md5Str := fmt.Sprintf("%x", has)
    if isUpper{
        md5Str = strings.ToUpper(md5Str)
    }
    return md5Str
}

des

DES(Data Encryption Standard)是对称加密算法,也就是加密和解密用相同的密钥。其入口参数有三个:key、data、mode。key为加密解密使用的密钥,data为加密解密的数据,mode为其工作模式。当模式为加密模式时,明文按照64位进行分组,形成明文组,key用于对数据加密,当模式为解密模式时,key用于对数据解密。实际运用中,密钥只用到了64位中的56位,这样才具有高的安全性。DES 的常见变体是三重 DES,使用 168 位的密钥对资料进行三次加密的一种机制;它通常(但非始终)提供极其强大的安全性。如果三个 56 位的子元素都相同,则三重 DES 向后兼容 DES。

des加密

/*
* DES加密,CBC模式,pkcs5padding,初始向量用key填充
*/
func DesEncrypt(origData, key string)(string, error){
    origBytes := []byte(origData)
    keyBytes := getDESKey(key)
    block, err := des.NewCipher(keyBytes)
    if err != nil{
        return "", err
    }
    origBytes = pkcs5Padding(origBytes, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, keyBytes)
    crypted := make([]byte, len(origBytes))

    blockMode.CryptBlocks(crypted, origBytes)
    return Base64Encode(string(crypted)), nil
}

des解密

/*
* DES解密,CBC模式,pkcs5padding,初始向量用key填充
*/
func DesDecrypt(crypted, key string)(string, error){
    crypted, _ = Base64Decode(crypted)
    cryptByts := []byte(crypted)
    keyByts := getDESKey(key)
    block, err := des.NewCipher(keyByts)
    if err != nil{
        return "", err
    }
    blockMode := cipher.NewCBCDecrypter(block, keyByts)
    origByts := make([]byte, len(cryptByts))
    blockMode.CryptBlocks(origByts, cryptByts)
    origByts = pkcs5UnPadding(origByts)
    return string(origByts), nil
}

注意:des加密解密的秘钥长度必须为8

三重des加密

/*
* 三重DES加密,CBC模式,pkcs5padding,初始向量用key填充
*/
func TripleDesEncrypt(origData, key string)(string, error){
    origBytes := []byte(origData)
    keyBytes := getTripleDESKey(key)
    block, err := des.NewTripleDESCipher(keyBytes)
    if err != nil{
        return "", err
    }
    origBytes = pkcs5Padding(origBytes, block.BlockSize())
    blockMode := cipher.NewCBCEncrypter(block, keyBytes[:8])
    crypted := make([]byte, len(origBytes))

    blockMode.CryptBlocks(crypted, origBytes)
    return Base64Encode(string(crypted)), nil
}

三重des解密

/*
* 三重DES解密,CBC模式,pkcs5padding,初始向量用key填充
*/
func TripleDesDecrypt(crypted, key string)(string, error){
    crypted, _ = Base64Decode(crypted)
    cryptByts := []byte(crypted)
    keyByts := getTripleDESKey(key)
    block, err := des.NewTripleDESCipher(keyByts)
    if err != nil{
        return "", err
    }
    blockMode := cipher.NewCBCDecrypter(block, keyByts[:8])
    origByts := make([]byte, len(cryptByts))
    blockMode.CryptBlocks(origByts, cryptByts)
    origByts = pkcs5UnPadding(origByts)
    return string(origByts), nil
}

注意:三重des加密解密的秘钥长度必须为24

aes

AES加密与DES加密一样,都是对称加密。但是相对来说,AES加密比DES更加安全,更加效率,更加灵活。

实现的话,也与DES类似。

aes加密

/*
* AES加密,CBC模式,pkcs5padding,初始向量用key填充
*/
func AesCBCEncrypte(origData, key string) (string, error) {
    origByts := []byte(origData)
    keybytes := getAESKey(key)
    plaintext := pkcs5Padding(origByts, aes.BlockSize)
    block, err := aes.NewCipher(keybytes[:aes.BlockSize])
    if err != nil{
        return "", err
    }
    mode := cipher.NewCBCEncrypter(block, keybytes[:aes.BlockSize])
    crypted := make([]byte, len(plaintext))
    mode.CryptBlocks(crypted, plaintext)
    return Base64Encode(string(crypted)), nil
}

aes解密

/*
* AES解密,CBC模式,pkcs5padding,初始向量用key填充
*/
func AesCBCDecrypte(crypted string, key string) (string, error) {
    defer func() {
        if err := recover(); err != nil {
            fmt.Fprintf(os.Stderr, "error string:%s key:%s err:%v\n", crypted, key, err)
        }
    }()

    keybytes := getAESKey(key)
    crypted, err := Base64Decode(crypted)
    if err != nil {
        return "", errors.New("crypted data format error")
    }
    cryptedData := []byte(crypted)
    block, err := aes.NewCipher(keybytes[:aes.BlockSize])
    if err != nil{
        return "", err
    }
    mode := cipher.NewCBCDecrypter(block, keybytes[:aes.BlockSize])

    decryptedData := make([]byte, len(cryptedData))
    mode.CryptBlocks(decryptedData, cryptedData)
    cryptedData = pkcs5UnPadding(decryptedData)
    return strings.TrimSpace(string(decryptedData)), nil
}

注意:aes的秘钥长度可以是32,24,16。

从des和aes的加密中,我们可以看到有几个自己定义的函数,比如获取key的,和pkcs5padding以及pkcs5unpadding。
下面看下这几个函数的实现

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)]
}

func getDESKey(key string)[]byte{
    key = Md5(key, false)
    keyBytes := []byte(key)
    return keyBytes[0:8]
}

func getTripleDESKey(key string)[]byte{
    key = Md5(key, false)
    keyBytes := []byte(key)
    return keyBytes[0:24]
}

func getAESKey(key string) []byte {
    key = Md5(key, false)
    keyLen := len(key)
    arrKey := []byte(key)
    if keyLen >= 32 {
        return arrKey[:32]
    }
    if keyLen >= 24 {
        return arrKey[:24]
    }
    return arrKey[:16]
}

测试

完成了这些加解密的算法之后,再测试一下吧:

package test

import(
    "fmt"

    "igo/util"
)

func Security_test(){
    source := "123456  "
    fmt.Println("md5 encode:", util.Md5(source, false))

    base64Str := util.Base64Encode(source)
    fmt.Println("base 64 encode: ", base64Str)
    base64DecodeStr, err := util.Base64Decode(base64Str)
    if err == nil{
        fmt.Println("base 64 decode: ", base64DecodeStr)
    }
    
    url := "http:www.baidu.com/s?wd=中国"
    urlEncode := util.UrlEncode(url)
    fmt.Println("url encode: ", urlEncode)
    urlDecode, err := util.UrlDecode(urlEncode)
    if err == nil{
        fmt.Println("url decode: ", urlDecode)
    }

    origData := "123456"
    key := "11"
    crypted, err := util.DesEncrypt(origData, key)
    if err != nil{
        fmt.Println("des encrypt error: ", err)
    }else{
        fmt.Println("des encrypt: ", crypted)
    }

    decrypted, err := util.DesDecrypt(crypted, key)
    if err != nil{
        fmt.Println("des decrypt error: ", err)
    }else{
        fmt.Println("des decrypt: ", decrypted)
    }

    crypted, err = util.TripleDesEncrypt(origData, key)
    if err != nil{
        fmt.Println("des encrypt error: ", err)
    }else{
        fmt.Println("triple des encrypt: ", crypted)
    }

    decrypted, err = util.TripleDesDecrypt(crypted, key)
    if err != nil{
        fmt.Println("triple des decrypt error: ", err)
    }else{
        fmt.Println("triple des decrypt: ", decrypted)
    }

    crypted, err = util.AesCBCEncrypte(origData, key)
    if err != nil{
        fmt.Println("aes error: ", err)
    }else{
        fmt.Println("aes encrypt: ", crypted)
    }

    decrypted, err = util.AesCBCDecrypte(crypted, key)
    if err != nil{
        fmt.Println("aes decrypt error: ", err)
    }else{
        fmt.Println("aes decrypt: ", decrypted)
    }
}

结果:


运行结果

转载请注明出处:
iGO实现之路 —— Security

iGO我的go语言库

golang
Gupao