概述

在项目开发过程中,当操作一些用户的隐私信息,诸如密码、帐户密钥等数据时,往往需要加密后可以在网上传输。这时,需要一些高效地、简单易用的加密算法加密数据,然后把加密后的数据存入数据库或进行其他操作;当需要读取数据时,把加密后的数据取出来,再通过算法解密。关于加密解密
当前我们项目中常用的加解密的方式无非三种.

对称加密, 加解密都使用的是同一个密钥, 其中的代表就是AES、DES
非对加解密, 加解密使用不同的密钥, 其中的代表就是RSA
签名算法, 如MD5、SHA1、HMAC等, 主要用于验证,防止信息被修改, 如:文件校验、数字签名、鉴权协议
Base64不是加密算法,它是一种数据编码方式,虽然是可逆的,但是它的编码方式是公开的,无所谓加密。本文也对Base64编码方式做了简要介绍。

AES

AES,即高级加密标准(Advanced Encryption Standard),是一个对称分组密码算法,旨在取代DES成为广泛使用的标准。AES中常见的有三种解决方案,分别为AES-128、AES-192和AES-256。
AES加密过程涉及到4种操作:字节替代(SubBytes)、行移位(ShiftRows)、列混淆(MixColumns)和轮密钥加(AddRoundKey)。解密过程分别为对应的逆操作。由于每一步操作都是可逆的,按照相反的顺序进行解密即可恢复明文。加解密中每轮的密钥分别由初始密钥扩展得到。算法中16字节的明文、密文和轮密钥都以一个4x4的矩阵表示。
AES 有五种加密模式:电码本模式(Electronic Codebook Book (ECB))、密码分组链接模式(Cipher Block Chaining (CBC))、计算器模式(Counter (CTR))、密码反馈模式(Cipher FeedBack (CFB))和输出反馈模式(Output FeedBack (OFB))

  1. import (
  2. "bytes"
  3. "crypto/aes"
  4. "fmt"
  5. "crypto/cipher"
  6. "encoding/base64"
  7. )
  8. func main() {
  9. orig := "hello world"
  10. key := "123456781234567812345678"
  11. fmt.Println("原文:", orig)
  12. encryptCode := AesEncrypt(orig, key)
  13. fmt.Println("密文:" , encryptCode)
  14. decryptCode := AesDecrypt(encryptCode, key)
  15. fmt.Println("解密结果:", decryptCode)
  16. }
  17. func AesEncrypt(orig string, key string) string {
  18. // 转成字节数组
  19. origData := []byte(orig)
  20. k := []byte(key)
  21. // 分组秘钥
  22. block, _ := aes.NewCipher(k)
  23. // 获取秘钥块的长度
  24. blockSize := block.BlockSize()
  25. // 补全码
  26. origData = PKCS7Padding(origData, blockSize)
  27. // 加密模式
  28. blockMode := cipher.NewCBCEncrypter(block, k[:blockSize])
  29. // 创建数组
  30. cryted := make([]byte, len(origData))
  31. // 加密
  32. blockMode.CryptBlocks(cryted, origData)
  33. return base64.StdEncoding.EncodeToString(cryted)
  34. }
  35. func AesDecrypt(cryted string, key string) string {
  36. // 转成字节数组
  37. crytedByte, _ := base64.StdEncoding.DecodeString(cryted)
  38. k := []byte(key)
  39. // 分组秘钥
  40. block, _ := aes.NewCipher(k)
  41. // 获取秘钥块的长度
  42. blockSize := block.BlockSize()
  43. // 加密模式
  44. blockMode := cipher.NewCBCDecrypter(block, k[:blockSize])
  45. // 创建数组
  46. orig := make([]byte, len(crytedByte))
  47. // 解密
  48. blockMode.CryptBlocks(orig, crytedByte)
  49. // 去补全码
  50. orig = PKCS7UnPadding(orig)
  51. return string(orig)
  52. }
  53. //补码
  54. func PKCS7Padding(ciphertext []byte, blocksize int) []byte {
  55. padding := blocksize - len(ciphertext)%blocksize
  56. padtext := bytes.Repeat([]byte{byte(padding)}, padding)
  57. return append(ciphertext, padtext...)
  58. }
  59. //去码
  60. func PKCS7UnPadding(origData []byte) []byte {
  61. length := len(origData)
  62. unpadding := int(origData[length-1])
  63. return origData[:(length - unpadding)]
  64. }

