【加密算法】简单区分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

相关推荐
喜欢吃燃面2 分钟前
C++算法竞赛:位运算
开发语言·c++·学习·算法
草莓熊Lotso5 分钟前
《详解 C++ Date 类的设计与实现:从运算符重载到功能测试》
开发语言·c++·经验分享·笔记·其他
水痕016 分钟前
gin结合minio来做文件存储
java·eureka·gin
谱写秋天12 分钟前
Qt 5.5 的安装与配置(使用 VSCode编辑)
开发语言·vscode·qt
Victor35613 分钟前
Redis(8)如何安装Redis?
后端
项目申报小狂人13 分钟前
算法应用上新!自适应更新策略差分进化算法求解球形多飞行器路径规划问题,附完整MATLAB代码
开发语言·算法·matlab
寒士obj15 分钟前
Spring事物
java·spring
Victor35618 分钟前
Redis(9)如何启动和停止Redis服务?
后端
你好,赵志伟1 小时前
Socket 编程 TCP
linux·服务器·tcp/ip