
本文深入探讨了在go语言中使用rsa-sha算法进行数字签名和验证的正确方法。我们将详细介绍如何加载pem格式的rsa私钥和公钥,并基于`crypto/rsa`和`crypto/sha256`包实现数据的签名与验证过程。文章重点纠正了常见的验证逻辑错误,提供了结构清晰、可直接运行的示例代码,并强调了关键的安全实践,旨在帮助开发者构建健壮可靠的数字签名系统。
数字签名是保障数据完整性、认证性和不可否认性的重要密码学技术。它利用非对称加密算法(如RSA)和哈希函数(如SHA256)来创建。签名过程通常包括:对原始数据计算哈希值,然后使用发送方的私钥对哈希值进行加密(签名)。验证过程则相反:接收方使用发送方的公钥解密(验证)签名,得到原始哈希值,同时独立计算接收到数据的哈希值,比对两者是否一致。如果一致,则证明数据未被篡改且确实来自私钥的持有者。
Go标准库提供了强大的密码学支持,主要涉及以下几个包:
在Go语言中,实现RSA签名首先需要加载PEM格式的私钥文件,然后使用该私钥对数据的哈希值进行签名。
RSA私钥通常以PEM格式存储,常见的有PKCS#1和PKCS#8两种格式。本教程中的示例使用PKCS#1格式,其PEM块类型为RSA PRIVATE KEY。
-----BEGIN RSA PRIVATE KEY----- ... (Base64 encoded private key data) ... -----END RSA PRIVATE KEY-----
通过encoding/pem解码PEM块,然后使用x509.ParsePKCS1PrivateKey解析PKCS#1格式的私钥。
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"log"
"os" // 使用 os.ReadFile 替代 ioutil.ReadFile
)
// Signer 接口定义了签名方法
type Signer interface {
Sign(data []byte) ([]byte, error)
}
// rsaPrivateKey 结构体包装了 rsa.PrivateKey
type rsaPrivateKey struct {
*rsa.PrivateKey
}
// Sign 方法实现了数据的RSA-SHA256签名
func (r *rsaPrivateKey) Sign(data []byte) ([]byte, error) {
// 1. 计算原始数据的SHA256哈希值
h := sha256.New()
h.Write(data)
hashed := h.Sum(nil)
// 2. 使用rsa.SignPKCS1v15进行签名
// rand.Reader 提供密码学安全的随机数源
// crypto.SHA256 指明使用的哈希算法
return rsa.SignPKCS1v15(rand.Reader, r.PrivateKey, crypto.SHA256, hashed)
}
// parsePrivateKey 解析PEM编码的私钥字节
func parsePrivateKey(pemBytes []byte) (Signer, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("ssh: no PEM block found")
}
var rawkey interface{}
switch block.Type {
case "RSA PRIVATE KEY":
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PKCS#1 private key: %w", err)
}
rawkey = rsaPrivKey
// 可以添加对PKCS#8格式的支持
// case "PRIVATE KEY":
// rsaPrivKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
// if err != nil {
// return nil, fmt.Errorf("failed to parse PKCS#8 private key: %w", err)
// }
// if key, ok := rsaPrivKey.(*rsa.PrivateKey); ok {
// rawkey = key
// } else {
// return nil, fmt.Errorf("unsupported private key type in PKCS#8: %T", rsaPrivKey)
// }
default:
return nil, fmt.Errorf("ssh: unsupported private key type %q", block.Type)
}
return newSignerFromKey(rawkey)
}
// loadPrivateKey 从文件加载并解析私钥
func loadPrivateKey(path string) (Signer, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read private key file: %w", err)
}
return parsePrivateKey(data)
}
func newSignerFromKey(k interface{}) (Signer, error) {
if t, ok := k.(*rsa.PrivateKey); ok {
return &rsaPrivateKey{t}, nil
}
return nil, fmt.Errorf("ssh: unsupported key type %T for signing", k)
}签名验证是数字签名流程的关键一步,它需要加载公钥,然后使用公钥来验证签名的有效性。
RSA公钥通常以PEM格式存储,常见的有PKIX格式,其PEM块类型为PUBLIC KEY。
-----BEGIN PUBLIC KEY----- ... (Base64 encoded public key data) ... -----END PUBLIC KEY-----
通过encoding/pem解码PEM块,然后使用x509.ParsePKIXPublicKey解析PKIX格式的公钥。
Reachout.ai
一个AI驱动的视频开发平台,专为忙碌的企业家和销售团队打造
142
查看详情
核心问题在于:签名验证不是解密操作。 原始代码中的Unsign方法错误地使用了rsa.EncryptPKCS1v15,这实际上是加密操作,而非验证。正确的做法是使用rsa.VerifyPKCS1v15函数,它需要原始数据、哈希算法类型、原始数据的哈希值以及签名值作为输入。
// Verifier 接口定义了验证方法
type Verifier interface {
Verify(message []byte, signature []byte) error
}
// rsaPublicKey 结构体包装了 rsa.PublicKey
type rsaPublicKey struct {
*rsa.PublicKey
}
// Verify 方法实现了RSA-SHA256签名验证
func (r *rsaPublicKey) Verify(message []byte, signature []byte) error {
// 1. 计算原始消息的SHA256哈希值
h := sha256.New()
h.Write(message)
hashed := h.Sum(nil)
// 2. 使用rsa.VerifyPKCS1v15进行验证
// r.PublicKey 是用于验证的公钥
// crypto.SHA256 指明签名时使用的哈希算法
// hashed 是原始消息的哈希值
// signature 是待验证的签名值
return rsa.VerifyPKCS1v15(r.PublicKey, crypto.SHA256, hashed, signature)
}
// parsePublicKey 解析PEM编码的公钥字节
func parsePublicKey(pemBytes []byte) (Verifier, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("ssh: no PEM block found")
}
var rawkey interface{}
switch block.Type {
case "PUBLIC KEY":
rsaPubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PKIX public key: %w", err)
}
if key, ok := rsaPubKey.(*rsa.PublicKey); ok {
rawkey = key
} else {
return nil, fmt.Errorf("unsupported public key type in PKIX: %T", rsaPubKey)
}
default:
return nil, fmt.Errorf("ssh: unsupported public key type %q", block.Type)
}
return newVerifierFromKey(rawkey)
}
// loadPublicKey 从文件加载并解析公钥
func loadPublicKey(path string) (Verifier, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read public key file: %w", err)
}
return parsePublicKey(data)
}
func newVerifierFromKey(k interface{}) (Verifier, error) {
if t, ok := k.(*rs
a.PublicKey); ok {
return &rsaPublicKey{t}, nil
}
return nil, fmt.Errorf("ssh: unsupported key type %T for verification", k)
}以下是一个整合了上述私钥签名和公钥验证逻辑的完整Go程序。它包括了密钥加载、数据签名、签名值Base64编码以及签名验证的全过程。
首先,请确保您有private.pem和public.pem文件在运行目录下。示例文件内容如下:
private.pem:
-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj+CvfSC8+q28hxA161QF NUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6Z4UMR7EOcpfdUE9Hf3m/hs+F UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJwoYi+1hqp1fIekaxsyQIDAQAB AoGBAJR8ZkCUvx5kzv+utdl7T5MnordT1TvoXXJGXK7ZZ+UuvMNUCdN2QPc4sBiA QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9+xEpubyeW2oH4Zx71wqBtOK kqwrXa/pzdpiucRRjk6vE6YY7EBBs/g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR/Zm3pR5m0tCmBqa5RK95u 412jt1dPIwJBANJT3v8pnkth48bQo/fKel6uEYyboRtA5/uHuHkZ6FQF7OUkGogc mSJluOdc5t6hI1VsLn0QZEjQZMEOWr+wKSMCQQCC4kXJEsHAve77oP6HtG/IiEn7 kpyUXRNvFsDE0czpJJBvL/aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci/2ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI 7U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA== -----END RSA PRIVATE KEY-----
public.pem:
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3 6rPJj+CvfSC8+q28hxA161QFNUd13wuCTUcq0Qd2qsBe/2hFyc2DCJJg0h1L78+6 Z4UMR7EOcpfdUE9Hf3m/hs+FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc+2xjJw oYi+1hqp1fIekaxsyQIDAQAB -----END PUBLIC KEY-----
main.go:
package main
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"log"
"os"
)
// Signer 接口定义了签名方法
type Signer interface {
Sign(data []byte) ([]byte, error)
}
// Verifier 接口定义了验证方法
type Verifier interface {
Verify(message []byte, signature []byte) error
}
// rsaPrivateKey 结构体包装了 rsa.PrivateKey
type rsaPrivateKey struct {
*rsa.PrivateKey
}
// Sign 方法实现了数据的RSA-SHA256签名
func (r *rsaPrivateKey) Sign(data []byte) ([]byte, error) {
h := sha256.New()
h.Write(data)
hashed := h.Sum(nil)
return rsa.SignPKCS1v15(rand.Reader, r.PrivateKey, crypto.SHA256, hashed)
}
// rsaPublicKey 结构体包装了 rsa.PublicKey
type rsaPublicKey struct {
*rsa.PublicKey
}
// Verify 方法实现了RSA-SHA256签名验证
func (r *rsaPublicKey) Verify(message []byte, signature []byte) error {
h := sha256.New()
h.Write(message)
hashed := h.Sum(nil)
return rsa.VerifyPKCS1v15(r.PublicKey, crypto.SHA256, hashed, signature)
}
// parsePrivateKey 解析PEM编码的私钥字节
func parsePrivateKey(pemBytes []byte) (Signer, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("ssh: no PEM block found")
}
var rawkey interface{}
switch block.Type {
case "RSA PRIVATE KEY":
rsaPrivKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PKCS#1 private key: %w", err)
}
rawkey = rsaPrivKey
default:
return nil, fmt.Errorf("ssh: unsupported private key type %q", block.Type)
}
return newSignerFromKey(rawkey)
}
// loadPrivateKey 从文件加载并解析私钥
func loadPrivateKey(path string) (Signer, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read private key file: %w", err)
}
return parsePrivateKey(data)
}
func newSignerFromKey(k interface{}) (Signer, error) {
if t, ok := k.(*rsa.PrivateKey); ok {
return &rsaPrivateKey{t}, nil
}
return nil, fmt.Errorf("ssh: unsupported key type %T for signing", k)
}
// parsePublicKey 解析PEM编码的公钥字节
func parsePublicKey(pemBytes []byte) (Verifier, error) {
block, _ := pem.Decode(pemBytes)
if block == nil {
return nil, errors.New("ssh: no PEM block found")
}
var rawkey interface{}
switch block.Type {
case "PUBLIC KEY":
rsaPubKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse PKIX public key: %w", err)
}
if key, ok := rsaPubKey.(*rsa.PublicKey); ok {
rawkey = key
} else {
return nil, fmt.Errorf("unsupported public key type in PKIX: %T", rsaPubKey)
}
default:
return nil, fmt.Errorf("ssh: unsupported public key type %q", block.Type)
}
return newVerifierFromKey(rawkey)
}
// loadPublicKey 从文件加载并解析公钥
func loadPublicKey(path string) (Verifier, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read public key file: %w", err)
}
return parsePublicKey(data)
}
func newVerifierFromKey(k interface{}) (Verifier, error) {
if t, ok := k.(*rsa.PublicKey); ok {
return &rsaPublicKey{t}, nil
}
return nil, fmt.Errorf("ssh: unsupported key type %T for verification", k)
}
func main() {
// 1. 加载私钥进行签名
signer, err := loadPrivateKey("private.pem")
if err != nil {
log.Fatalf("failed to load private key: %v", err)
}
messageToSign := "date: Thu, 05 Jan 2012 21:31:40 GMT"以上就是Go语言中RSA-SHA数字签名与验证的正确实践的详细内容,更多请关注其它相关文章!
# 莞城seo培训
# 原始数据
# 资源管理
# 常以
# 是一个
# 这是
# 几个
# seo网站推广佛山
# 郑州网站建设九零后
# 实现了
# SEO目录排版
# 网站优化定做
# 河北招商网站推广介绍
# 全网营销线上推广陈经理
# 攀枝花网站的优化
# 炸串店怎样推广营销产品
# 奇门遁甲关键词排名
# js
# 装了
# 公钥
# 加载
# crypto
# yy
# 标准库
# cdn
# switch
# ai
# qq
# 字节
# 编码
# go语言
# go
# git
相关文章:
C++如何实现线程池_C++11手动实现一个简单的固定大小线程池
解决Bootstrap卡片顶部边距导致背景图下移的问题
提升Kafka消费者健壮性:会话超时处理与消息处理语义
J*aScript类型检查_j*ascript代码规范
Lar*el头像管理:图片缩放与旧文件删除的最佳实践
Yandex官方入口网址 Yandex俄罗斯搜索引擎最新在线地址
J*aScript中在Map循环中检测并处理空数组元素
PHP表单提交后函数重复执行的解决方案:管理$_POST数据
C++如何实现一个装饰器模式_C++设计模式之动态地给对象添加额外职责
如何有效阻止外部脚本意外修改内联样式的高度属性
J*aScript DOM操作:高效清空列表元素的策略与实践
反效果?《战地6》免费试玩开启后玩家数不升反降
2025-2030年全球乘用车销量预测:新能源成增长主力
漫蛙MANWA漫画主页官方入口 漫蛙漫画最新在线阅读地址
俄罗斯浏览器官网直达链接 俄罗斯浏览器最新在线入口导航
12306怎么选座位选到安静区_12306选座安静区域选择策略
Win11怎么关闭快速启动_Win11彻底关机设置教程
零跑汽车11月交付量达70327台 实现连续9个月正增长
谷歌推RCS信息存档功能:公司可监控员工私密信息!
sublime如何配置Python开发环境_将sublime打造成轻量级Python IDE
新手怎么开始学化妆 零基础化妆入门教程
Yandex官网免登录入口_俄罗斯Yandex搜索引擎一键访问
Walmart退货API集成指南:PHP cURL实现与常见问题解析
火狐浏览器占用内存高卡顿怎么办 火狐浏览器性能优化设置技巧
初次安装JDK时环境变量如何正确配置_J*A_HOME与PATH设置规则讲解
JUnit5/Mockito:优雅测试内部依赖与异常处理的实践
css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染
c++如何使用TBB库进行任务并行_c++ Intel线程构建模块
Node.js CSV 数据处理:基于字段值条件过滤整条记录的策略
KFC早餐时段怎么领特惠代码_KFC早餐订餐优惠代码获取与使用说明
msn官网入口地址手机版 msn官方网站手机最新链接
SteamMachine定价或为699美元 大家想入手吗?
CSS自定义字体样式被系统字体替换怎么办_font-face方式指定font-display控制渲染策略
Win11 BitLocker密码忘了怎么办 Win11找回BitLocker恢复密钥方法【解决】
照顾宝贝2小游戏免费秒玩入口
Mudbox图层蒙版怎么用_Mudbox图层蒙版数字雕刻应用技巧
谷歌浏览器浏览体验优化_谷歌浏览器新版直连永久可用提示
mcjs网页版流畅运行 mcjs低配电脑畅玩入口
如何在复杂的电商平台中优雅地管理共享资源并确保正确重定向,使用spryker-shop/resource-share-page模块助你一臂之力
excel如何生成目录 excel一键生成工作表目录超链接
ExcelARRAYTOTEXT函数怎么自定义分隔符输出数组文本_ARRAYTOTEXT实现动态生成SQL语句
Adobe PDF表单中利用J*aScript解析与格式化日期组件的教程
QQ邮箱电脑版登录入口_QQ邮箱官方网站登录平台
顺丰快件物流信息 官方网站查询入口
J*aScript中正确使用querySelectorAll与复杂CSS选择器
Lar*el Excel导入时生成自定义递增ID的策略与实践
Win10怎么设置静态IP地址 Win10手动配置IP地址步骤【指南】
Bilibili动漫最新防封地址发布-Bilibili动漫2025年最稳正版入口推荐
Angular Material 垂直步进器:实现底部到顶部排序的教程
MAC怎么在地图App里使用“四处看看”_MAC体验部分城市的3D实景街景
*请认真填写需求信息,我们会在24小时内与您取得联系。