【加密算法】简单区分HS、RSA、ES 和 ED,与对应go实现案例

HSRSAESED 四种签名算法:

一、算法对比

属性 HS RSA ES ED
加密类型 对称加密 非对称加密 非对称加密 非对称加密
密钥长度 任意长度 私钥:2048+ 位 私钥:256+ 位 私钥:256 位(Ed25519)
签名效率 较低
验证效率 较低
安全性
密钥分离 不支持 支持 支持 支持
典型场景 内部系统通信 安全性要求高的场景 移动设备和 IoT 场景 安全敏感的高效场景

二、构建过程

1. HS (HMAC-SHA) 密钥生成流程

步骤 操作代码 说明
1. 定义密钥长度 key := make([]byte, 32) 定义对称密钥长度,常用 256 位(32 字节)。
2. 生成随机密钥 _, err := rand.Read(key) 使用随机生成器填充密钥。
3. 保存到文件 os.WriteFile("hmac.key", key, 0644) 将密钥以二进制文件形式保存到本地,确保权限控制。

2. RSA 密钥生成流程

步骤 操作代码 说明
1. 生成密钥对 privateKey, err := rsa.GenerateKey(rand.Reader, 2048) 使用 RSA 生成 2048 位密钥对。
2. 转换为 x509 格式 x509PrivateKey := x509.MarshalPKCS8PrivateKey(privateKey) 将私钥封装为 PKCS8 格式。
x509PublicKey := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) 将公钥封装为 PKIX 格式。注意这里传地址
3. 封装为 PEM 格式 privateBlock := &pem.Block{Type: "PRIVATE KEY", Bytes: x509PrivateKey} 使用 PEM 格式封装私钥。
publicBlock := &pem.Block{Type: "PUBLIC KEY", Bytes: x509PublicKey} 使用 PEM 格式封装公钥。
4. 保存到文件 os.OpenFile + pem.Encode 将密钥分别写入 private.pempublic.pem 文件。

3. ES (Elliptic Curve) 密钥生成流程

步骤 操作代码 说明
1. 定义曲线 curve := elliptic.P256() 选择椭圆曲线(如 P256)。
2. 生成密钥对 privateKey, err := ecdsa.GenerateKey(es.getCurve(), rand.Reader) 生成私钥及公钥。
3. 转换为 x509 格式 x509PrivateKey := x509.MarshalECPrivateKey(privateKey) 将私钥封装为 x509 格式。
4. 封装为 PEM 格式 privateBlock := &pem.Block{Type: "EC PRIVATE KEY", Bytes: x509PrivateKey} 使用 PEM 格式封装私钥。
publicBlock := &pem.Block{Type: "PUBLIC KEY", Bytes: x509PublicKey} 使用 PEM 格式封装公钥。
5. 保存到文件 os.OpenFile + pem.Encode 将密钥分别写入 ec_private.pemec_public.pem 文件。

4. ED (Ed25519) 密钥生成流程

步骤 操作代码 说明
1. 生成密钥对 publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) 使用 Ed25519 算法生成密钥对,密钥长度固定。
2. 转换为 PEM 格式 privateBlock := &pem.Block{Type: "PRIVATE KEY", Bytes: privateKey} 使用 PEM 格式封装私钥。
publicBlock := &pem.Block{Type: "PUBLIC KEY", Bytes: publicKey} 使用 PEM 格式封装公钥。
3. 保存到文件 os.OpenFile + pem.Encode 将密钥分别写入 ed_private.pemed_public.pem 文件。

总结表格

加密方式 密钥类型 生成流程关键步骤
HS 对称密钥 随机生成固定长度的密钥,直接保存到文件。
RSA 公钥 + 私钥 生成密钥对 → 转换为 x509 → 封装为 PEM → 写入文件。
ES 公钥 + 私钥 选择椭圆曲线 → 生成密钥对 → 转换为 x509 → 封装为 PEM → 写入文件。
ED 公钥 + 私钥 直接生成 Ed25519 密钥对 → 封装为 PEM → 写入文件。

go案例实现

hs.go

go 复制代码
package secret

import (
	"encoding/hex"
	"math/rand"
	"time"
)

// HsGenerator HS HS256、HS384、HS512 属于对称加密算法。
// 加密和验证使用相同的密钥。
type HsGenerator struct {
	Length int
}

