// 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 ( "errors" "fmt" "sort" ) // EncryptType 标识一种密码学算法。 // // 枚举值显式赋值而非常量递增(iota),因为历史版本用的是连续 iota:一旦在中间 // 插入新算法,所有后续算法的数值都会平移,导致已经落库的配置指向错误的算法。 // 这里保留原有数值不变,新算法一律追加在末尾。 type EncryptType int // 哈希算法。 const ( MD5 EncryptType = 0 SHA1 EncryptType = 1 SHA256 EncryptType = 2 SHA512 EncryptType = 3 SHA3_256 EncryptType = 4 SHA3_384 EncryptType = 5 SHA3_512 EncryptType = 6 ) // 对称加密算法(历史数值区间)。 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", } // String 返回签名算法名称。 func (s SignType) String() string { if name, ok := signTypeNames[s]; ok { return name } return fmt.Sprintf("SignType(%d)", int(s)) } // 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]) }) return types } // SecurityLevel 表示算法的安全等级,供管理界面给出风险提示或禁止选择。 type SecurityLevel int 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 nil, false, err } return decoded, true, nil default: return nil, false, fmt.Errorf("%w: 选项 %q 期望 []byte 或字符串, 得到 %T", ErrInvalidOption, key, raw) } } // rejectUnknown 校验没有传入未声明的选项,避免参数被静默忽略。 func (o Options) rejectUnknown(allowed ...string) error { if len(o) == 0 { return nil } 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 }