using Microsoft.Extensions.Options;
using Org.BouncyCastle.Utilities.Encoders;
using SharpAbp.Abp.Crypto.SM2;
using SharpAbp.Abp.Crypto.SM3;
using SharpAbp.Abp.Crypto.SM4;
using System.Security.Cryptography;
using System.Text;
using static Org.BouncyCastle.Crypto.Engines.SM2Engine;

namespace YourCompany.GmCrypto;

/// <summary>
/// 国密 SM2 / SM3 / SM4 静态帮助类(基于 SharpAbp.Abp.Crypto)
/// </summary>
public static class SmHelper
{
    private static readonly ISm2EncryptionService Sm2 = new Sm2EncryptionService(
        Options.Create(new AbpSm2EncryptionOptions
        {
            DefaultCurve = Sm2EncryptionNames.CurveSm2p256v1
        }));

    private static readonly ISm3EncryptionService Sm3 = new Sm3EncryptionService();

    private static readonly ISm4EncryptionService Sm4 = new Sm4EncryptionService(
        Options.Create(new AbpSm4EncryptionOptions
        {
            DefaultMode = Sm4EncryptionNames.ModeCBC,
            DefaultPadding = Sm4EncryptionNames.PKCS7Padding,
            DefaultIv = Encoding.UTF8.GetBytes("1234567890123456") // 16 字节
        }));

    #region SM2

    /// <summary>生成 SM2 密钥对(十六进制)</summary>
    public static Sm2KeyPairResult GenerateSm2KeyPair()
    {
        var keyPair = Sm2.GenerateSm2KeyPair();
        return new Sm2KeyPairResult
        {
            PublicKeyHex = keyPair.ExportPublicKey(),
            PrivateKeyHex = keyPair.ExportPrivateKey()
        };
    }

    /// <summary>SM2 公钥加密,返回十六进制密文</summary>
    public static string Sm2Encrypt(string plainText, string publicKeyHex)
        => Sm2.Encrypt(publicKeyHex, plainText, Encoding.UTF8, mode: Mode.C1C2C3);

    /// <summary>SM2 私钥解密</summary>
    public static string Sm2Decrypt(string cipherTextHex, string privateKeyHex)
        => Sm2.Decrypt(privateKeyHex, cipherTextHex, Encoding.UTF8, mode: Mode.C1C2C3);

    /// <summary>SM2 私钥签名,返回十六进制签名</summary>
    public static string Sm2Sign(string plainText, string privateKeyHex)
        => Sm2.Sign(privateKeyHex, plainText, Encoding.UTF8);

    /// <summary>SM2 公钥验签</summary>
    public static bool Sm2Verify(string plainText, string signatureHex, string publicKeyHex)
        => Sm2.VerifySign(publicKeyHex, plainText, signatureHex, Encoding.UTF8);

    /// <summary>C1C2C3 转 C1C3C2(对接 Java/前端时可能需要)</summary>
    public static byte[] Sm2C123ToC132(byte[] c1c2c3)
        => Sm2.C123ToC132(c1c2c3);

    /// <summary>C1C3C2 转 C1C2C3</summary>
    public static byte[] Sm2C132ToC123(byte[] c1c3c2)
        => Sm2.C132ToC123(c1c3c2);

    #endregion

    #region SM3

    /// <summary>SM3 哈希,返回十六进制</summary>
    public static string Sm3Hash(string plainText)
        => Sm3.GetHash(plainText, Encoding.UTF8);

    /// <summary>SM3 哈希(字节数组)</summary>
    public static byte[] Sm3Hash(byte[] plainBytes)
        => Sm3.GetHash(plainBytes);

    #endregion

    #region SM4

    /// <summary>生成 SM4 密钥(16 字节,32 位十六进制)</summary>
    public static string GenerateSm4KeyHex()
        => Hex.ToHexString(RandomNumberGenerator.GetBytes(16)).ToLowerInvariant();

    /// <summary>生成 SM4 IV(16 字节,32 位十六进制)</summary>
    public static string GenerateSm4IvHex()
        => Hex.ToHexString(RandomNumberGenerator.GetBytes(16)).ToLowerInvariant();

    /// <summary>SM4 加密(CBC + PKCS7),返回十六进制密文</summary>
    public static string Sm4Encrypt(string plainText, string keyHex, string? ivHex = null)
    {
        ivHex ??= GenerateSm4IvHex();
        return Sm4.Encrypt(
            plainText,
            keyHex,
            ivHex,
            Sm4EncryptionNames.ModeCBC,
            Sm4EncryptionNames.PKCS7Padding,
            Encoding.UTF8);
    }

