[TOC]
# 數字簽名
## 數字簽名的方法

## 簽名的生成和驗證
> 1. 簽名
> - 有原始數據對其進行哈希運算 -> 散列值
> - 使用非對稱加密的<font color="red">私鑰</font>對散列值加密 -> 簽名
> - 將原始數據和簽名一并發送給對方
> 2. 驗證
> - 接收數據
> - 原始數據
> - 數字簽名
> - 數字簽名, 需要使用<font color="red">公鑰</font>解密, 得到散列值
> - 對原始數據進行哈希運算得到新的散列值
## 非對稱加密和數字簽名
> 總結:
>
> 1. 數據通信
> - 公鑰加密, 私鑰解密
> 2. 數字簽名:
> - 私鑰加密, 公鑰解密
## 使用RSA進行數字簽名
1. 使用rsa生成密鑰對
> 1. 生成密鑰對
> 2. 序列化
> 3. 保存到磁盤文件
2. 使用私鑰進行數字簽名
> 1. 打開磁盤的私鑰文件
>
> 2. 將私鑰文件中的內容讀出
>
> 3. 使用pem對數據解碼, 得到了pem.Block結構體變量
>
> 4. x509將數據解析成私鑰結構體 -> 得到了私鑰
>
> 5. 創建一個哈希對象 -> md5/sha1
>
> 6. 給哈希對象添加數據
>
> 7. 計算哈希值
>
> 8. 使用rsa中的函數對散列值簽名
>
> ```go
> func SignPKCS1v15(rand io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) (s []byte, err error)
> 參數1: rand.Reader
> 參數2: 非對稱加密的私鑰
> 參數3: 使用的哈希算法
> crypto.sha1
> crypto.md5
> 參數4: 數據計算之后得到的散列值
> 返回值:
> - s: 得到的簽名數據
> - err: 錯誤信息
> ```
3. 使用公鑰進行簽名認證
> 1. 打開公鑰文件, 將文件內容讀出 - []byte
>
> 2. 使用pem解碼 -> 得到pem.Block結構體變量
>
> 3. 使用x509對pem.Block中的Bytes變量中的數據進行解析 -> 得到一接口
>
> 4. 進行類型斷言 -> 得到了公鑰結構體
>
> 5. 對原始消息進行哈希運算(和簽名使用的哈希算法一致) -> 散列值
>
> 1. 創建哈希接口
> 2. 添加數據
> 3. 哈希運算
>
> 6. 簽名認證 - rsa中的函數
>
> ```go
> func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) (err error)
> 參數1: 公鑰
> 參數2: 哈希算法 -> 與簽名使用的哈希算法一致
> 參數3: 將原始數據進行哈希原始得到的散列值
> 參數4: 簽名的字符串
> 返回值:
> - nil -> 驗證成功
> - !=nil -> 失敗
> ```
## 使用橢圓曲線進行數字簽名
> 橢圓曲線在go中對應的包: import "crypto/elliptic"
>
> 使用橢圓曲線在go中進行數字簽名: import "crypto/ecdsa"
>
> 美國FIPS186-2標準, 推薦使用5個素域上的橢圓曲線, 這5個素數模分別是:
>
> P~192~ = 2^192^ - 2^64^ - 1
>
> P~224~ = 2^224^ - 2^96^ + 1
>
> P~256~ = 2^256^ - 2^224^ + 2^192^ - 2^96^ -1
>
> P~384~ = 2^384^ - 2^128^ - 2^96^ + 2^32^ -1
>
> P~512~ = 2^512^ - 1
1. 秘鑰對稱的生成, 并保存到磁盤
> 1. 使用ecdsa生成密鑰對
>
> ```go
> func GenerateKey(c elliptic.Curve, rand io.Reader) (priv *PrivateKey, err error)
> ```
>
> 2. 將私鑰寫入磁盤
>
> - 使用x509進行序列化
>
> ```go
> func MarshalECPrivateKey(key *ecdsa.PrivateKey) ([]byte, error)
> ```
>
> - 將得到的切片字符串放入pem.Block結構體中
>
> block := pem.Block{
>
> Type : "描述....",
>
> Bytes : MarshalECPrivateKey返回值中的切片字符串,
>
> }
>
> - 使用pem編碼
>
> pem.Encode();
>
> 3. 將公鑰寫入磁盤
>
> - 從私鑰中得到公鑰
>
> - 使用x509進行序列化
>
> ```go
> func MarshalPKIXPublicKey(pub interface{}) ([]byte, error)
> ```
>
> - 將得到的切片字符串放入pem.Block結構體中
>
> block := pem.Block{
>
> Type : "描述....",
>
> Bytes : MarshalECPrivateKey返回值中的切片字符串,
>
> }
>
> - 使用pem編碼
>
> pem.Encode();
2. 使用私鑰進行數字簽名
> 1. 打開私鑰文件, 將內容讀出來 ->[]byte
>
> 2. 使用pem進行數據解碼 -> pem.Decode()
>
> 3. 使用x509, 對私鑰進行還原
>
> ```go
> func ParseECPrivateKey(der []byte) (key *ecdsa.PrivateKey, err error)
> ```
>
> 4. 對原始數據進行哈希運算 -> 散列值
>
> 5. 進行數字簽名
>
> ```go
> func Sign(rand io.Reader, priv *PrivateKey, hash []byte) (r, s *big.Int, err error)
> - 得到的r和s不能直接使用, 因為這是指針
> 應該將這兩塊內存中的數據進行序列化 -> []byte
> func (z *Int) MarshalText() (text []byte, err error)
> ```
3. 使用公鑰驗證數字簽名
> 1. 打開公鑰文件, 將里邊的內容讀出 -> []byte
>
> 2. pem解碼 -> pem.Decode()
>
> 3. 使用x509對公鑰還原
>
> ```go
> func ParsePKIXPublicKey(derBytes []byte) (pub interface{}, err error)
> ```
>
> 4. 將接口 -> 公鑰
>
> 5. 對原始數據進行哈希運算 -> 得到散列值
>
> 6. 簽名的認證 - > ecdsa
>
> ```go
> func Verify(pub *PublicKey, hash []byte, r, s *big.Int) bool
> - 參數1: 公鑰
> - 參數2: 原始數據生成的散列值
> - 參數3,4: 通過簽名得到的連個點
> func (z *Int) UnmarshalText(text []byte) error
> ```
## 數字簽名無法解決的問題