// Generate 生成一个指定长度随机密钥,将其编码为16进制字符串返回
// 生成过程,直接生成密钥
func (hs *HsGenerator) Generate() (*OutSecret, error) {
	out := &OutSecret{}
	length := 32
	if hs.Length > 0 {
		length = hs.Length
	}
	//rand.Seed(time.Now().UnixNano())
	rand.New(rand.NewSource(time.Now().UnixNano()))
	b := make([]byte, length)
	rand.Read(b)
	out.Secret = hex.EncodeToString(b)[:length]
	return out, nil
}

rsa.go

go 复制代码
package secret

import (
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/pem"
	"log"
	"os"
)

type RsGenerator struct {
}

// Generate 生成RSA密钥对
// 步骤:
// 生成RSA私钥和公钥。
// 将私钥和公钥转换为X509格式。
// 将私钥和公钥封装为PEM格式并保存到指定路径。
// 返回包含私钥和公钥文件路径的对象。
func (rs *RsGenerator) Generate() (*OutSecret, error) {
	out := &OutSecret{}
	var err error
	// 生成密钥对
	privateKey, err := rsa.GenerateKey(rand.Reader, 1024)
	// rsa.GenerateKey 用于生成指定位数rsa密钥对 rand.Reader提供加密安全随机数,1024位数
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}

	// x509格式封装
	// x509是一种公私钥证书标准格式,定义公钥证书结构
	// 用x509封装,保证一致性
	x509PrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	// ,privateKey.PublicKey 是一个 rsa.PublicKey 类型的值(不是指针),
	// 因此你需要使用 & 来获取它的地址,以便将其传递给 x509.MarshalPKIXPublicKey 函数。
	// 这里接收的是 &privateKey.PublicKey,表示获取 privateKey.PublicKey 的地址。
	x509PublicKey, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}

	// 表示 PEM 编码的数据块。block用于保存加密密钥、证书等二进制数据。
	privateBlock := &pem.Block{
		Type:  "PRIVATE KEY",  // 数据块类型,例如"PRIVATE KEY" 或 "PUBLIC KEY"
		Bytes: x509PrivateKey, // 用x509封装的,实际的二进制数据
	}
	// 生成对应文件
	privateKeyFile := KEY_PATH + "/rsa/private.pem"
	privateFile, err := os.OpenFile(privateKeyFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	defer privateFile.Close()
	pem.Encode(privateFile, privateBlock) // pem.Encode将block数据块写入到io.Writer接口,这里就是文件

	publicBlock := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: x509PublicKey,
	}
	publicKeyFile := KEY_PATH + "/rsa/public.pem"
	publicFile, err := os.OpenFile(publicKeyFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	defer publicFile.Close()
	pem.Encode(publicFile, publicBlock)

	out.PrivateKeyFile = privateKeyFile
	out.PublicKeyFile = publicKeyFile

	return out, nil

}

ed.go

go 复制代码
package secret

import (
	"crypto/ed25519"
	"crypto/rand"
	"crypto/x509"
	"encoding/pem"
	"log"
	"os"
)

type EdGenerator struct {
}

func (ed *EdGenerator) Generate() (*OutSecret, error) {
	out := &OutSecret{}
	publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// x509格式封装
	x509PrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey) // ed25519的这里privateKey不是一个指针
	if err != nil {
		log.Println(err)
		return nil, err
	}
	x509PublicKey, err := x509.MarshalPKIXPublicKey(publicKey)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// 设置pem编码的数据块
	privateBlock := &pem.Block{
		Type:  "PRIVATE_KEY",
		Bytes: x509PrivateKey,
	}
	privateKeyFile := KEY_PATH + "/ed/private.pem"
	privateFile, err := os.OpenFile(privateKeyFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	defer privateFile.Close()
	pem.Encode(privateFile, privateBlock) // pem编码

	publicBlock := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: x509PublicKey,
	}
	publicKeyFile := KEY_PATH + "/ed/public.pem"
	publicFile, err := os.OpenFile(publicKeyFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	defer publicFile.Close()
	pem.Encode(publicFile, publicBlock)

	out.PrivateKeyFile = privateKeyFile
	out.PublicKeyFile = publicKeyFile

	return out, nil
}