    /// <summary>SM4 解密(CBC + PKCS7)</summary>
    public static string Sm4Decrypt(string cipherTextHex, string keyHex, string ivHex)
        => Sm4.Decrypt(
            cipherTextHex,
            keyHex,
            ivHex,
            Sm4EncryptionNames.ModeCBC,
            Sm4EncryptionNames.PKCS7Padding,
            Encoding.UTF8);

    #endregion
}

/// <summary>SM2 密钥对</summary>
public sealed class Sm2KeyPairResult
{
    public required string PublicKeyHex { get; init; }
    public required string PrivateKeyHex { get; init; }
}

使用示例

using YourCompany.GmCrypto;

// ============================================================
// SM2:非对称加密(公钥加密 / 私钥解密)+ 数字签名 / 验签
// 适用:密钥交换、小数据加密、身份认证、防篡改
// ============================================================

// 1. 生成一对 SM2 密钥(公钥给别人加密/验签,私钥自己保管)
var keys = SmHelper.GenerateSm2KeyPair();
// keys.PublicKeyHex  → 公钥(可公开)
// keys.PrivateKeyHex → 私钥(务必保密)

// 2. 用【公钥】加密明文(只有对应私钥能解开)
var cipher = SmHelper.Sm2Encrypt("hello", keys.PublicKeyHex);
// cipher 是十六进制密文字符串

// 3. 用【私钥】解密密文,还原明文
var plain = SmHelper.Sm2Decrypt(cipher, keys.PrivateKeyHex);
// plain 应等于 "hello"

// 4. 用【私钥】对数据签名(证明“是我签的、没被改过”)
var sign = SmHelper.Sm2Sign("hello", keys.PrivateKeyHex);
// sign 是十六进制签名字符串

// 5. 用【公钥】验签(确认签名是否有效、数据是否被篡改)
var ok = SmHelper.Sm2Verify("hello", sign, keys.PublicKeyHex);
// ok == true 表示验签通过


// ============================================================
// SM3:哈希(摘要),不可逆
// 适用:密码摘要、数据完整性校验、配合 SM2 签名前的预处理等
// ============================================================

// 对字符串做 SM3 哈希,得到固定长度的十六进制摘要
var hash = SmHelper.Sm3Hash("hello");
// 同样输入永远得到同样输出;无法从 hash 反推原文


// ============================================================
// SM4:对称加密(同一个密钥加解密)
// 适用:大批量数据加密、本地存储加密、报文加密等
// 模式:CBC + PKCS7(类库内默认配置)
// ============================================================

// 1. 生成 16 字节(128 位)密钥,32 位十六进制
var key = SmHelper.GenerateSm4KeyHex();

// 2. 生成 16 字节 IV(初始化向量),CBC 模式必须
var iv = SmHelper.GenerateSm4IvHex();

// 3. 用 key + iv 加密明文
var sm4Cipher = SmHelper.Sm4Encrypt("hello", key, iv);
// 若 iv 传 null,会自动生成;解密时必须用【同一个 iv】

// 4. 用【相同 key + iv】解密密文
var sm4Plain = SmHelper.Sm4Decrypt(sm4Cipher, key, iv);
// sm4Plain 应等于 "hello"


// ============================================================
// 典型组合场景(注释说明,按需选用)
// ============================================================

// 场景 A:传输敏感小数据
//   → SM2 加密(对方公钥加密,己方私钥解密)

// 场景 B:接口防篡改
//   → 发送方 Sm2Sign,接收方 Sm2Verify

// 场景 C:存密码 / 做指纹
//   → Sm3Hash(不要明文存密码)

// 场景 D:加密较长内容或文件内容
//   → 用 SM4;SM2 只用来加密 SM4 的 key(混合加密,需自行封装)

// 场景 E:与 Java / 前端对接 SM2 密文格式不一致时
//   → 可能需要 C1C2C3 ↔ C1C3C2 转换:
//   // var converted = SmHelper.Sm2C123ToC132(cipherBytes);

 

对应关系		
原来	            国密替代	  用途
MD5	            SM3	      数据摘要、完整性校验、指纹
SHA-1 / SHA-256	SM3	      同上(国密场景优先 SM3)
AES / DES	    SM4	      对称加密
RSA	            SM2	      非对称加密、签名

  

posted on 2026-06-18 09:46  小熊吉米  阅读(54)  评论(0)    收藏  举报