## 代碼
### RSA簽名和認證
~~~
// RSA簽名 - 私鑰
func SignatureRSA(plainText []byte, fileName string) []byte{
//1. 打開磁盤的私鑰文件
file, err := os.Open(fileName)
if err != nil {
panic(err)
}
//2. 將私鑰文件中的內容讀出
info, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, info.Size())
file.Read(buf)
file.Close()
//3. 使用pem對數據解碼, 得到了pem.Block結構體變量
block, _ := pem.Decode(buf)
//4. x509將數據解析成私鑰結構體 -> 得到了私鑰
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
panic(err)
}
//5. 創建一個哈希對象 -> md5/sha1 -> sha512
// sha512.Sum512()
myhash := sha512.New()
//6. 給哈希對象添加數據
myhash.Write(plainText)
//7. 計算哈希值
hashText := myhash.Sum(nil)
//8. 使用rsa中的函數對散列值簽名
sigText, err := rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA512, hashText)
if err != nil {
panic(err)
}
return sigText
}
// RSA簽名驗證
func VerifyRSA(plainText, sigText []byte, pubFileName string) bool {
//1. 打開公鑰文件, 將文件內容讀出 - []byte
file, err := os.Open(pubFileName)
if err != nil {
panic(err)
}
info, err := file.Stat()
if err != nil {
panic(err)
}
buf := make([]byte, info.Size())
file.Read(buf)
file.Close()
//2. 使用pem解碼 -> 得到pem.Block結構體變量
block, _ := pem.Decode(buf)
//3. 使用x509對pem.Block中的Bytes變量中的數據進行解析 -> 得到一接口
pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
panic(err)
}
//4. 進行類型斷言 -> 得到了公鑰結構體
publicKey := pubInterface.(*rsa.PublicKey)
//5. 對原始消息進行哈希運算(和簽名使用的哈希算法一致) -> 散列值
hashText := sha512.Sum512(plainText)
//6. 簽名認證 - rsa中的函數
err = rsa.VerifyPKCS1v15(publicKey, crypto.SHA512, hashText[:], sigText)
if err == nil {
return true
}
return false
}
func main() {
src := []byte("在消息認證碼中,需要發送者和接收者之間共享密鑰,而這個密鑰不能被主動攻擊者Mallory獲取。如果這個密鑰落入Mallory手中,則Mallory也可以計算出MAC值,從而就能夠自由地進行篡改和偽裝攻擊,這樣一來消息認證碼就無法發揮作用了。")
sigText := SignatureRSA(src, "private.pem")
bl := VerifyRSA(src, sigText, "public.pem")
fmt.Println(bl)
}
~~~
- 基礎
- 簡介
- 主要特征
- 變量和常量
- 編碼轉換
- 數組
- byte與rune
- big
- sort接口
- 和mysql類型對應
- 函數
- 閉包
- 工作區
- 復合類型
- 指針
- 切片
- map
- 結構體
- sync.Map
- 隨機數
- 面向對象
- 匿名組合
- 方法
- 接口
- 權限
- 類型查詢
- 異常處理
- error
- panic
- recover
- 自定義錯誤
- 字符串處理
- 正則表達式
- json
- 文件操作
- os
- 文件讀寫
- 目錄
- bufio
- ioutil
- gob
- 棧幀的內存布局
- shell
- 時間處理
- time詳情
- time使用
- new和make的區別
- container
- list
- heap
- ring
- 測試
- 單元測試
- Mock依賴
- delve
- 命令
- TestMain
- path和filepath包
- log日志
- 反射
- 詳解
- plugin包
- 信號
- goto
- 協程
- 簡介
- 創建
- 協程退出
- runtime
- channel
- select
- 死鎖
- 互斥鎖
- 讀寫鎖
- 條件變量
- 嵌套
- 計算單個協程占用內存
- 執行規則
- 原子操作
- WaitGroup
- 定時器
- 對象池
- sync.once
- 網絡編程
- 分層模型
- socket
- tcp
- udp
- 服務端
- 客戶端
- 并發服務器
- Http
- 簡介
- http服務器
- http客戶端
- 爬蟲
- 平滑重啟
- context
- httptest
- 優雅中止
- web服務平滑重啟
- beego
- 安裝
- 路由器
- orm
- 單表增刪改查
- 多級表
- orm使用
- 高級查詢
- 關系查詢
- SQL查詢
- 元數據二次定義
- 控制器
- 參數解析
- 過濾器
- 數據輸出
- 表單數據驗證
- 錯誤處理
- 日志
- 模塊
- cache
- task
- 調試模塊
- config
- 部署
- 一些包
- gjson
- goredis
- collection
- sjson
- redigo
- aliyunoss
- 密碼
- 對稱加密
- 非對稱加密
- 單向散列函數
- 消息認證
- 數字簽名
- mysql優化
- 常見錯誤
- go run的錯誤
- 新手常見錯誤
- 中級錯誤
- 高級錯誤
- 常用工具
- 協程-泄露
- go env
- gometalinter代碼檢查
- go build
- go clean
- go test
- 包管理器
- go mod
- gopm
- go fmt
- pprof
- 提高編譯
- go get
- 代理
- 其他的知識
- go內存對齊
- 細節總結
- nginx路由匹配
- 一些博客
- redis為什么快
- cpu高速緩存
- 常用命令
- Go 永久阻塞的方法
- 常用技巧
- 密碼加密解密
- for 循環迭代變量
- 備注
- 垃圾回收
- 協程和纖程
- tar-gz
- 紅包算法
- 解決golang.org/x 下載失敗
- 逃逸分析
- docker
- 鏡像
- 容器
- 數據卷
- 網絡管理
- 網絡模式
- dockerfile
- docker-composer
- 微服務
- protoBuf
- GRPC
- tls
- consul
- micro
- crontab
- shell調用
- gorhill/cronexpr
- raft
- go操作etcd
- mongodb