数据传输加密解密

在前后端数据传输的过程中, 如果没有对数据加密, 抓包软件直接能看到我请求发的是什么数据,服务端给我返回的数据是什么

流程 #

以用对称加密加密数据, 非对称加密加密key

  1. 服务端生成一对RSA秘钥,私钥放在服务端(不可泄露),公钥下发给客户端。
  2. 客户端使用随机函数生成 key。
  3. 客户端使用随机的 key 对传输的数据用AES进行加密。
  4. 使用服务端给的公钥对 key进行加密。
  5. 客户端将使用AES加密的数据 以及使用 RSA公钥加密的key 一起发给服务端。
  6. 服务端拿到数据后,先使用私钥对加密的随机key进行解密,解密成功即可确定是客户端发来的数据,没有经过他人修改,然后使用解密成功的随机key对使用AES加密的数据进行解密,获取最终的数据。

这是单向的加密认证, 如果要实现双向加密验证, 就要生成两对公钥和私钥

步骤 #

生成RSA密钥对 #

生成私钥

openssl genrsa -out private_client.pem 1024
openssl genrsa -out private_server.pem 1024

生成公钥

openssl rsa -in private_client.pem -pubout -out public_client.pem
openssl rsa -in private_server.pem -pubout -out public_server.pem

加解密代码 #

  • rsa.go 非对称加密
package encrypt

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/sha256"
	"crypto/x509"
	"encoding/pem"
	"errors"
)

// BytesToPrivateKey bytes to private key
func BytesToPrivateKey(priv []byte) (*rsa.PrivateKey, error) {
	block, _ := pem.Decode(priv)
	enc := x509.IsEncryptedPEMBlock(block)
	b := block.Bytes
	var err error
	if enc {
		b, err = x509.DecryptPEMBlock(block, nil)
		if err != nil {
			return nil, err
		}
	}
	key, err := x509.ParsePKCS1PrivateKey(b)
	if err != nil {
		return nil, err
	}
	return key, nil
}

// BytesToPublicKey bytes to public key
func BytesToPublicKey(pub []byte) (*rsa.PublicKey, error) {
	block, _ := pem.Decode(pub)
	enc := x509.IsEncryptedPEMBlock(block)
	b := block.Bytes
	var err error
	if enc {
		b, err = x509.DecryptPEMBlock(block, nil)
		if err != nil {
			return nil, err
		}
	}
	ifc, err := x509.ParsePKIXPublicKey(b)
	if err != nil {
		return nil, err
	}
	key, ok := ifc.(*rsa.PublicKey)
	if !ok {
		return nil, errors.New("not ok")
	}
	return key, nil
}

// EncryptWithPublicKey encrypts data with public key
func EncryptWithPublicKey(msg []byte, pub *rsa.PublicKey) ([]byte, error) {
	hash := sha256.New()
	ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, pub, msg, nil)
	if err != nil {
		return nil, err
	}
	return ciphertext, nil
}

// DecryptWithPrivateKey decrypts data with private key
func DecryptWithPrivateKey(ciphertext []byte, priv *rsa.PrivateKey) ([]byte, error) {
	hash := sha256.New()
	plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, priv, ciphertext, nil)
	if err != nil {
		return nil, err
	}
	return plaintext, nil
}

aes.go 对称加密

package encrypt

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

// =================== CBC ======================
func AesEncryptCBC(origData []byte, key []byte) (encrypted []byte) {
	// 分组秘钥
	// NewCipher该函数限制了输入k的长度必须为16, 24或者32
	block, _ := aes.NewCipher(key)
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	origData = pkcs5Padding(origData, blockSize)                // 补全码
	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize]) // 加密模式
	encrypted = make([]byte, len(origData))                     // 创建数组
	blockMode.CryptBlocks(encrypted, origData)                  // 加密
	return encrypted
}
func AesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) {
	block, _ := aes.NewCipher(key)                              // 分组秘钥
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
	decrypted = make([]byte, len(encrypted))                    // 创建数组
	blockMode.CryptBlocks(decrypted, encrypted)                 // 解密
	decrypted = pkcs5UnPadding(decrypted)                       // 去除补全码
	return decrypted
}
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)]
}

