流程概述
- 一般情况下整个过程存在两对公私钥。A:开发者持有私钥,提供服务的平台(如支付宝、衫德等)持有公钥;B:提供服务的平台持有私钥,开发者持有公钥。
- 开发者使用A私钥加签后发送请求到平台,平台使用A公钥验签,确定数据有效后,平台会将执行结果使用B私钥加签后发送给开发者(常见就是回调),此时开发者拿到数据需要验签确定数据有效
衫德(SHA1WithRSA方式加签)秘钥格式转化
openssl pkcs12 -in xxx.pfx -nodes -out xxx.pem
openssl x509 -inform der -in xxx.cer -out xxx.pem
加载秘钥文件获取需要的公私钥
func LoadPrivateKey(pemPath string) (string, *rsa.PrivateKey, error) {
key, err := ioutil.ReadFile(pemPath)
if err != nil {
return "", nil, err
}
block, _ := pem.Decode(key)
if block == nil {
return "", nil, nil
}
p, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", nil, err
} else {
pk := p.(*rsa.PrivateKey)
return "", pk, nil
}
}
func LoadPublicKey(pemPath string) (*rsa.PublicKey, error) {
key, err := ioutil.ReadFile(pemPath)
if err != nil {
return nil, err
}
block, _ := pem.Decode(key)
if block == nil {
return nil, err
}
certBody, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, err
}
pb := certBody.PublicKey.(*rsa.PublicKey)
return pb, nil
}
加签
func _sign(signStr string) (sign string, err error) {
h := crypto.Hash.New(crypto.SHA1)
//对数据进行签名
h.Write([]byte(signStr))
hashed := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand.Reader, A私钥,
crypto.SHA1, hashed)
if err != nil {
return
}
sign = base64.StdEncoding.EncodeToString(signature)
return
}
验签
func VerifySign(signData string, sign []byte, pk *rsa.PublicKey) error {
hash := crypto.SHA1
if !hash.Available() {
return fmt.Errorf("crypto: requested hash function (%s) is unavailable", hash.String())
}
h := hash.New()
h.Write([]byte(signData))
return rsa.VerifyPKCS1v15(pk, hash, h.Sum(nil), sign)
}
衫德云账户账户侧加签验签demo:
//SignAccount 云账户账户侧签名
func SignAccount(signData []byte) (sign, encryptKey, encryptData string, err error) {
//0返回一个16位字符串
aes := SandAES{}
rand16 := aes.RandStr(16)
//1使用加签公钥 对16位随机数加密
encryptKeyByte, err := rsa.EncryptPKCS1v15(rand.Reader, B公钥, []byte(rand16))
if err != nil {
return
}
encryptKey = base64.StdEncoding.EncodeToString(encryptKeyByte)
//2使用16位随机数 对内容进行ase加密,得到data报文体
aes.Key = []byte(rand16)
encryptData = aes.Encypt5(signData)
//3通过私钥获取签名
h := crypto.Hash.New(crypto.SHA1)
//对data参数进行签名
h.Write([]byte(encryptData))
hashed := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand.Reader, A私钥,
crypto.SHA1, hashed)
if err != nil {
return
}
sign = base64.StdEncoding.EncodeToString(signature)
return
}
// DataAccount 账户侧解析返回数据
func DataAccount(signData, data, encryptKey string) ([]byte, error) {
sign, err := base64.StdEncoding.DecodeString(strings.Replace(signData, " ", "+", -1))
if err != nil {
return nil, err
}
err = VerifySign(data, sign, B公钥)
if err != nil {
err = VerifySign(data, sign, A公钥)
if err != nil {
return nil, err
}
}
encryptKey1, err := base64.StdEncoding.DecodeString(encryptKey)
if err != nil {
return nil, err
}
rand16, err := rsa.DecryptPKCS1v15(rand.Reader, A私钥, encryptKey1)
if err != nil {
return nil, err
}
aes := SandAES{}
data1, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return nil, err
}
originData := aes.EcbDecrypt(data1, rand16)
return originData, nil
}
AES加密解密可借鉴:
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"errors"
"math/rand"
"strings"
)
type SandAES struct {
Key []byte
}
func (s *SandAES) Pkcs7Padding(data []byte, blockSize int) []byte {
//判断缺少几位长度。最少1,最多 blockSize
padding := blockSize - len(data)%blockSize
//补足位数。把切片[]byte{byte(padding)}复制padding个
padText := bytes.Repeat([]byte{byte(padding)}, padding)
return append(data, padText...)
}
// pkcs7UnPadding 填充的反向操作
func (s *SandAES) Pkcs7UnPadding(data []byte) ([]byte, error) {
length := len(data)
if length == 0 {
return nil, errors.New("加密字符串错误")
}
// 获取填充的个数
unPadding := int(data[length-1])
return data[:(length - unPadding)], nil
}
func (s *SandAES) AESEncrypt(data []byte) ([]byte, error) {
// 创建加密实例
block, err := aes.NewCipher(s.Key)
if err != nil {
return nil, err
}
//判断加密块的大小
blockSize := block.BlockSize()
//填充
encryptBytes := s.Pkcs7Padding(data, blockSize)
// 初始化加密数据接受切片
crypted := make([]byte, len(encryptBytes))
// 使用cbc加密
blockMod := cipher.NewCBCEncrypter(block, s.Key[:blockSize])
// 执行加密
blockMod.CryptBlocks(crypted, encryptBytes)
return crypted, nil
}
// 解密
func (s *SandAES) AESDecrypt(data []byte) ([]byte, error) {
//创建实例
block, err := aes.NewCipher(s.Key)
if err != nil {
return nil, err
}
//获取块的大小
blockSize := block.BlockSize()
//使用cbc
blockMode := cipher.NewCBCDecrypter(block, s.Key[:blockSize])
//初始化数据
crypted := make([]byte, len(data))
//执行解密
blockMode.CryptBlocks(crypted, data)
//去处填充
crypted, err = s.Pkcs7UnPadding(crypted)
if err != nil {
return nil, err
}
return crypted, nil
}
func (s *SandAES) EncryptByAES(data []byte) (string, error) {
res, err := s.AESEncrypt(data)
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(res), nil
}
//解密
func (s *SandAES) DecryptByAES(data string) ([]byte, error) {
dataByte, err := base64.StdEncoding.DecodeString(data)
if err != nil {
return nil, err
}
return s.AESDecrypt(dataByte)
}
func (s *SandAES) RandStr(n int) string {
str := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
strData := strings.Builder{}
for i := 0; i < n; i++ {
index := rand.Intn(len(str))
strData.WriteString(str[index])
}
res := strData.String()
return res
}
func (s *SandAES) Encypt5(data []byte) string {
block, _ := aes.NewCipher(s.Key)
data = s.Pkcs7Padding(data, block.BlockSize())
decrypted := make([]byte, len(data))
size := block.BlockSize()
for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
block.Encrypt(decrypted[bs:be], data[bs:be])
}
return base64.StdEncoding.EncodeToString(decrypted)
}
func (s *SandAES) EcbDecrypt(data, key []byte) []byte {
block, _ := aes.NewCipher(key)
decrypted := make([]byte, len(data))
size := block.BlockSize()
for bs, be := 0, size; bs < len(data); bs, be = bs+size, be+size {
block.Decrypt(decrypted[bs:be], data[bs:be])
}
b, _ := s.Pkcs7UnPadding(decrypted)
return b
}