完善不同加密方法的使用
This commit is contained in:
+451
-340
@@ -1,371 +1,482 @@
|
||||
// Package encryptx 提供一个统一的、字符串驱动的加解密门面,覆盖哈希、对称加密、
|
||||
// 消息认证码、密钥派生、口令哈希、非对称加密与数字签名。
|
||||
//
|
||||
// # 定位与建议
|
||||
//
|
||||
// 本包适合「算法由配置/管理后台决定」的场景:调用方从配置里读到算法名,转成
|
||||
// EncryptType 后交给 Encrypt/Decrypt 即可,无需为每种算法写分支。
|
||||
//
|
||||
// 如果你在写业务代码而不是配置驱动的调度代码,**优先直接使用子包**
|
||||
// (aesx、chachax、rsax、ed25519x、argon2x 等):它们有强类型的参数、
|
||||
// 更明确的错误,并且不需要在运行时解析选项。
|
||||
//
|
||||
// # 密钥约定
|
||||
//
|
||||
// 密钥一律是 []byte,绝不使用字符串:
|
||||
//
|
||||
// - 对称算法、HMAC、KDF:key 是**原始密钥字节**(不是十六进制、不是 Base64)。
|
||||
// 用相应子包的 GenerateKey 获取;
|
||||
// - RSA / Ed25519 / ECDSA:key 是 **PEM 编码的密钥内容**(公钥用于加密/验签,
|
||||
// 私钥用于解密/签名)。本包不会读取文件,需要从文件加载时请调用
|
||||
// rsax.LoadPublicKey 等函数。
|
||||
//
|
||||
// 历史版本把密钥当字符串并直接按字节使用,而 GenerateKey 返回的却是十六进制
|
||||
// 文本,导致「生成密钥 → 加密」这条路径必然失败。统一为 []byte 后这类错误在
|
||||
// 类型层面就不可能出现。
|
||||
//
|
||||
// # 输出格式
|
||||
//
|
||||
// 不同算法族的输出编码不同,这是为了与既有系统保持一致:
|
||||
//
|
||||
// - 哈希 / HMAC / HEX 编码:小写十六进制;
|
||||
// - Base64 编码:标准 Base64(带填充);
|
||||
// - 分组密码模式(ECB/CBC/CFB/OFB/CTR):**大写**十六进制,且不含换行;
|
||||
// - AEAD(AES-GCM、ChaCha20-Poly1305):"密文十六进制:nonce十六进制"(小写、
|
||||
// 无换行),两者以冒号分隔;
|
||||
// - RSA 加密、数字签名:标准 Base64。
|
||||
package encryptx
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"code.yun.ink/pkg/encryptx/aesx"
|
||||
"code.yun.ink/pkg/encryptx/base64x"
|
||||
"code.yun.ink/pkg/encryptx/desx"
|
||||
"code.yun.ink/pkg/encryptx/hmacx"
|
||||
"code.yun.ink/pkg/encryptx/md5x"
|
||||
"code.yun.ink/pkg/encryptx/pbkdf2x"
|
||||
"code.yun.ink/pkg/encryptx/rsax"
|
||||
"code.yun.ink/pkg/encryptx/sha1x"
|
||||
"code.yun.ink/pkg/encryptx/sha256x"
|
||||
"code.yun.ink/pkg/encryptx/sha3x"
|
||||
"code.yun.ink/pkg/encryptx/sha512x"
|
||||
"code.yun.ink/pkg/encryptx/tdesx"
|
||||
"code.yun.ink/pkg/encryptx/utilsx"
|
||||
)
|
||||
|
||||
// EncryptType 加密类型枚举
|
||||
// EncryptType 标识一种密码学算法。
|
||||
//
|
||||
// 枚举值显式赋值而非常量递增(iota),因为历史版本用的是连续 iota:一旦在中间
|
||||
// 插入新算法,所有后续算法的数值都会平移,导致已经落库的配置指向错误的算法。
|
||||
// 这里保留原有数值不变,新算法一律追加在末尾。
|
||||
type EncryptType int
|
||||
|
||||
// 哈希算法。
|
||||
const (
|
||||
// 哈希算法
|
||||
MD5 EncryptType = iota
|
||||
SHA1
|
||||
SHA256
|
||||
SHA512
|
||||
SHA3_256
|
||||
SHA3_384
|
||||
SHA3_512
|
||||
|
||||
// 对称加密算法
|
||||
DES_ECB
|
||||
DES_CBC
|
||||
DES_CFB
|
||||
TDES_ECB
|
||||
TDES_CBC
|
||||
TDES_CFB
|
||||
AES_ECB
|
||||
AES_CBC
|
||||
AES_CFB
|
||||
AES_GCM
|
||||
BLOWFISH_ECB
|
||||
BLOWFISH_CBC
|
||||
BLOWFISH_CFB
|
||||
|
||||
// 非对称加密算法
|
||||
RSA
|
||||
|
||||
// 消息认证码
|
||||
HMAC_SHA256
|
||||
HMAC_SHA512
|
||||
|
||||
// 密钥派生
|
||||
PBKDF2
|
||||
|
||||
// 编码
|
||||
BASE64
|
||||
MD5 EncryptType = 0
|
||||
SHA1 EncryptType = 1
|
||||
SHA256 EncryptType = 2
|
||||
SHA512 EncryptType = 3
|
||||
SHA3_256 EncryptType = 4
|
||||
SHA3_384 EncryptType = 5
|
||||
SHA3_512 EncryptType = 6
|
||||
)
|
||||
|
||||
var encryptTypeNames = map[EncryptType]string{
|
||||
MD5: "MD5",
|
||||
SHA1: "SHA1",
|
||||
SHA256: "SHA256",
|
||||
SHA512: "SHA512",
|
||||
SHA3_256: "SHA3-256",
|
||||
SHA3_384: "SHA3-384",
|
||||
SHA3_512: "SHA3-512",
|
||||
DES_ECB: "DES-ECB",
|
||||
DES_CBC: "DES-CBC",
|
||||
DES_CFB: "DES-CFB",
|
||||
TDES_ECB: "3DES-ECB",
|
||||
TDES_CBC: "3DES-CBC",
|
||||
TDES_CFB: "3DES-CFB",
|
||||
AES_ECB: "AES-ECB",
|
||||
AES_CBC: "AES-CBC",
|
||||
AES_CFB: "AES-CFB",
|
||||
AES_GCM: "AES-GCM",
|
||||
BLOWFISH_ECB: "Blowfish-ECB",
|
||||
BLOWFISH_CBC: "Blowfish-CBC",
|
||||
BLOWFISH_CFB: "Blowfish-CFB",
|
||||
RSA: "RSA",
|
||||
HMAC_SHA256: "HMAC-SHA256",
|
||||
HMAC_SHA512: "HMAC-SHA512",
|
||||
PBKDF2: "PBKDF2",
|
||||
BASE64: "Base64",
|
||||
// 对称加密算法(历史数值区间)。
|
||||
const (
|
||||
DES_ECB EncryptType = 7
|
||||
DES_CBC EncryptType = 8
|
||||
DES_CFB EncryptType = 9
|
||||
TDES_ECB EncryptType = 10
|
||||
TDES_CBC EncryptType = 11
|
||||
TDES_CFB EncryptType = 12
|
||||
AES_ECB EncryptType = 13
|
||||
AES_CBC EncryptType = 14
|
||||
AES_CFB EncryptType = 15
|
||||
AES_GCM EncryptType = 16
|
||||
BLOWFISH_ECB EncryptType = 17
|
||||
BLOWFISH_CBC EncryptType = 18
|
||||
BLOWFISH_CFB EncryptType = 19
|
||||
)
|
||||
|
||||
// 非对称加密、消息认证码、密钥派生与编码(历史数值区间)。
|
||||
const (
|
||||
RSA EncryptType = 20
|
||||
HMAC_SHA256 EncryptType = 21
|
||||
HMAC_SHA512 EncryptType = 22
|
||||
PBKDF2 EncryptType = 23
|
||||
BASE64 EncryptType = 24
|
||||
)
|
||||
|
||||
// 以下为新增算法,从 25 开始追加,保证既有数值稳定。
|
||||
const (
|
||||
// 哈希
|
||||
SHA224 EncryptType = 25
|
||||
SHA384 EncryptType = 26
|
||||
SHA3_224 EncryptType = 27
|
||||
BLAKE2B_256 EncryptType = 28
|
||||
BLAKE2S_256 EncryptType = 29
|
||||
RIPEMD160 EncryptType = 30
|
||||
|
||||
// 对称加密(补齐 OFB/CTR 与 AEAD)
|
||||
DES_OFB EncryptType = 31
|
||||
DES_CTR EncryptType = 32
|
||||
TDES_OFB EncryptType = 33
|
||||
TDES_CTR EncryptType = 34
|
||||
AES_OFB EncryptType = 35
|
||||
AES_CTR EncryptType = 36
|
||||
BLOWFISH_OFB EncryptType = 37
|
||||
BLOWFISH_CTR EncryptType = 38
|
||||
CHACHA20_POLY1305 EncryptType = 39
|
||||
XCHACHA20_POLY1305 EncryptType = 40
|
||||
|
||||
// 消息认证码
|
||||
HMAC_SHA1 EncryptType = 41
|
||||
HMAC_SHA384 EncryptType = 42
|
||||
|
||||
// 密钥派生与口令哈希
|
||||
HKDF EncryptType = 43
|
||||
SCRYPT EncryptType = 44
|
||||
ARGON2ID EncryptType = 45
|
||||
BCRYPT EncryptType = 46
|
||||
|
||||
// 非对称加密(OAEP 为现代默认)
|
||||
RSA_OAEP EncryptType = 47
|
||||
|
||||
// 编码
|
||||
BASE64_URL EncryptType = 48
|
||||
HEX EncryptType = 49
|
||||
)
|
||||
|
||||
// SignType 标识一种数字签名算法。
|
||||
//
|
||||
// 签名与加密的语义不同(签名用私钥、验签用公钥,且不涉及机密性),因此使用
|
||||
// 独立的枚举与独立的 Sign/Verify 入口,而不是塞进 EncryptType。
|
||||
type SignType int
|
||||
|
||||
const (
|
||||
// ED25519 是当前首选的签名算法:实现简单、无参数、签名确定、抗侧信道。
|
||||
ED25519 SignType = iota
|
||||
// ECDSA_P256 是 NIST P-256 曲线上的 ECDSA。
|
||||
ECDSA_P256
|
||||
// ECDSA_P384 是 NIST P-384 曲线上的 ECDSA。
|
||||
ECDSA_P384
|
||||
// ECDSA_P521 是 NIST P-521 曲线上的 ECDSA。
|
||||
ECDSA_P521
|
||||
// RSA_PSS 是带 PSS 填充的 RSA 签名,是现代推荐形式。
|
||||
RSA_PSS
|
||||
// RSA_PKCS1V15 是传统的 PKCS#1 v1.5 签名,仅为兼容旧系统保留。
|
||||
RSA_PKCS1V15
|
||||
)
|
||||
|
||||
var signTypeNames = map[SignType]string{
|
||||
ED25519: "ED25519",
|
||||
ECDSA_P256: "ECDSA-P256",
|
||||
ECDSA_P384: "ECDSA-P384",
|
||||
ECDSA_P521: "ECDSA-P521",
|
||||
RSA_PSS: "RSA-PSS",
|
||||
RSA_PKCS1V15: "RSA-PKCS1v15",
|
||||
}
|
||||
|
||||
// GetEncryptTypeName 获取加密类型的名称
|
||||
func GetEncryptTypeName(encryptType EncryptType) string {
|
||||
return encryptTypeNames[encryptType]
|
||||
// String 返回签名算法名称。
|
||||
func (s SignType) String() string {
|
||||
if name, ok := signTypeNames[s]; ok {
|
||||
return name
|
||||
}
|
||||
return fmt.Sprintf("SignType(%d)", int(s))
|
||||
}
|
||||
|
||||
// GetAllEncryptTypes 获取所有加密类型
|
||||
func GetAllEncryptTypes() []EncryptType {
|
||||
types := make([]EncryptType, 0, len(encryptTypeNames))
|
||||
for t := range encryptTypeNames {
|
||||
// Valid 报告签名算法是否受支持。
|
||||
func (s SignType) Valid() bool {
|
||||
_, ok := signTypeNames[s]
|
||||
return ok
|
||||
}
|
||||
|
||||
// UsesRSA 报告该签名算法是否基于 RSA(密钥格式为 RSA PEM)。
|
||||
func (s SignType) UsesRSA() bool {
|
||||
return s == RSA_PSS || s == RSA_PKCS1V15
|
||||
}
|
||||
|
||||
// GetAllSignTypes 返回全部签名算法,按数值升序。
|
||||
func GetAllSignTypes() []SignType {
|
||||
types := make([]SignType, 0, len(signTypeNames))
|
||||
for t := range signTypeNames {
|
||||
types = append(types, t)
|
||||
}
|
||||
sort.Slice(types, func(i, j int) bool {
|
||||
return int(types[i]) < int(types[j])
|
||||
})
|
||||
sort.Slice(types, func(i, j int) bool { return int(types[i]) < int(types[j]) })
|
||||
return types
|
||||
}
|
||||
|
||||
// Encrypt 加密数据
|
||||
// encryptType: 加密类型
|
||||
// data: 要加密的数据
|
||||
// key: 密钥(某些算法需要)
|
||||
// options: 额外选项(如盐值、迭代次数等)
|
||||
func Encrypt(encryptType EncryptType, data string, key string, options map[string]interface{}) (string, error) {
|
||||
switch encryptType {
|
||||
// 哈希算法
|
||||
case MD5:
|
||||
return md5x.Md5String(data), nil
|
||||
case SHA1:
|
||||
return sha1x.Sha1(data), nil
|
||||
case SHA256:
|
||||
return sha256x.Sha256(data), nil
|
||||
case SHA512:
|
||||
return sha512x.Sha512(data), nil
|
||||
case SHA3_256:
|
||||
return sha3x.Sha3_256(data), nil
|
||||
case SHA3_384:
|
||||
return sha3x.Sha3_384(data), nil
|
||||
case SHA3_512:
|
||||
return sha3x.Sha3_512(data), nil
|
||||
// SecurityLevel 表示算法的安全等级,供管理界面给出风险提示或禁止选择。
|
||||
type SecurityLevel int
|
||||
|
||||
// 对称加密算法
|
||||
case DES_ECB:
|
||||
return desx.EncryptECBHex(data, key)
|
||||
case DES_CBC:
|
||||
return desx.EncryptCBCHex(data, key)
|
||||
case DES_CFB:
|
||||
return desx.EncryptCFBHex(data, key)
|
||||
case TDES_ECB:
|
||||
return tdesx.EncryptECBHex(data, key)
|
||||
case TDES_CBC:
|
||||
return tdesx.EncryptCBCHex(data, key)
|
||||
case TDES_CFB:
|
||||
return tdesx.EncryptCFBHex(data, key)
|
||||
case AES_ECB:
|
||||
return aesx.EncryptECBHex(data, key)
|
||||
case AES_CBC:
|
||||
return aesx.EncryptCBCHex(data, key)
|
||||
case AES_CFB:
|
||||
return aesx.EncryptCFBHex(data, key)
|
||||
case AES_GCM:
|
||||
ciphertext, nonce, err := aesx.EncryptGCM(key, data)
|
||||
const (
|
||||
// SecurityBroken 表示算法已被实际攻破,任何情况下都不应使用。
|
||||
SecurityBroken SecurityLevel = iota
|
||||
// SecurityLegacy 表示算法未被攻破但已不推荐,仅用于与既有系统互操作。
|
||||
SecurityLegacy
|
||||
// SecurityAcceptable 表示算法仍可接受,但存在更好的现代替代。
|
||||
SecurityAcceptable
|
||||
// SecurityRecommended 表示当前推荐使用的算法。
|
||||
SecurityRecommended
|
||||
)
|
||||
|
||||
// String 返回安全等级的中文说明。
|
||||
func (l SecurityLevel) String() string {
|
||||
switch l {
|
||||
case SecurityBroken:
|
||||
return "已攻破"
|
||||
case SecurityLegacy:
|
||||
return "遗留"
|
||||
case SecurityAcceptable:
|
||||
return "可接受"
|
||||
case SecurityRecommended:
|
||||
return "推荐"
|
||||
default:
|
||||
return fmt.Sprintf("SecurityLevel(%d)", int(l))
|
||||
}
|
||||
}
|
||||
|
||||
// typeInfo 描述一种算法的元数据。集中放在一张表里,便于 GetAllEncryptTypes
|
||||
// 与界面直接消费,也避免在多处 switch 里各写一遍。
|
||||
type typeInfo struct {
|
||||
name string
|
||||
level SecurityLevel
|
||||
reversible bool // 是否可逆(能否 Decrypt)
|
||||
needsKey bool // 是否需要密钥
|
||||
aead bool // 是否提供完整性保护(AEAD)
|
||||
note string
|
||||
}
|
||||
|
||||
var typeInfos = map[EncryptType]typeInfo{
|
||||
// ---- 哈希(不可逆、不需要密钥)----
|
||||
MD5: {"MD5", SecurityBroken, false, false, false, "已被实际碰撞攻击攻破,仅可用于非安全的校验和"},
|
||||
SHA1: {"SHA1", SecurityBroken, false, false, false, "已被 SHAttered 碰撞攻击攻破,仅可用于非安全的校验和"},
|
||||
SHA224: {"SHA-224", SecurityAcceptable, false, false, false, "SHA-256 的截断版本,输出 28 字节"},
|
||||
SHA256: {"SHA-256", SecurityRecommended, false, false, false, ""},
|
||||
SHA384: {"SHA-384", SecurityRecommended, false, false, false, "SHA-512 的截断版本,输出 48 字节"},
|
||||
SHA512: {"SHA-512", SecurityRecommended, false, false, false, ""},
|
||||
SHA3_224: {"SHA3-224", SecurityAcceptable, false, false, false, ""},
|
||||
SHA3_256: {"SHA3-256", SecurityRecommended, false, false, false, "与 SHA-256 无继承关系,抗长度扩展攻击"},
|
||||
SHA3_384: {"SHA3-384", SecurityRecommended, false, false, false, ""},
|
||||
SHA3_512: {"SHA3-512", SecurityRecommended, false, false, false, ""},
|
||||
BLAKE2B_256: {"BLAKE2b-256", SecurityRecommended, false, false, false, "速度快于 SHA-2,适合大数据量校验"},
|
||||
BLAKE2S_256: {"BLAKE2s-256", SecurityRecommended, false, false, false, "面向 32 位平台优化的 BLAKE2"},
|
||||
RIPEMD160: {"RIPEMD-160", SecurityLegacy, false, false, false, "仅用于兼容旧系统(如比特币地址)"},
|
||||
|
||||
// ---- 对称加密 ----
|
||||
DES_ECB: {"DES-ECB", SecurityBroken, true, true, false, "56 位密钥可被暴力破解,且 ECB 无语义安全性"},
|
||||
DES_CBC: {"DES-CBC", SecurityBroken, true, true, false, "56 位密钥可被暴力破解"},
|
||||
DES_CFB: {"DES-CFB", SecurityBroken, true, true, false, "56 位密钥可被暴力破解"},
|
||||
DES_OFB: {"DES-OFB", SecurityBroken, true, true, false, "56 位密钥可被暴力破解"},
|
||||
DES_CTR: {"DES-CTR", SecurityBroken, true, true, false, "56 位密钥可被暴力破解"},
|
||||
TDES_ECB: {"3DES-ECB", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险,且 ECB 无语义安全性"},
|
||||
TDES_CBC: {"3DES-CBC", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
TDES_CFB: {"3DES-CFB", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
TDES_OFB: {"3DES-OFB", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
TDES_CTR: {"3DES-CTR", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
BLOWFISH_ECB: {"Blowfish-ECB", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险,且 ECB 无语义安全性"},
|
||||
BLOWFISH_CBC: {"Blowfish-CBC", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
BLOWFISH_CFB: {"Blowfish-CFB", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
BLOWFISH_OFB: {"Blowfish-OFB", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
BLOWFISH_CTR: {"Blowfish-CTR", SecurityLegacy, true, true, false, "64 位分组存在 Sweet32 风险"},
|
||||
AES_ECB: {"AES-ECB", SecurityLegacy, true, true, false, "ECB 无语义安全性,相同明文块产生相同密文块"},
|
||||
AES_CBC: {"AES-CBC", SecurityAcceptable, true, true, false, "无完整性保护;建议改用 AES-GCM"},
|
||||
AES_CFB: {"AES-CFB", SecurityAcceptable, true, true, false, "无完整性保护;建议改用 AES-GCM"},
|
||||
AES_OFB: {"AES-OFB", SecurityAcceptable, true, true, false, "无完整性保护;建议改用 AES-GCM"},
|
||||
AES_CTR: {"AES-CTR", SecurityAcceptable, true, true, false, "无完整性保护;建议改用 AES-GCM"},
|
||||
AES_GCM: {"AES-GCM", SecurityRecommended, true, true, true, "推荐;务必保证 nonce 在同一密钥下不重复"},
|
||||
CHACHA20_POLY1305: {"ChaCha20-Poly1305", SecurityRecommended, true, true, true, "推荐;无 AES 硬件加速时的首选"},
|
||||
XCHACHA20_POLY1305: {"XChaCha20-Poly1305", SecurityRecommended, true, true, true, "推荐;24 字节 nonce 可安全随机生成"},
|
||||
|
||||
// ---- 消息认证码 ----
|
||||
HMAC_SHA1: {"HMAC-SHA1", SecurityLegacy, false, true, true, "HMAC 本身仍安全,但底层 SHA-1 已不推荐"},
|
||||
HMAC_SHA256: {"HMAC-SHA256", SecurityRecommended, false, true, true, ""},
|
||||
HMAC_SHA384: {"HMAC-SHA384", SecurityRecommended, false, true, true, ""},
|
||||
HMAC_SHA512: {"HMAC-SHA512", SecurityRecommended, false, true, true, ""},
|
||||
|
||||
// ---- 密钥派生与口令哈希 ----
|
||||
PBKDF2: {"PBKDF2-HMAC-SHA256", SecurityAcceptable, false, false, false, "迭代次数需按硬件能力调整,建议 ≥ 600000"},
|
||||
HKDF: {"HKDF-SHA256", SecurityRecommended, false, false, false, "用于高熵密钥材料的派生与分离,不适合低熵口令"},
|
||||
SCRYPT: {"scrypt", SecurityRecommended, false, false, false, "内存硬,适合口令存储;Encrypt 走原始密钥派生,需显式提供 salt"},
|
||||
ARGON2ID: {"Argon2id", SecurityRecommended, false, false, false, "当前口令哈希的首选;Encrypt 走 PHC 口令哈希,DeriveKey 走原始密钥派生"},
|
||||
BCRYPT: {"bcrypt", SecurityRecommended, false, false, false, "经过长期考验;内存开销较小"},
|
||||
|
||||
// ---- 非对称 ----
|
||||
RSA: {"RSA-OAEP(SHA-256)", SecurityRecommended, true, true, true, "带 OAEP 填充,支持长明文自动分块"},
|
||||
RSA_OAEP: {"RSA-OAEP(SHA-256)", SecurityRecommended, true, true, true, "与 RSA 等价,保留独立取值便于配置区分"},
|
||||
|
||||
// ---- 编码 ----
|
||||
BASE64: {"Base64", SecurityRecommended, true, false, false, "不是加密,仅编码"},
|
||||
BASE64_URL: {"Base64-URL", SecurityRecommended, true, false, false, "URL 与文件名安全的 Base64"},
|
||||
HEX: {"Hex", SecurityRecommended, true, false, false, "不是加密,仅编码"},
|
||||
}
|
||||
|
||||
// String 返回算法名称;未知取值返回带数值的占位串。
|
||||
func (t EncryptType) String() string {
|
||||
if info, ok := typeInfos[t]; ok {
|
||||
return info.name
|
||||
}
|
||||
return fmt.Sprintf("EncryptType(%d)", int(t))
|
||||
}
|
||||
|
||||
// Valid 报告该算法是否受支持。
|
||||
func (t EncryptType) Valid() bool {
|
||||
_, ok := typeInfos[t]
|
||||
return ok
|
||||
}
|
||||
|
||||
// SecurityLevel 返回算法的安全等级。
|
||||
func (t EncryptType) SecurityLevel() SecurityLevel {
|
||||
if info, ok := typeInfos[t]; ok {
|
||||
return info.level
|
||||
}
|
||||
return SecurityBroken
|
||||
}
|
||||
|
||||
// IsReversible 报告算法是否可逆(即能否调用 Decrypt 还原)。
|
||||
func (t EncryptType) IsReversible() bool {
|
||||
info, ok := typeInfos[t]
|
||||
return ok && info.reversible
|
||||
}
|
||||
|
||||
// NeedsKey 报告算法是否需要密钥。
|
||||
func (t EncryptType) NeedsKey() bool {
|
||||
info, ok := typeInfos[t]
|
||||
return ok && info.needsKey
|
||||
}
|
||||
|
||||
// IsAEAD 报告算法是否提供完整性保护(AEAD 或消息认证码)。
|
||||
func (t EncryptType) IsAEAD() bool {
|
||||
info, ok := typeInfos[t]
|
||||
return ok && info.aead
|
||||
}
|
||||
|
||||
// Note 返回该算法的补充说明(安全提示、使用建议),可能为空。
|
||||
func (t EncryptType) Note() string {
|
||||
return typeInfos[t].note
|
||||
}
|
||||
|
||||
// IsDeprecated 报告算法是否已不推荐使用(含"已攻破"与"遗留"两级)。
|
||||
func (t EncryptType) IsDeprecated() bool {
|
||||
return t.SecurityLevel() <= SecurityLegacy
|
||||
}
|
||||
|
||||
// GetEncryptTypeName 返回算法名称,兼容历史 API。
|
||||
//
|
||||
// 新代码请直接使用 EncryptType.String。
|
||||
func GetEncryptTypeName(encryptType EncryptType) string {
|
||||
return encryptType.String()
|
||||
}
|
||||
|
||||
// GetAllEncryptTypes 返回全部受支持的算法,按数值升序。
|
||||
func GetAllEncryptTypes() []EncryptType {
|
||||
types := make([]EncryptType, 0, len(typeInfos))
|
||||
for t := range typeInfos {
|
||||
types = append(types, t)
|
||||
}
|
||||
sort.Slice(types, func(i, j int) bool { return int(types[i]) < int(types[j]) })
|
||||
return types
|
||||
}
|
||||
|
||||
// 门面层错误。调用方可用 errors.Is 精确判断。
|
||||
var (
|
||||
// ErrUnsupportedType 表示算法取值不在支持范围内。
|
||||
ErrUnsupportedType = errors.New("encryptx: unsupported algorithm")
|
||||
// ErrIrreversible 表示对所给算法调用了 Decrypt,但该算法不可逆。
|
||||
ErrIrreversible = errors.New("encryptx: algorithm is not reversible")
|
||||
// ErrMissingKey 表示算法需要密钥但未提供。
|
||||
ErrMissingKey = errors.New("encryptx: key is required")
|
||||
// ErrMissingOption 表示缺少必需的选项。
|
||||
ErrMissingOption = errors.New("encryptx: required option is missing")
|
||||
// ErrUnknownOption 表示传入了该算法不认识的选项。
|
||||
//
|
||||
// 历史版本会静默忽略拼错或多余的选项,导致「以为设置了参数、实际用的却是
|
||||
// 默认值」这类安全隐患。现在一律报错。
|
||||
ErrUnknownOption = errors.New("encryptx: unknown option")
|
||||
// ErrInvalidOption 表示选项类型不正确。
|
||||
ErrInvalidOption = errors.New("encryptx: invalid option type")
|
||||
// ErrInvalidInput 表示输入数据格式非法(如非法的十六进制密文、长度不足的密文)。
|
||||
ErrInvalidInput = errors.New("encryptx: invalid input")
|
||||
// ErrUnexpectedKey 表示给一个不使用密钥的算法传入了密钥。
|
||||
//
|
||||
// 典型场景:把 BASE64、哈希或口令哈希误当成加密算法,却仍然按加密的方式
|
||||
// 传了密钥。历史版本会静默忽略这个密钥,让调用方以为数据受到了保护。
|
||||
ErrUnexpectedKey = errors.New("encryptx: unexpected key for a keyless algorithm")
|
||||
)
|
||||
|
||||
// Options 承载算法的额外参数。
|
||||
//
|
||||
// 取值务必使用与本包配套的构造函数(如 IntOption、BytesOption)或直接传
|
||||
// int/string/bool/[]byte,以免类型不符。所有取值都会在解析时校验:
|
||||
// 类型不对或键名不认识都会返回错误,不再静默回退到默认值。
|
||||
type Options map[string]any
|
||||
|
||||
// Int 读取一个整数选项。第二个返回值表示该键是否存在。
|
||||
//
|
||||
// 接受 int、int64,以及能精确表示为整数的 float64(便于处理 JSON 解码结果)。
|
||||
func (o Options) Int(key string) (int, bool, error) {
|
||||
raw, ok := o[key]
|
||||
if !ok {
|
||||
return 0, false, nil
|
||||
}
|
||||
switch v := raw.(type) {
|
||||
case int:
|
||||
return v, true, nil
|
||||
case int64:
|
||||
return int(v), true, nil
|
||||
case float64:
|
||||
if v != float64(int(v)) {
|
||||
return 0, false, fmt.Errorf("%w: 选项 %q 的值 %v 不是整数", ErrInvalidOption, key, v)
|
||||
}
|
||||
return int(v), true, nil
|
||||
default:
|
||||
return 0, false, fmt.Errorf("%w: 选项 %q 期望整数, 得到 %T", ErrInvalidOption, key, raw)
|
||||
}
|
||||
}
|
||||
|
||||
// String 读取一个字符串选项。
|
||||
func (o Options) String(key string) (string, bool, error) {
|
||||
raw, ok := o[key]
|
||||
if !ok {
|
||||
return "", false, nil
|
||||
}
|
||||
v, ok := raw.(string)
|
||||
if !ok {
|
||||
return "", false, fmt.Errorf("%w: 选项 %q 期望字符串, 得到 %T", ErrInvalidOption, key, raw)
|
||||
}
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
// Bool 读取一个布尔选项。
|
||||
func (o Options) Bool(key string) (bool, bool, error) {
|
||||
raw, ok := o[key]
|
||||
if !ok {
|
||||
return false, false, nil
|
||||
}
|
||||
v, ok := raw.(bool)
|
||||
if !ok {
|
||||
return false, false, fmt.Errorf("%w: 选项 %q 期望布尔值, 得到 %T", ErrInvalidOption, key, raw)
|
||||
}
|
||||
return v, true, nil
|
||||
}
|
||||
|
||||
// Bytes 读取一个二进制选项,接受 []byte 或十六进制/Base64 字符串。
|
||||
func (o Options) Bytes(key string) ([]byte, bool, error) {
|
||||
raw, ok := o[key]
|
||||
if !ok {
|
||||
return nil, false, nil
|
||||
}
|
||||
switch v := raw.(type) {
|
||||
case []byte:
|
||||
return v, true, nil
|
||||
case string:
|
||||
decoded, err := decodeOptionBytes(key, v)
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, false, err
|
||||
}
|
||||
return ciphertext + ":" + nonce, nil
|
||||
|
||||
// 非对称加密算法
|
||||
case RSA:
|
||||
publicKeyPath, ok := options["publicKeyPath"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("RSA加密需要publicKeyPath选项")
|
||||
}
|
||||
cipherBytes := rsax.RSA_Encrypt([]byte(data), publicKeyPath)
|
||||
return base64.StdEncoding.EncodeToString(cipherBytes), nil
|
||||
|
||||
// 消息认证码
|
||||
case HMAC_SHA256:
|
||||
return hmacx.HMACSHA256(data, key), nil
|
||||
case HMAC_SHA512:
|
||||
return hmacx.HMACSHA512(data, key), nil
|
||||
|
||||
// 密钥派生
|
||||
case PBKDF2:
|
||||
salt, ok := options["salt"].(string)
|
||||
if !ok {
|
||||
// 如果没有提供盐值,生成一个随机盐值
|
||||
saltBytes := make([]byte, 16)
|
||||
if _, err := rand.Read(saltBytes); err != nil {
|
||||
return "", fmt.Errorf("生成随机盐值失败: %v", err)
|
||||
}
|
||||
salt = hex.EncodeToString(saltBytes)
|
||||
}
|
||||
|
||||
iterations, ok := options["iterations"].(int)
|
||||
if !ok {
|
||||
iterations = 10000 // 默认迭代次数
|
||||
}
|
||||
|
||||
keyLength, ok := options["keyLength"].(int)
|
||||
if !ok {
|
||||
keyLength = 32 // 默认密钥长度(32字节)
|
||||
}
|
||||
|
||||
return pbkdf2x.DeriveKeyFromPassword(data, salt, iterations, keyLength)
|
||||
|
||||
// 编码
|
||||
case BASE64:
|
||||
return base64x.StdEncode(data), nil
|
||||
|
||||
return decoded, true, nil
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的加密类型: %d", encryptType)
|
||||
return nil, false, fmt.Errorf("%w: 选项 %q 期望 []byte 或字符串, 得到 %T", ErrInvalidOption, key, raw)
|
||||
}
|
||||
}
|
||||
|
||||
// Decrypt 解密数据
|
||||
// encryptType: 加密类型
|
||||
// data: 要解密的数据
|
||||
// key: 密钥(某些算法需要)
|
||||
// options: 额外选项(如盐值、迭代次数等)
|
||||
func Decrypt(encryptType EncryptType, data string, key string, options map[string]interface{}) (string, error) {
|
||||
switch encryptType {
|
||||
// 哈希算法(不可逆)
|
||||
case MD5, SHA1, SHA256, SHA512, SHA3_256, SHA3_384, SHA3_512:
|
||||
return "", fmt.Errorf("哈希算法不可逆")
|
||||
|
||||
// 对称加密算法
|
||||
case DES_ECB:
|
||||
return desx.DecryptECBHex(data, key)
|
||||
case DES_CBC:
|
||||
return desx.DecryptCBCHex(data, key)
|
||||
case DES_CFB:
|
||||
return desx.DecryptCFBHex(data, key)
|
||||
case TDES_ECB:
|
||||
return tdesx.DecryptECBHex(data, key)
|
||||
case TDES_CBC:
|
||||
return tdesx.DecryptCBCHex(data, key)
|
||||
case TDES_CFB:
|
||||
return tdesx.DecryptCFBHex(data, key)
|
||||
case AES_ECB:
|
||||
return aesx.DecryptECBHex(data, key)
|
||||
case AES_CBC:
|
||||
return aesx.DecryptCBCHex(data, key)
|
||||
case AES_CFB:
|
||||
return aesx.DecryptCFBHex(data, key)
|
||||
case AES_GCM:
|
||||
parts := strings.Split(data, ":")
|
||||
if len(parts) != 2 {
|
||||
return "", fmt.Errorf("AES-GCM解密需要格式为'密文:nonce'")
|
||||
}
|
||||
return aesx.DecryptGCM(key, parts[0], parts[1])
|
||||
|
||||
// 非对称加密算法
|
||||
case RSA:
|
||||
privateKeyPath, ok := options["privateKeyPath"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("RSA解密需要privateKeyPath选项")
|
||||
}
|
||||
cipherBytes, err := base64.StdEncoding.DecodeString(data)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Base64解码失败: %v", err)
|
||||
}
|
||||
plainBytes := rsax.RSA_Decrypt(cipherBytes, privateKeyPath)
|
||||
return string(plainBytes), nil
|
||||
|
||||
// 消息认证码(不可逆)
|
||||
case HMAC_SHA256, HMAC_SHA512:
|
||||
return "", fmt.Errorf("消息认证码不可逆")
|
||||
|
||||
// 密钥派生(不可逆)
|
||||
case PBKDF2:
|
||||
return "", fmt.Errorf("密钥派生不可逆")
|
||||
|
||||
// 编码
|
||||
case BASE64:
|
||||
return base64x.StdDecode(data)
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的加密类型: %d", encryptType)
|
||||
// rejectUnknown 校验没有传入未声明的选项,避免参数被静默忽略。
|
||||
func (o Options) rejectUnknown(allowed ...string) error {
|
||||
if len(o) == 0 {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// GenerateKey 生成密钥
|
||||
// encryptType: 加密类型
|
||||
// options: 额外选项
|
||||
func GenerateKey(encryptType EncryptType, options map[string]interface{}) (string, error) {
|
||||
switch encryptType {
|
||||
// 哈希算法(不需要密钥)
|
||||
case MD5, SHA1, SHA256, SHA512, SHA3_256, SHA3_384, SHA3_512:
|
||||
return "", fmt.Errorf("哈希算法不需要密钥")
|
||||
|
||||
// 对称加密算法
|
||||
case DES_ECB, DES_CBC, DES_CFB:
|
||||
// DES密钥长度为8字节
|
||||
keyBytes := make([]byte, 8)
|
||||
if _, err := rand.Read(keyBytes); err != nil {
|
||||
return "", fmt.Errorf("生成DES密钥失败: %v", err)
|
||||
}
|
||||
return hex.EncodeToString(keyBytes), nil
|
||||
|
||||
case TDES_ECB, TDES_CBC, TDES_CFB:
|
||||
// 3DES密钥长度为24字节
|
||||
keyBytes := make([]byte, 24)
|
||||
if _, err := rand.Read(keyBytes); err != nil {
|
||||
return "", fmt.Errorf("生成3DES密钥失败: %v", err)
|
||||
}
|
||||
return hex.EncodeToString(keyBytes), nil
|
||||
|
||||
case AES_ECB, AES_CBC, AES_CFB, AES_GCM:
|
||||
// AES密钥长度为16、24或32字节
|
||||
keyLength, ok := options["keyLength"].(int)
|
||||
if !ok {
|
||||
keyLength = 32 // 默认使用AES-256
|
||||
}
|
||||
if keyLength != 16 && keyLength != 24 && keyLength != 32 {
|
||||
return "", fmt.Errorf("AES密钥长度必须为16、24或32字节")
|
||||
}
|
||||
keyBytes := make([]byte, keyLength)
|
||||
if _, err := rand.Read(keyBytes); err != nil {
|
||||
return "", fmt.Errorf("生成AES密钥失败: %v", err)
|
||||
}
|
||||
return hex.EncodeToString(keyBytes), nil
|
||||
|
||||
case BLOWFISH_ECB, BLOWFISH_CBC, BLOWFISH_CFB:
|
||||
// Blowfish密钥长度可以是4-56字节
|
||||
keyLength, ok := options["keyLength"].(int)
|
||||
if !ok {
|
||||
keyLength = 16 // 默认使用16字节
|
||||
}
|
||||
if keyLength < 4 || keyLength > 56 {
|
||||
return "", fmt.Errorf("Blowfish密钥长度必须为4-56字节")
|
||||
}
|
||||
keyBytes := make([]byte, keyLength)
|
||||
if _, err := rand.Read(keyBytes); err != nil {
|
||||
return "", fmt.Errorf("生成Blowfish密钥失败: %v", err)
|
||||
}
|
||||
return hex.EncodeToString(keyBytes), nil
|
||||
|
||||
// 非对称加密算法
|
||||
case RSA:
|
||||
bits, ok := options["bits"].(int)
|
||||
if !ok {
|
||||
bits = 2048 // 默认使用2048位
|
||||
}
|
||||
rsax.GenerateRSAKey(bits)
|
||||
return "RSA密钥已生成并保存到文件", nil
|
||||
|
||||
// 消息认证码
|
||||
case HMAC_SHA256, HMAC_SHA512:
|
||||
// HMAC密钥长度可以是任意值
|
||||
keyLength, ok := options["keyLength"].(int)
|
||||
if !ok {
|
||||
keyLength = 32 // 默认使用32字节
|
||||
}
|
||||
keyBytes := make([]byte, keyLength)
|
||||
if _, err := rand.Read(keyBytes); err != nil {
|
||||
return "", fmt.Errorf("生成HMAC密钥失败: %v", err)
|
||||
}
|
||||
return hex.EncodeToString(keyBytes), nil
|
||||
|
||||
// 密钥派生
|
||||
case PBKDF2:
|
||||
// PBKDF2需要盐值
|
||||
saltLength, ok := options["saltLength"].(int)
|
||||
if !ok {
|
||||
saltLength = 16 // 默认使用16字节盐值
|
||||
}
|
||||
return utilsx.GenerateRandomHexString(saltLength)
|
||||
|
||||
// 编码
|
||||
case BASE64:
|
||||
return "", fmt.Errorf("Base64编码不需要密钥")
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("不支持的加密类型: %d", encryptType)
|
||||
known := make(map[string]struct{}, len(allowed))
|
||||
for _, k := range allowed {
|
||||
known[k] = struct{}{}
|
||||
}
|
||||
var unknown []string
|
||||
for k := range o {
|
||||
if _, ok := known[k]; !ok {
|
||||
unknown = append(unknown, k)
|
||||
}
|
||||
}
|
||||
if len(unknown) > 0 {
|
||||
sort.Strings(unknown)
|
||||
return fmt.Errorf("%w: %v (该算法接受的选项: %v)", ErrUnknownOption, unknown, allowed)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user