DES

DES是一种对称加密算法,又称为美国数据加密标准。DES加密时以64位分组对数据进行加密,加密和解密都使用的是同一个长度为64位的密钥,实际上只用到了其中的56位,密钥中的第8、16…64位用来作奇偶校验。DES有ECB(电子密码本)和CBC(加密块)等加密模式。
DES算法的安全性很高,目前除了穷举搜索破解外, 尚无更好的的办法来破解。其密钥长度越长,破解难度就越大。
填充和去填充函数。

  1. func ZeroPadding(ciphertext []byte, blockSize int) []byte {
  2. padding := blockSize - len(ciphertext)%blockSize
  3. padtext := bytes.Repeat([]byte{0}, padding)
  4. return append(ciphertext, padtext...)
  5. }
  6. func ZeroUnPadding(origData []byte) []byte {
  7. return bytes.TrimFunc(origData,
  8. func(r rune) bool {
  9. return r == rune(0)
  10. })
  11. }

加密。

  1. func Encrypt(text string, key []byte) (string, error) {
  2. src := []byte(text)
  3. block, err := des.NewCipher(key)
  4. if err != nil {
  5. return "", err
  6. }
  7. bs := block.BlockSize()
  8. src = ZeroPadding(src, bs)
  9. if len(src)%bs != 0 {
  10. return "", errors.New("Need a multiple of the blocksize")
  11. }
  12. out := make([]byte, len(src))
  13. dst := out
  14. for len(src) > 0 {
  15. block.Encrypt(dst, src[:bs])
  16. src = src[bs:]
  17. dst = dst[bs:]
  18. }
  19. return hex.EncodeToString(out), nil
  20. }

解密。

  1. func Decrypt(decrypted string , key []byte) (string, error) {
  2. src, err := hex.DecodeString(decrypted)
  3. if err != nil {
  4. return "", err
  5. }
  6. block, err := des.NewCipher(key)
  7. if err != nil {
  8. return "", err
  9. }
  10. out := make([]byte, len(src))
  11. dst := out
  12. bs := block.BlockSize()
  13. if len(src)%bs != 0 {
  14. return "", errors.New("crypto/cipher: input not full blocks")
  15. }
  16. for len(src) > 0 {
  17. block.Decrypt(dst, src[:bs])
  18. src = src[bs:]
  19. dst = dst[bs:]
  20. }
  21. out = ZeroUnPadding(out)
  22. return string(out), nil
  23. }

测试。在这里,DES中使用的密钥key只能为8位。

  1. func main() {
  2. key := []byte("2fa6c1e9")
  3. str :="I love this beautiful world!"
  4. strEncrypted, err := Encrypt(str, key)
  5. if err != nil {
  6. log.Fatal(err)
  7. }
  8. fmt.Println("Encrypted:", strEncrypted)
  9. strDecrypted, err := Decrypt(strEncrypted, key)
  10. if err != nil {
  11. log.Fatal(err)
  12. }
  13. fmt.Println("Decrypted:", strDecrypted)
  14. }
  15. //Output:
  16. //Encrypted: 5d2333b9fbbe5892379e6bcc25ffd1f3a51b6ffe4dc7af62beb28e1270d5daa1
  17. //Decrypted: I love this beautiful world!

RSA