es.go

go 复制代码
package secret

import (
	"crypto/ecdsa"
	"crypto/elliptic"
	"crypto/rand"
	"crypto/x509"
	"encoding/pem"
	"log"
	"os"
)

/*
为什么需要分类
在 ES 算法中,不同的签名方法(如 ES256、ES384、ES512)
对应不同的椭圆曲线和哈希函数。具体来说:
ES256 使用 P-256 曲线和 SHA-256 哈希函数。
ES384 使用 P-384 曲线和 SHA-384 哈希函数。
ES512 使用 P-521 曲线和 SHA-512 哈希函数。
这些不同的曲线和哈希函数提供了不同级别的安全性。
通过定义常量和类型 ESSigningMethodS,可以方便地管理和切换不同的签名方法,确保代码的灵活性和可扩展性
*/
const (
	ES256 ESSigningMethodS = "ES256"
	ES384 ESSigningMethodS = "ES384"
	ES512 ESSigningMethodS = "ES512"
)

type ESSigningMethodS string

type EsGenerator struct {
	SigningMethod ESSigningMethodS
}

func (es *EsGenerator) getCurve() elliptic.Curve {
	switch es.SigningMethod {
	case ES256:
		return elliptic.P256()
	case ES384:
		return elliptic.P384()
	case ES512:
		return elliptic.P521()
	default:
		return elliptic.P256()
	}
}

func (es *EsGenerator) Generate() (*OutSecret, error) {
	out := &OutSecret{}
	privateKey, err := ecdsa.GenerateKey(es.getCurve(), rand.Reader)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// x509格式封装
	x509PrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey) // privateKey是一个指针
	if err != nil {
		log.Println(err)
		return nil, err
	}
	// ,privateKey.PublicKey 是一个 rsa.PublicKey 类型的值(不是指针),
	// 因此你需要使用 & 来获取它的地址,以便将其传递给 x509.MarshalPKIXPublicKey 函数。
	// 这里接收的是 &privateKey.PublicKey,表示获取 privateKey.PublicKey 的地址。
	x509PublicKey, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
	if err != nil {
		log.Println(err)
		return nil, err
	}

	// 设置pem编码的数据块
	privateBlock := &pem.Block{
		Type:  "PRIVATE_KEY",
		Bytes: x509PrivateKey,
	}
	privateKeyFile := KEY_PATH + "/es/private.pem"
	privateFile, err := os.OpenFile(privateKeyFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	defer privateFile.Close()
	pem.Encode(privateFile, privateBlock) // pem编码

	publicBlock := &pem.Block{
		Type:  "PUBLIC KEY",
		Bytes: x509PublicKey,
	}
	publicKeyFile := KEY_PATH + "/es/public.pem"
	publicFile, err := os.OpenFile(publicKeyFile, os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalln(err)
		return nil, err
	}
	defer publicFile.Close()
	pem.Encode(publicFile, publicBlock)

	out.PrivateKeyFile = privateKeyFile
	out.PublicKeyFile = publicKeyFile

	return out, nil
}

https://github.com/0voice

相关推荐
雾岛听蓝9 小时前
Qt操作指南:窗口组成与菜单栏
开发语言·经验分享·笔记·qt
kyriewen9 小时前
你的首屏慢得像蜗牛?这6招让页面“秒开”
前端·面试·性能优化
段小二9 小时前
服务一重启全丢了——Spring AI Alibaba Agent 三层持久化完整方案
java·后端
UIUV9 小时前
Go语言入门到精通学习笔记
后端·go·编程语言
lizhongxuan9 小时前
开发 Agent 的坑
后端
段小二9 小时前
Agent 自动把机票改错了,推理完全正确——这才是真正的风险
java·后端
zopple9 小时前
Laravel vs ThinkPHP:PHP框架终极对决
开发语言·php·laravel
算是难了10 小时前
Nestjs学习总结_3
前端·typescript·node.js
itjinyin10 小时前
ShardingSphere-jdbc 5.5.0 + spring boot 基础配置 - 实战篇
java·spring boot·后端
松☆10 小时前
C++ 算法竞赛题解:P13569 [CCPC 2024 重庆站] osu!mania —— 浮点数精度陷阱与 `eps` 的深度解析
开发语言·c++·算法