[TOC]
# 数字签名
## 数字签名的方法
![](https://box.kancloud.cn/60527f6bb8f7bfeea399cfe22cc7b60d_649x619.png)
## 签名的生成和验证
> 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
> ```
## 数字签名无法解决的问题
![](https://box.kancloud.cn/e4e6e9e370d4787c509fde0166ac53ce_615x327.png)
## 代码
### 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