首先使用openssl生成公私钥,使用RSA的时候需要提供公钥和私钥 , 可以通过openss来生成对应的pem格式的公钥和私钥匙

  1. import (
  2. "crypto/rand"
  3. "crypto/rsa"
  4. "crypto/x509"
  5. "encoding/base64"
  6. "encoding/pem"
  7. "errors"
  8. "fmt"
  9. )
  10. // 私钥生成
  11. //openssl genrsa -out rsa_private_key.pem 1024
  12. var privateKey = []byte(`
  13. -----BEGIN RSA PRIVATE KEY-----
  14. MIICWwIBAAKBgQDcGsUIIAINHfRTdMmgGwLrjzfMNSrtgIf4EGsNaYwmC1GjF/bM
  15. h0Mcm10oLhNrKNYCTTQVGGIxuc5heKd1gOzb7bdTnCDPPZ7oV7p1B9Pud+6zPaco
  16. qDz2M24vHFWYY2FbIIJh8fHhKcfXNXOLovdVBE7Zy682X1+R1lRK8D+vmQIDAQAB
  17. AoGAeWAZvz1HZExca5k/hpbeqV+0+VtobMgwMs96+U53BpO/VRzl8Cu3CpNyb7HY
  18. 64L9YQ+J5QgpPhqkgIO0dMu/0RIXsmhvr2gcxmKObcqT3JQ6S4rjHTln49I2sYTz
  19. 7JEH4TcplKjSjHyq5MhHfA+CV2/AB2BO6G8limu7SheXuvECQQDwOpZrZDeTOOBk
  20. z1vercawd+J9ll/FZYttnrWYTI1sSF1sNfZ7dUXPyYPQFZ0LQ1bhZGmWBZ6a6wd9
  21. R+PKlmJvAkEA6o32c/WEXxW2zeh18sOO4wqUiBYq3L3hFObhcsUAY8jfykQefW8q
  22. yPuuL02jLIajFWd0itjvIrzWnVmoUuXydwJAXGLrvllIVkIlah+lATprkypH3Gyc
  23. YFnxCTNkOzIVoXMjGp6WMFylgIfLPZdSUiaPnxby1FNM7987fh7Lp/m12QJAK9iL
  24. 2JNtwkSR3p305oOuAz0oFORn8MnB+KFMRaMT9pNHWk0vke0lB1sc7ZTKyvkEJW0o
  25. eQgic9DvIYzwDUcU8wJAIkKROzuzLi9AvLnLUrSdI6998lmeYO9x7pwZPukz3era
  26. zncjRK3pbVkv0KrKfczuJiRlZ7dUzVO0b6QJr8TRAA==
  27. -----END RSA PRIVATE KEY-----
  28. `)
  29. // 公钥: 根据私钥生成
  30. //openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
  31. var publicKey = []byte(`
  32. -----BEGIN PUBLIC KEY-----
  33. MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcGsUIIAINHfRTdMmgGwLrjzfM
  34. NSrtgIf4EGsNaYwmC1GjF/bMh0Mcm10oLhNrKNYCTTQVGGIxuc5heKd1gOzb7bdT
  35. nCDPPZ7oV7p1B9Pud+6zPacoqDz2M24vHFWYY2FbIIJh8fHhKcfXNXOLovdVBE7Z
  36. y682X1+R1lRK8D+vmQIDAQAB
  37. -----END PUBLIC KEY-----
  38. `)
  39. // 加密
  40. func RsaEncrypt(origData []byte) ([]byte, error) {
  41. //解密pem格式的公钥
  42. block, _ := pem.Decode(publicKey)
  43. if block == nil {
  44. return nil, errors.New("public key error")
  45. }
  46. // 解析公钥
  47. pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
  48. if err != nil {
  49. return nil, err
  50. }
  51. // 类型断言
  52. pub := pubInterface.(*rsa.PublicKey)
  53. //加密
  54. return rsa.EncryptPKCS1v15(rand.Reader, pub, origData)
  55. }
  56. // 解密
  57. func RsaDecrypt(ciphertext []byte) ([]byte, error) {
  58. //解密
  59. block, _ := pem.Decode(privateKey)
  60. if block == nil {
  61. return nil, errors.New("private key error!")
  62. }
  63. //解析PKCS1格式的私钥
  64. priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
  65. if err != nil {
  66. return nil, err
  67. }
  68. // 解密
  69. return rsa.DecryptPKCS1v15(rand.Reader, priv, ciphertext)
  70. }
  71. func main() {
  72. data, _ := RsaEncrypt([]byte("hello world"))
  73. fmt.Println(base64.StdEncoding.EncodeToString(data))
  74. origData, _ := RsaDecrypt(data)
  75. fmt.Println(string(origData))
  76. }

