Golang示例代码
1. 核心安全组件 (sud/security.go)
go
package sud
import (
"crypto"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
"fmt"
"io"
)
const (
gcmStandardNonceSize = 12
)
// EncryptedBody 对应 HTTP 请求 Body 的 JSON 结构
type EncryptedBody struct {
Iv string `json:"iv"`
Data string `json:"data"`
AuthTag string `json:"authtag"`
}
// EncryptRequest 执行 AES-256-GCM 请求数据加密
func EncryptRequest(urlPath, appId, keySn, symKeyBase64 string, reqData map[string]any, timestamp int64) (*EncryptedBody, error) {
// 生成防重放 Nonce (16字节随机数,Base64 Raw 编码无等号填充)
nonceBytes := make([]byte, 16)
if _, err := io.ReadFull(rand.Reader, nonceBytes); err != nil {
return nil, fmt.Errorf("generate nonce failed: %w", err)
}
nonce := base64.RawStdEncoding.EncodeToString(nonceBytes)
// 补齐安全校验字段
reqData["_n"] = nonce
reqData["_appid"] = appId
reqData["_timestamp"] = timestamp
plaintext, err := json.Marshal(reqData)
if err != nil {
return nil, fmt.Errorf("marshal request data failed: %w", err)
}
// 拼接 AAD: urlpath|appid|timestamp|sn
aadStr := fmt.Sprintf("%s|%s|%d|%s", urlPath, appId, timestamp, keySn)
aad := []byte(aadStr)
realKey, err := base64.StdEncoding.DecodeString(symKeyBase64)
if err != nil {
return nil, fmt.Errorf("decode symmetric key failed: %w", err)
}
realIv := make([]byte, gcmStandardNonceSize)
if _, err := io.ReadFull(rand.Reader, realIv); err != nil {
return nil, fmt.Errorf("generate gcm iv failed: %w", err)
}
block, err := aes.NewCipher(realKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// gcm.Seal 会将 AuthTag 自动追加在密文切片末尾
sealedData := gcm.Seal(nil, realIv, plaintext, aad)
// 分离密文与 AuthTag
tagSize := gcm.Overhead()
ciphertext := sealedData[:len(sealedData)-tagSize]
authTag := sealedData[len(sealedData)-tagSize:]
return &EncryptedBody{
Iv: base64.StdEncoding.EncodeToString(realIv),
Data: base64.StdEncoding.EncodeToString(ciphertext),
AuthTag: base64.StdEncoding.EncodeToString(authTag),
}, nil
}
// SignRequest 生成 RSA-PSS 请求签名
func SignRequest(urlPath, appId string, timestamp int64, reqDataJSON string, privateKeyPem string) (string, error) {
// 严格按顺序拼接,不添加多余的结尾换行符
payload := fmt.Sprintf("%s\n%s\n%d\n%s", urlPath, appId, timestamp, reqDataJSON)
hashed := sha256.Sum256([]byte(payload))
block, _ := pem.Decode([]byte(privateKeyPem))
if block == nil {
return "", errors.New("parse private key failed: invalid PEM format")
}
// 解析 PKCS#8 格式私钥
privInterface, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return "", fmt.Errorf("parse PKCS#8 private key failed: %w", err)
}
privKey, ok := privInterface.(*rsa.PrivateKey)
if !ok {
return "", errors.New("parsed key is not an RSA private key")
}
// 配置 PSS 参数,盐长度对齐 SHA256 (32字节)
opts := &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
Hash: crypto.SHA256,
}
sigBytes, err := rsa.SignPSS(rand.Reader, privKey, crypto.SHA256, hashed[:], opts)
if err != nil {
return "", fmt.Errorf("sign PSS failed: %w", err)
}
return base64.StdEncoding.EncodeToString(sigBytes), nil
}
// VerifyResponse 验证平台响应的 RSA-PSS 签名
func VerifyResponse(urlPath, expectedAppId, localCertSn, platformCertPem string,
respAppId string, respTs int64, respSn, respSig, respDeprecatedSn, respDeprecatedSig, respDataJSON string) error {
if expectedAppId != respAppId {
return errors.New("security check failed: appid mismatch")
}
// 证书序列号匹配与平滑轮换支持
var signatureBase64 string
if localCertSn == respSn {
signatureBase64 = respSig
} else if respDeprecatedSn != "" && localCertSn == respDeprecatedSn {
signatureBase64 = respSig
if respDeprecatedSig != "" {
signatureBase64 = respDeprecatedSig
}
} else {
return fmt.Errorf("verify failed: cert sn mismatch (local: %s, remote: %s)", localCertSn, respSn)
}
payload := fmt.Sprintf("%s\n%s\n%d\n%s", urlPath, respAppId, respTs, respDataJSON)
hashed := sha256.Sum256([]byte(payload))
block, _ := pem.Decode([]byte(platformCertPem))
if block == nil {
return errors.New("parse cert failed: invalid PEM format")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return fmt.Errorf("parse x509 cert failed: %w", err)
}
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return errors.New("cert public key is not RSA")
}
sigBytes, err := base64.StdEncoding.DecodeString(signatureBase64)
if err != nil {
return fmt.Errorf("decode signature base64 failed: %w", err)
}
opts := &rsa.PSSOptions{
SaltLength: rsa.PSSSaltLengthEqualsHash,
Hash: crypto.SHA256,
}
if err := rsa.VerifyPSS(pubKey, crypto.SHA256, hashed[:], sigBytes, opts); err != nil {
return fmt.Errorf("verify PSS signature failed: %w", err)
}
return nil
}
// DecryptResponse 执行 AES-256-GCM 响应解密并校验安全字段
func DecryptResponse(urlPath, appId string, respTs int64, keySn, symKeyBase64, respDataJSON string) (map[string]any, error) {
var encBody EncryptedBody
if err := json.Unmarshal([]byte(respDataJSON), &encBody); err != nil {
return nil, fmt.Errorf("unmarshal encrypted body failed: %w", err)
}
aadStr := fmt.Sprintf("%s|%s|%d|%s", urlPath, appId, respTs, keySn)
aad := []byte(aadStr)
realKey, err := base64.StdEncoding.DecodeString(symKeyBase64)
if err != nil {
return nil, fmt.Errorf("decode symmetric key failed: %w", err)
}
ivBytes, _ := base64.StdEncoding.DecodeString(encBody.Iv)
dataBytes, _ := base64.StdEncoding.DecodeString(encBody.Data)
authTagBytes, _ := base64.StdEncoding.DecodeString(encBody.AuthTag)
// Go 规范:将密文与 AuthTag 组合
combinedData := append(dataBytes, authTagBytes...)
block, err := aes.NewCipher(realKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
// 解密并验证 AuthTag
decryptedBytes, err := gcm.Open(nil, ivBytes, combinedData, aad)
if err != nil {
return nil, fmt.Errorf("gcm open failed (invalid authtag or tampered data): %w", err)
}
var realResp map[string]any
if err := json.Unmarshal(decryptedBytes, &realResp); err != nil {
return nil, fmt.Errorf("unmarshal decrypted plaintext failed: %w", err)
}
// 校验防重放安全字段 (注意 Go 弱类型 JSON 数字默认解析为 float64)
innerAppID, ok1 := realResp["_appid"].(string)
innerTimestampFloat, ok2 := realResp["_timestamp"].(float64)
if !ok1 || !ok2 || innerAppID != appId || int64(innerTimestampFloat) != respTs {
return nil, errors.New("security field validation failed: internal _appid or _timestamp tampered")
}
return realResp, nil
}2. 业务调用演示 (main.go)
go
package main
import (
"encoding/json"
"fmt"
"time"
"your_project/sud" // 引入上方的工具包
)
func main() {
// 1. 全局应用配置
const (
appID = "cp_10010"
urlPath = "https://api.sud.com/v1/game/login"
symKeySn = "key_v1"
symKeyBase64 = "otUpngOjU+nVQaWJIC3D/yMLV17RKaP6t4Ot9tbnzLY="
privateKeyPem = "-----BEGIN PRIVATE KEY-----\nMIIEvQ... (应用私钥) ...W6w=\n-----END PRIVATE KEY-----"
platformCertSn = "sud_cert_v1"
platformCertPem = "-----BEGIN CERTIFICATE-----\nMIID0j... (SUD平台公钥证书) ...Q==\n-----END CERTIFICATE-----"
)
fmt.Println("=== 1. 发起请求:业务数据加密与签名 ===")
// 构造业务请求数据
reqTs := time.Now().Unix()
bizReq := map[string]any{
"appid": appID,
"sud_uid": "user_889900",
"scene": 0,
"client_ip": "127.0.0.1",
}
// 步骤 A:执行加密
encBody, err := sud.EncryptRequest(urlPath, appID, symKeySn, symKeyBase64, bizReq, reqTs)
if err != nil {
panic(fmt.Sprintf("加密失败: %v", err))
}
reqBodyJSON, _ := json.Marshal(encBody)
reqBodyStr := string(reqBodyJSON)
// 步骤 B:生成签名
reqSig, err := sud.SignRequest(urlPath, appID, reqTs, reqBodyStr, privateKeyPem)
if err != nil {
panic(fmt.Sprintf("签名失败: %v", err))
}
fmt.Printf("HTTP Header X-Sud-Timestamp: %d\n", reqTs)
fmt.Printf("HTTP Header X-Sud-Signature: %s\n", reqSig)
fmt.Printf("HTTP Body: %s\n", reqBodyStr)
fmt.Println("\n=== 2. 收到响应:签名验证与密文解密 ===")
// 模拟从 HTTP 响应拦截器中获取到的数据
respTs := reqTs
respHeaderAppId := appID
respHeaderSn := platformCertSn
respHeaderSig := reqSig // 这里仅用作演示,实际应为接收到的签名
respBodyStr := `{"iv":"r2WDQt56rEAmMuoR","data":"HExs66...","authtag":"z2BF..."}`
// 步骤 C:执行验签
err = sud.VerifyResponse(urlPath, appID, platformCertSn, platformCertPem,
respHeaderAppId, respTs, respHeaderSn, respHeaderSig, "", "", respBodyStr)
if err != nil {
fmt.Printf("响应验签失败,丢弃报文: %v\n", err)
} else {
fmt.Println("响应验签成功!")
// 步骤 D:执行解密
realResp, err := sud.DecryptResponse(urlPath, appID, respTs, symKeySn, symKeyBase64, respBodyStr)
if err != nil {
fmt.Printf("响应解密失败: %v\n", err)
} else {
respJSON, _ := json.MarshalIndent(realResp, "", " ")
fmt.Printf("响应解密成功,业务明文数据:\n%s\n", string(respJSON))
}
}
}