-RSA 加密解密 數據非明文
-RSA 簽名驗簽 數據明文 校驗數據真實性
> RSA(MD5WithRSA 算法)簽名和驗簽方式
- 簽名算法為直接把請求數據中的所有元素(除 sign 本身)按照"key 值=value 值"的格式拼接起來,并且把這些拼接以后的元素按首字母升序排列順序,最后以"&"字符連接起來得到簽名串,使用私鑰對簽名串進行 RSA 簽名
- [golang中關于RSA加密、解密、簽名、驗簽的總結](https://blog.csdn.net/xz_studying/article/details/80314111)
- [本次使用的密鑰生成工具](https://opendocs.alipay.com/open/291/106097)
```
package main
import (
"crypto"
"crypto/md5"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"fmt"
"net/url"
"strings"
"time"
)
const (
publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCeRsyfEMUo6PFs7upFQJr15CUveSDVvWAg8KRXz1fU5XwBgc6d2YY7ly3QeuKz7_rd5nlXlUxnMzT2dVhTPfNEI1Q4AnNbFxqhNN3gEeTUlIh1lBXdfoWOsOOb3LHs0124BgTViTRjDW-1CmJk-vqGq-maOaA7BcXRlt8kMecJdwIDAQAB"
privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJ5GzJ8QxSjo8Wzu6kVAmvXkJS95INW9YCDwpFfPV9TlfAGBzp3ZhjuXLdB64rPv-t3meVeVTGczNPZ1WFM980QjVDgCc1sXGqE03eAR5NSUiHWUFd1-hY6w45vcsezTXbgGBNWJNGMNb7UKYmT6-oar6Zo5oDsFxdGW3yQx5wl3AgMBAAECgYBedfCfKjoQ3V1g3wHQDOuuvtd2irsO9TPO1O-wPF22ALPOjnMKgAz9uY8tMnnkW-AD2Q4oOEFeAhCk2om5PGrXGThggJNKRrqYeeyKDtGdtdnp8Za9nI1jeVz-LeNNQHNPhIuhR5z8ufDaWVCm7sNcGyIbP_qg2SErg3yJpypcIQJBAN3trJ_z-McAmV6o_xJm5mk094tols74Ix59F60i0mB4VWdFY3M9fHjIvCKKavkGb4szl4FAiklsKO5myb64eCcCQQC2k3PV7dPiTs1_HDyP6ZuFTfuPT_r7llj8PSQXQPXCJb5o-oKXTsex06C8J6R0EpXCiBZhmWFe-Hqb1nIAU-YxAkBsQp8tQDShz1cB6GrVrUDFHcOMTC8VM9Ld8qP0H8KEsO7oe97xvpLT0QiFyQQ6CrurKjXEJZnQC2VENvw_f3mNAkAbkwWJp9O6eEBdFDypV5TfezmlGWVEnh5uaiWLRYpYei7Z2AvlIkbSuq2p_Sq_RRdNPBR1RR8JoumRo7-wAPvhAkBCm_ylQbGV8McWqzjnlFKd5Fz5SDrTYAC-xJcQ433jiFxAVseWKTpOYb7XR4AhDXSk7GFLseQMv-IdnR5fh7yx"
)
func main() {
m := make(map[string]string)
m["param1"] = "參數1_/+-"
m["param2"] = "參數2"
m["param3"] = "參數3"
fmt.Println("簽名前的數據:", m)
if err := signData(m); err != nil {
fmt.Println(err.Error())
}
fmt.Println("簽名后的數據:", m)
if err := verifyData(m); err != nil {
fmt.Println("驗簽結果(公鑰驗簽):", "失敗", err)
} else {
fmt.Println("驗簽結果(公鑰驗簽):", "成功")
}
}
func signData(m map[string]string) error {
m["timestamp"] = fmt.Sprintf("%d", time.Now().UnixNano()/1e6)
sig := url.Values{}
for key, value := range m {
sig.Add(key, value)
}
//進行轉碼使之可以安全的用在URL查詢里
quUrl, err := url.QueryUnescape(sig.Encode())
if err != nil {
fmt.Println("QueryUnescape", err)
return err
}
if out, err := RsaSignWithMd5(quUrl, privateKey); err != nil {
return err
} else {
m["sig"] = out
return nil
}
}
func verifyData(m map[string]string) error {
sign := m["sig"]
delete(m, "sig")
sig := url.Values{}
for key, value := range m {
sig.Add(key, value)
}
//進行轉碼使之可以安全的用在URL查詢里
quUrl, _ := url.QueryUnescape(sig.Encode())
return RsaVerifySignWithMd5(quUrl, sign, publicKey)
}
// 簽名
func RsaSignWithMd5(data string, prvKey string) (sign string, err error) {
//如果密鑰是urlSafeBase64的話需要處理下
prvKey = Base64URLDecode(prvKey)
keyBytes, err := base64.StdEncoding.DecodeString(prvKey)
if err != nil {
fmt.Println("DecodeString:", err)
return "", err
}
privateKey, err := x509.ParsePKCS8PrivateKey(keyBytes)
if err != nil {
fmt.Println("ParsePKCS8PrivateKey", err)
return "", err
}
h := md5.New()
h.Write([]byte(data))
hash := h.Sum(nil)
signature, err := rsa.SignPKCS1v15(rand.Reader, privateKey.(*rsa.PrivateKey), crypto.MD5, hash[:])
if err != nil {
fmt.Println("SignPKCS1v15:", err)
return "", err
}
out := base64.RawURLEncoding.EncodeToString(signature)
return out, nil
}
// 驗簽
func RsaVerifySignWithMd5(originalData, signData, pubKey string) error {
//TODO : 驗證時間
sign, err := base64.RawURLEncoding.DecodeString(signData)
if err != nil {
fmt.Println("DecodeString:", err)
return err
}
pubKey = Base64URLDecode(pubKey)
public, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil {
fmt.Println("DecodeString")
return err
}
pub, err := x509.ParsePKIXPublicKey(public)
if err != nil {
fmt.Println("ParsePKIXPublicKey", err)
return err
}
hash := md5.New()
hash.Write([]byte(originalData))
return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.MD5, hash.Sum(nil), sign)
}
//因為Base64轉碼后可能包含有+,/,=這些不安全的URL字符串,所以要進行換字符
// '+' -> '-'
// '/' -> '_'
// '=' -> ''
// 字符串長度不足4倍的位補"="
func Base64URLDecode(data string) string {
var missing = (4 - len(data)%4) % 4
data += strings.Repeat("=", missing) //字符串長度不足4倍的位補"="
data = strings.Replace(data, "_", "/", -1)
data = strings.Replace(data, "-", "+", -1)
return data
}
func Base64UrlSafeEncode(data string) string {
safeUrl := strings.Replace(data, "/", "_", -1)
safeUrl = strings.Replace(safeUrl, "+", "-", -1)
safeUrl = strings.Replace(safeUrl, "=", "", -1)
return safeUrl
}
```
- 要計算簽名的話,最好都用字符串來表達,免得麻煩。如果要用浮點數的話,那必須要另外約定小數位數
```
package main
import (
"crypto"
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"fmt"
"net/url"
"reflect"
"strconv"
"strings"
)
const (
publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvKbAeFgG-0hxLPyVCBEemiXcjbnkJ4Hy971V0AHzDIUwkqessV8EWx3hvgm6bqm48gBApUQwRQGymw7cIuLcnt0eRiCc6gPXI72ZUpRKYEk7C-zeOZYi4zSPXFA_3LmGKT7p0IYmTFEtlEz8kz1EtYZnNxJGbZwIZHjuIMP3G9QIDAQAB"
)
func main() {
jsonData := `{"connectorId":"YKC-YY13747#3201060000619002","lockStatus":0,"parkStatus":0,"platformCode":"920030401","sig":"Nr93wqjXAT/mWU0uS6sJhDv3oXR8Ygt0NuOUsf76z6Dtsj2AmgpJVojSZqeaLv7jbKSzbcSp5jsFRSyABvMUXcwaczTU9pK3Ml3KvzdqZjmULrgXALoqMtLsV4rPOgAVzkA7wHuhsrUEBAr1ShYXvEWvouvTBj26QNfNEmHYyiU=","status":1}`
fmt.Println(verifyResult(jsonData))
}
func verifyResult(jsonData string) bool {
m := make(map[string]interface{})
if err := json.Unmarshal([]byte(jsonData), &m); err != nil {
fmt.Println(err.Error())
}
if _, ok := m["chargeDetails"]; ok {
delete(m, "chargeDetails")
}
m2 := make(map[string]string)
for k, v := range m {
if v == nil || v == "" {
delete(m, k)
continue
}
if reflect.TypeOf(v).String() == "float64" {
if v != 0 && !InArrayOfString(k, []string{"startChargeSeqStat", "sumPeriod", "connectorStatus", "stopReason", "stayCost", "succStat", "failReason", "lockStatus", "parkStatus", "status"}) {
vv := strconv.FormatFloat(v.(float64), 'f', -1, 64)
if strings.IndexAny(vv, ".") < 1 {
changeV := fmt.Sprintf("%.1f", v)
m2[k] = changeV
} else {
m2[k] = vv
}
} else {
changeV := fmt.Sprintf("%.0f", v)
m2[k] = changeV
}
} else {
m2[k] = v.(string)
}
}
if err := verifyData(m2); err != nil {
fmt.Println(err.Error())
return false
} else {
return true
}
}
func verifyData(m map[string]string) error {
sign := m["sig"]
delete(m, "sig")
sig := url.Values{}
for key, value := range m {
sig.Add(key, value)
}
//進行轉碼使之可以安全的用在URL查詢里
quUrl, _ := url.QueryUnescape(sig.Encode())
fmt.Println(quUrl)
return RsaVerifySignWithMd5(quUrl, sign, publicKey)
}
// 驗簽
func RsaVerifySignWithMd5(originalData, signData, pubKey string) error {
fmt.Println(signData)
fmt.Println(Base64UrlSafeEncode(signData))
sign, err := base64.RawURLEncoding.DecodeString(Base64UrlSafeEncode(signData))
if err != nil {
fmt.Println("DecodeString:", err)
return err
}
pubKey = Base64URLDecode(pubKey)
public, err := base64.StdEncoding.DecodeString(pubKey)
if err != nil {
fmt.Println("DecodeString")
return err
}
pub, err := x509.ParsePKIXPublicKey(public)
if err != nil {
fmt.Println("ParsePKIXPublicKey", err)
return err
}
hash := md5.New()
hash.Write([]byte(originalData))
return rsa.VerifyPKCS1v15(pub.(*rsa.PublicKey), crypto.MD5, hash.Sum(nil), sign)
}
func Base64URLDecode(data string) string {
var missing = (4 - len(data)%4) % 4
data += strings.Repeat("=", missing) //字符串長度不足4倍的位補"="
data = strings.Replace(data, "_", "/", -1)
data = strings.Replace(data, "-", "+", -1)
return data
}
func Base64UrlSafeEncode(data string) string {
safeUrl := strings.Replace(data, "/", "_", -1)
safeUrl = strings.Replace(safeUrl, "+", "-", -1)
safeUrl = strings.Replace(safeUrl, "=", "", -1)
return safeUrl
}
func InArrayOfString(needle string, haystack []string) bool {
for _, v := range haystack {
if ok := strings.Contains(needle, v); ok == true {
return ok
}
}
return false
}
```
- 草稿
- Golang
- 切片 slice
- 數組和切片的區別
- 左閉右開
- make([]int, 5) 和 make([]int, 0, 5) 區別
- 切片非線程安全,并發操作為啥不會像map一樣報錯
- []struct{} 如何遍歷
- 切片如何刪除某個元素
- append 一個nil 切片
- 哈希表 map
- 并發操作
- 并發寫報錯
- 并發讀不會報錯
- 并發讀有寫報錯
- 并發迭代有寫報錯
- 自制并發安全字典
- 官方并發安全字典
- 對未初始化的 map 進行賦值操作
- map的底層
- 無序輸出
- 等量擴容
- 實現集合
- map的key可以使哪些值
- 協程 go
- 協程相關閱讀
- 進程、線程、協程
- 協程 (捕獲異常 和 協程池)
- GPM 模型
- CSP模型
- channel
- channel 相關操作
- 交替打印
- 如何讓channel 只能接收/只能發送
- channel 常見報錯
- channel 死鎖
- nil channel 和 已關閉的 channel
- 使用 select 來多路復用 channel
- channel 的使用
- 接口和結構體
- 簡單使用
- 兩個結構體能否比較
- 工廠模式
- 概念
- 簡單工廠
- 方法工廠
- 堆和棧,值類型和引用類型,內存逃逸,垃圾回收
- 棧和堆
- 內存逃逸
- 值類型和引用類型
- 垃圾回收方式
- 性能優化分析工具 pprof
- golang 代碼片段
- 片段一 defer
- 片段二 channel
- Golang 相關
- Golang 相關閱讀
- Golang 1-10
- make 和 new 的區別
- 使用指針的場景
- Go語言的context包
- 位運算
- Copy 是淺拷貝還是深拷貝
- init 函數 和 sync.Once
- select 多路復用
- Golang 其它
- MongoDB
- 可比較類型 與 可轉json 類型
- Gorm
- 面向對象和面向過程
- go語言實現-面向對象
- go語言實現-面向過程
- 限流,熔斷,降級
- 了解
- 熔斷配置
- 熔斷例子
- 服務降級
- github.com/alibaba/sentinel-golang
- 互斥鎖 讀寫鎖 原子鎖
- 為什么需要鎖
- 互斥鎖
- 讀寫鎖
- 原子鎖
- 互斥鎖性能對比
- 原子鎖性能對比
- 互斥鎖 or 原子鎖?
- 條件鎖
- 計數器
- GoFrame
- GF1.16版本
- 修改使用的表
- 按天、周、月、年
- GoFrame 文檔
- 配置文件
- 生成腳本
- 排序算法
- 相關排序
- 冒泡排序
- 選擇排序
- 插入排序
- 快速排序
- 歸并排序
- 堆排序
- 數據庫
- 分布式怎么保證線程安全
- 數據庫實現方式
- 基于表記錄
- 樂觀鎖
- 悲觀鎖
- Redis實現方式
- Zookeeper實現方式
- Mysql 相關
- group_concat
- 索引優化
- 索引優化1
- 定期分析和優化索引
- 覆蓋索引
- 組合索引
- 聚簇索引和非聚簇索引
- 索引類型與方式、聚簇與非聚簇索引
- 事務特征和隔離級別
- 查詢優化
- mysql自增表插入數據時,Id不連續問題
- InnoDB引擎 和 MyISAM引擎區別
- 鎖
- 悲觀鎖和樂觀鎖
- 查詢,更新,插入語句
- 什么是死鎖
- 怎么處理死鎖
- MySQL 隔離級別
- 事務特征
- 隔離級別
- 廢棄3
- 索引
- 索引類型和方式、聚簇和非聚簇索引(上)
- 索引類型和方式、聚簇和非聚簇索引(下)
- 回表、覆蓋索引、最左前綴、聯合索引、索引下推、索引合并
- Mysql 優化
- 索引的原理
- 千萬級表修改表結構
- Redis
- 獲取隨機三條數據
- Redis 持久化方式
- 全量模式 RDB 冷備份(內存快照)
- 增量模式 AOF 熱備份(文件追加)
- 過期key的刪除策略、內存淘汰機制
- 數據結構
- 位圖
- 網絡
- 網絡相關
- 游戲同步方式:幀同步和狀態同步
- Websocket
- OSI模型
- TCP 與 UDP
- 三次握手四次揮手
- Http 狀態碼
- 1xx(信息性狀態碼)
- 101 服務端代碼
- 101 客戶端代碼
- 2xx(成功狀態碼)
- 3xx(重定向狀態碼)
- 302 服務端代碼
- 302 客戶端代碼
- 4xx(客戶端錯誤狀態碼)
- 5xx(服務器錯誤狀態碼)
- 如何排查接口問題
- 網絡請求和響應過程
- time_wait
- keep-alive
- http 和 rpc 的區別
- I/O多路復用 select和poll
- too many open file
- 其它技術
- git 相關操作
- 修改提交備注
- 多個提交合并成一個提交
- 回退版本
- 小程序和公眾號
- 消息模板
- 獲取code
- 靜默登錄
- 其它技術相關
- C盤空間不足
- 生成式人工智能AIGC
- 共享文件
- 接口文檔, mock提供測試數據
- 抓包工具
- Python
- 安裝包失敗
- 自動化測試 Scrapy
- AIGC:人工智能生成內容
- PHP
- xhprof 性能分析
- 一鍵安裝
- 哈希沖突的解決方式
- 鏈地址法(拉鏈法)
- 開放地址法
- 再哈希
- 概念1
- Nginx
- 負載均衡方式
- 加密解密
- 簡單了解
- 簽名算法例子
- 碼例子1
- 代碼例子2
- Linux
- netstat (用于查看和管理網絡連接和路由表)
- ps 用于查看和管理進程
- ab 壓測
- nohup 守護進程
- lsof (List Open File 獲取被進程打開文件的信息)
- tail 查看日志
- 各類linux同步機制
- Socket 服務端的實現,select 和epoll的區別?
- scp 傳輸,awk 是一個強大的文本分析工具
- pidof
- 項目
- 棋牌
- 牌的編碼
- 出牌規則
- 洗牌
- 股票
- 股票知識
- 龍虎榜數據緩存方式
- 單日龍虎榜數據
- 單只股票的歷史上榜
- 遇到的問題
- 浮點數精度問題
- Mysql Sum 精度問題(float, double精度問題)
- 分頁問題(數據重復)
- 工具包
- v3
- common.go
- common_test.go
- customized.go
- customized_test.go
- slice.go
- slice_test.go
- time.go
- time_test.go
- v4
- common.go
- common_test.go
- customized.go
- customized_test.go
- slice.go
- time.go
- time_test.go
- 相關閱讀
- 協程 goroutine
- 通道 channel
- json 和 gob 序列化和反序列化
- redis 有序集合
- mysql22
- 相關閱讀 s
- pyTorch
- defer
- 內存泄漏
- 數據傳輸
- 雜項
- 一提
- gogogoo
- 內容