当前位置: 首页 > article >正文

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

HSRSAESED 四种签名算法:

一、算法对比

属性HSRSAESED
加密类型对称加密非对称加密非对称加密非对称加密
密钥长度任意长度私钥: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

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

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

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

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


http://www.kler.cn/a/517755.html

相关文章:

  • Linux 内核进程调度
  • 粒子群算法 笔记 数学建模
  • RNN实现阿尔茨海默症的诊断识别
  • hadoop==docker desktop搭建hadoop
  • Java 实现Excel转HTML、或HTML转Excel
  • Linux Futex学习笔记
  • C# OpenCV机器视觉:实现农作物病害检测
  • 【转帖】eclipse-24-09版本后,怎么还原原来版本的搜索功能
  • vulshare/nginx-php-flag命令执行漏洞
  • 8、提升用户体验的技巧
  • STM32新建不同工程的方式
  • 如何运用python爬虫获取大型资讯类网站文章,并同时导出pdf或word格式文本?
  • 【图文详解】lnmp架构搭建Discuz论坛
  • 纯css实现div宽度可调整
  • 为什么 TCP 挥手需要有 TIME_WAIT 状态?
  • 论文阅读的附录(七):Understanding Diffusion Models: A Unified Perspective(二):公式46的推导
  • 计算机图形学:实验三 光照与阴影
  • IBM 后端开发(二)
  • 【Project】CupFox电影网站数据爬取分析与可视化
  • 三代PacBio HiFi SV检测工具的安装
  • springboot基于spark的保险平台用户行为分析与研究
  • OpenAI Edge-TTS的使用方法
  • DAY5, 使用read 和 write 实现链表保存到文件,以及从文件加载数据到链表中的功能
  • 《Effective Java》学习笔记——第8部分 序列化
  • PyQt5菜单加多页签实现
  • Python爬虫之——Cookie存储器