MD5

MD5的全称是Message-DigestAlgorithm 5,它可以把一个任意长度的字节数组转换成一个定长的整数,并且这种转换是不可逆的。对于任意长度的数据,转换后的MD5值长度是固定的,而且MD5的转换操作很容易,只要原数据有一点点改动,转换后结果就会有很大的差异。正是由于MD5算法的这些特性,它经常用于对于一段信息产生信息摘要,以防止其被篡改。其还广泛就于操作系统的登录过程中的安全验证,比如Unix操作系统的密码就是经过MD5加密后存储到文件系统中,当用户登录时输入密码后, 对用户输入的数据经过MD5加密后与原来存储的密文信息比对,如果相同说明密码正确,否则输入的密码就是错误的。
MD5以512位为一个计算单位对数据进行分组,每一分组又被划分为16个32位的小组,经过一系列处理后,输出4个32位的小组,最后组成一个128位的哈希值。对处理的数据进行512求余得到N和一个余数,如果余数不为448,填充1和若干个0直到448位为止,最后再加上一个64位用来保存数据的长度,这样经过预处理后,数据变成(N+1)x 512位。
加密。Encode 函数用来加密数据,Check函数传入一个未加密的字符串和与加密后的数据,进行对比,如果正确就返回true。

  1. func Check(content, encrypted string) bool {
  2. return strings.EqualFold(Encode(content), encrypted)
  3. }
  4. func Encode(data string) string {
  5. h := md5.New()
  6. h.Write([]byte(data))
  7. return hex.EncodeToString(h.Sum(nil))
  8. }

测试。

  1. func main() {
  2. strTest := "I love this beautiful world!"
  3. strEncrypted := "98b4fc4538115c4980a8b859ff3d27e1"
  4. fmt.Println(Check(strTest, strEncrypted))
  5. }
  6. //Output:
  7. //true

Sha1

  1. package main
  2. import (
  3. "crypto/sha1"
  4. "fmt"
  5. )
  6. func main() {
  7. s := "sha1 this string"
  8. //产生一个散列值得方式是 sha1.New(),sha1.Write(bytes),然后 sha1.Sum([]byte{})。这里我们从一个新的散列开始。
  9. h := sha1.New()
  10. //写入要处理的字节。如果是一个字符串,需要使用[]byte(s) 来强制转换成字节数组。
  11. h.Write([]byte(s))
  12. //这个用来得到最终的散列值的字符切片。Sum 的参数可以用来都现有的字符切片追加额外的字节切片:一般不需要要。
  13. bs := h.Sum(nil)
  14. //SHA1 值经常以 16 进制输出,例如在 git commit 中。使用%x 来将散列结果格式化为 16 进制字符串。
  15. fmt.Println(s)
  16. fmt.Printf("%x\n", bs)
  17. }

Base64

Base64是一种任意二进制到文本字符串的编码方法,常用于在URL、Cookie、网页中传输少量二进制数据。
首先使用Base64编码需要一个含有64个字符的表,这个表由大小写字母、数字、+和/组成。采用Base64编码处理数据时,会把每三个字节共24位作为一个处理单元,再分为四组,每组6位,查表后获得相应的字符即编码后的字符串。编码后的字符串长32位,这样,经Base64编码后,原字符串增长1/3。如果要编码的数据不是3的倍数,最后会剩下一到两个字节,Base64编码中会采用\x00在处理单元后补全,编码后的字符串最后会加上一到两个 = 表示补了几个字节。

  1. const (
  2. base64Table = "IJjkKLMNO567PQX12RVW3YZaDEFGbcdefghiABCHlSTUmnopqrxyz04stuvw89+/"
  3. )
  4. var coder = base64.NewEncoding(base64Table)
  5. func Base64Encode(src []byte) []byte { //编码
  6. return []byte(coder.EncodeToString(src))
  7. }
  8. func Base64Decode(src []byte) ([]byte, error) { //解码
  9. return coder.DecodeString(string(src))
  10. }

持续更新中