// =================== ECB ======================
func AesEncryptECB(origData []byte, key []byte) (encrypted []byte) {
	newCipher, _ := aes.NewCipher(generateKey(key))
	length := (len(origData) + aes.BlockSize) / aes.BlockSize
	plain := make([]byte, length*aes.BlockSize)
	copy(plain, origData)
	pad := byte(len(plain) - len(origData))
	for i := len(origData); i < len(plain); i++ {
		plain[i] = pad
	}
	encrypted = make([]byte, len(plain))
	// 分组分块加密
	for bs, be := 0, newCipher.BlockSize(); bs <= len(origData); bs, be = bs+newCipher.BlockSize(), be+newCipher.BlockSize() {
		newCipher.Encrypt(encrypted[bs:be], plain[bs:be])
	}

	return encrypted
}
func AesDecryptECB(encrypted []byte, key []byte) (decrypted []byte) {
	newCipher, _ := aes.NewCipher(generateKey(key))
	decrypted = make([]byte, len(encrypted))

	for bs, be := 0, newCipher.BlockSize(); bs < len(encrypted); bs, be = bs+newCipher.BlockSize(), be+newCipher.BlockSize() {
		newCipher.Decrypt(decrypted[bs:be], encrypted[bs:be])
	}

	trim := 0
	if len(decrypted) > 0 {
		trim = len(decrypted) - int(decrypted[len(decrypted)-1])
	}

	return decrypted[:trim]
}
func generateKey(key []byte) (genKey []byte) {
	genKey = make([]byte, 16)
	copy(genKey, key)
	for i := 16; i < len(key); {
		for j := 0; j < 16 && i < len(key); j, i = j+1, i+1 {
			genKey[j] ^= key[i]
		}
	}
	return genKey
}

中间件处代码

package middleware

import (
	"bytes"
	"crypt/encrypt"
	"encoding/json"
	"fmt"
	"gzs"
	"io/ioutil"
	"log"
	"net/http"
)

type EncryptParam struct {
	Key           string `json:"key" form:"key"`
	EncryptedData string `json:"encrypted_data" form:"encrypted_data"`
}

type EncryptResponseWriter struct {
	gzs.Response
	Buff *bytes.Buffer
}

func (e *EncryptResponseWriter) Write(p []byte) (int, error) {
	return  e.Buff.Write(p)
}

func Encrypt() gzs.HandlerFunc {
	return func(c *gzs.Context) {
		encryptType := c.Request.Header.Get("bb-encrypt")
		version := c.Request.Header.Get("bb-encrypt-ver")
		if encryptType == "" || encryptType == "none" {
			return
		}
		encryptWriter := &EncryptResponseWriter{c.Response, bytes.NewBuffer(make([]byte, 0))}
		c.Response = encryptWriter

		if encryptType == "request" || encryptType == "all" {

			fmt.Println("start encoder")
			param := EncryptParam{
				Key:c.Request.PostFormValue("key"),
				EncryptedData: c.Request.PostFormValue("encrypted_data"),
			}

			if param.Key == "" || param.EncryptedData == "" {
				c.AbortWithStatus(http.StatusBadRequest)
				log.Println("EncryptedData is empty")
				return
			}

			privateKey := encrypt.GetPrivateServer()
			key, err := encrypt.RsaDecryptData(string(privateKey), param.Key)
			if err != nil {
				c.AbortWithStatus(http.StatusBadRequest)
				log.Printf("RsaDecryptData err: %s", err)
				return
			}
			data, err := encrypt.AesDecryptData(key, param.EncryptedData)
			if err != nil {
				c.AbortWithStatus(http.StatusBadRequest)
				log.Printf("AesDecryptData err: %s", err)
				return
			}

			if c.Request.Method == http.MethodGet {
				c.Request.URL.RawQuery = data
			} else {
				c.Request.Body = ioutil.NopCloser(bytes.NewBuffer([]byte(data)))
			}

			log.Printf("%v-middlewares-decrypt raw: %v", c.Request.URL.Path, data)
		}

		c.Next()

		normalReturn := func() {
			if _, err := encryptWriter.Response.Write(encryptWriter.Buff.Bytes()); err != nil {
				log.Println(err.Error())
			}
		}
		if c.StatusCode != http.StatusOK { // 不成功, 直接返回
			normalReturn()
			return
		}

		encryptWriter.Header().Set("bb-encrypted", version)
		encryptWriter.Header().Set("bb-encrypt-ver", "0")
		// 加密返回
		if encryptType == "response" || encryptType == "all" {

			randomKey := encrypt.RandStringRunes(16)
			publicKey := encrypt.GetPublicServer()
			key, err := encrypt.RsaEncryptData(publicKey, []byte(randomKey))
			if err != nil {
				log.Printf("RsaEncryptData err: %s", err)
				return
			}
			encryptedData, err := encrypt.AesEncryptData(randomKey, encryptWriter.Buff.String())
			if err != nil {
				log.Printf("AesEncryptData err: %s", err)
				return
			}

			data, err := json.Marshal(EncryptParam{Key: key, EncryptedData: encryptedData})
			if err != nil {
				log.Println(err.Error())
			} else {
				log.Printf("%v-middlewares-encrypt raw: %v", c.Request.URL.Path, encryptWriter.Buff.String())
				encryptWriter.Header().Set("bb-encrypt-ver", "1")
				if _, err := encryptWriter.Response.Write(data); err != nil {
					log.Println(err.Error())
				}
			}
		} else {
			normalReturn()
			return
		}
	}
}

代码地址和效果 #

代码地址

http://reps.sfwzgroup.cn/zhuxishun/crypt

postman模拟效果

加密

加密

解密

头设置

传递参数