登录
原创

身份证四要素校验(国密版)(SM2+SM4国密加密版)- 聚合数据API调用C#示例

发布于 2026-05-07 阅读 61
  • 后端
  • C#
原创
使用 C# 调用聚合数据身份证四要素校验(国密版):id_card_four_factors/sm,SM2 封装 sm4secret、SM4-CBC 加密四要素、HMAC-SM3 请求签名。业务字段为姓名、身份证号、有效期起止(yyyyMMdd)。
  • 请求地址: https://apis.juhe.cn/id_card_four_factors/sm
  • 加密: SM2 公钥加密「16 字节 SM4 密钥 + 16 字节 IV」拼接体 → sm4secret(Base64);四要素为 SM4-CBC 密文(Base64);HMAC-SM3 签名
  • 传输: HTTP POST,Content-Type: application/x-www-form-urlencoded

四要素: 姓名、身份证号、有效期开始、有效期结束。日期为 yyyyMMdd;长期有效时结束日期为 00000000。

说明: 企业类接口,需资质;SM2 公钥向商务获取。OpenId 用于 HMAC,与参数 key(AppKey)不同。

流程摘要

  1. 随机 16 字节 SM4 密钥 + 16 字节 IV,拼接为 byte[],SM2 公钥加密后 Base64 → sm4secret(密文 C1C2C3 经 ASN.1/DER 再 Base64)
  2. 四要素明文以 SM4-CBC(PKCS5),UTF-8,分别加密后 Base64
  3. HMAC-SM3:消息 = idcard+realname+start_date+end_date+key,密钥 = OpenId,结果 Base64 → signature
  4. POST:key、sm4secret、四个加密字段、signature

成功时 error_code=0;result.res 为 1 一致、2 不一致。result.signature 为服务端对返回数据的签名,与请求中 signature 不同。

NuGet 包:BouncyCastle.Cryptography 版本 2.6.2
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Macs;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Paddings;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Crypto.Modes;

public class JuheIdCardSm
{
    private const string ApiUrl = "https://apis.juhe.cn/id_card_four_factors/sm";
    private const string ApiKey = "92d***********************bb";
    private const string OpenId = "JHc***********************46";
    public static string VerifyIdCardFourFactors(string realName, string idCard,
        string startDate, string endDate, string sm2PublicKeyPath)
    {
        try
        {
            Console.WriteLine("=== 身份证四要素核验(国密版)===\n");

            byte[] sm4Key = GenerateRandomBytes(16);
            byte[] sm4Iv = GenerateRandomBytes(16);
            Console.WriteLine("1. 生成SM4密钥与IV:");
            Console.WriteLine($"   SM4密钥(Hex): {Hex.ToHexString(sm4Key)}");
            Console.WriteLine($"   SM4 IV(Hex):  {Hex.ToHexString(sm4Iv)}");

            Console.WriteLine($"\n2. 读取SM2公钥: {sm2PublicKeyPath}");
            AsymmetricKeyParameter sm2PublicKey = LoadSm2PublicKeyFromPem(sm2PublicKeyPath);
            Console.WriteLine("   ✓ 公钥加载成功");

            Console.WriteLine("\n3. SM2加密密钥IV生成sm4secret:");
            byte[] keyIv = CombineBytes(sm4Key, sm4Iv);
            string sm4Secret = Sm2Encrypt(sm2PublicKey, keyIv, useDer: true);
            Console.WriteLine("   ✓ 完成");

            Console.WriteLine("\n4. SM4-CBC加密业务字段:");
            string encRealName = Sm4CbcEncrypt(realName, sm4Key, sm4Iv);
            string encIdCard = Sm4CbcEncrypt(idCard, sm4Key, sm4Iv);
            string encStart = Sm4CbcEncrypt(startDate, sm4Key, sm4Iv);
            string encEnd = Sm4CbcEncrypt(endDate, sm4Key, sm4Iv);
            Console.WriteLine("   ✓ 加密完成");

            Console.WriteLine("\n5. 生成HMAC-SM3签名:");
            string signature = HmacSm3Sign(OpenId, idCard, realName, startDate, endDate, ApiKey);
            Console.WriteLine("   ✓ 签名完成");

            Console.WriteLine("\n6. 发送请求...");
            var parameters = new Dictionary<string, string>
            {
                { "key", ApiKey },
                { "sm4secret", sm4Secret },
                { "realname", encRealName },
                { "idcard", encIdCard },
                { "start_date", encStart },
                { "end_date", encEnd },
                { "signature", signature }
            };

            string response = SendPostForm(ApiUrl, parameters);
            Console.WriteLine("\n7. 接口响应:");
            Console.WriteLine(response);
            return response;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ 调用失败: {ex.Message}");
            Console.WriteLine(ex.ToString());
            return null;
        }
    }

    #region SM2 加密
    private static string Sm2Encrypt(AsymmetricKeyParameter publicKey, byte[] data, bool useDer)
    {
        SM2Engine engine = new SM2Engine(SM2Engine.Mode.C1C2C3);
        engine.Init(true, new ParametersWithRandom(publicKey, new SecureRandom()));
        byte[] cipherRaw = engine.ProcessBlock(data, 0, data.Length);
        byte[] cipherFinal = useDer ? ConvertToDer(cipherRaw) : cipherRaw;
        return Convert.ToBase64String(cipherFinal);
    }

    private static byte[] ConvertToDer(byte[] cipher)
    {
        if (cipher[0] != 0x04)
            throw new ArgumentException("SM2密文必须以0x04开头");

        byte[] x = new byte[32];
        byte[] y = new byte[32];
        byte[] c3 = new byte[32];
        Array.Copy(cipher, 1, x, 0, 32);
        Array.Copy(cipher, 33, y, 0, 32);

        int c2Len = cipher.Length - 97;
        byte[] c2 = new byte[c2Len];
        Array.Copy(cipher, 65, c2, 0, c2Len);
        Array.Copy(cipher, 65 + c2Len, c3, 0, 32);

        Asn1EncodableVector v = new Asn1EncodableVector();
        v.Add(new DerInteger(new BigInteger(1, x)));
        v.Add(new DerInteger(new BigInteger(1, y)));
        v.Add(new DerOctetString(c3));
        v.Add(new DerOctetString(c2));

        return new DerSequence(v).GetEncoded(Asn1Encodable.Der);
    }

    /// <summary>
    /// 【标准兼容版】加载X.509/PKCS#8格式SM2公钥(解决Invalid point encoding 48)
    /// </summary>
    private static AsymmetricKeyParameter LoadSm2PublicKeyFromPem(string path)
    {
        try
        {
            // 读取PEM文件内容
            string pem = File.ReadAllText(path)
                .Replace("-----BEGIN PUBLIC KEY-----", "")
                .Replace("-----END PUBLIC KEY-----", "")
                .Replace("\r", "")
                .Replace("\n", "")
                .Trim();

            // Base64解码公钥字节
            byte[] publicKeyBytes = Convert.FromBase64String(pem);

            // 解析X.509标准公钥(兼容所有标准SM2公钥)
            AsymmetricKeyParameter publicKey = PublicKeyFactory.CreateKey(publicKeyBytes);
            return publicKey;
        }
        catch (Exception ex)
        {
            throw new Exception($"SM2公钥加载失败:{ex.Message}", ex);
        }
    }
    #endregion

    #region SM4-CBC
    private static string Sm4CbcEncrypt(string plainText, byte[] key, byte[] iv)
    {
        byte[] data = Encoding.UTF8.GetBytes(plainText);
        var cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(new SM4Engine()), new Pkcs7Padding());
        cipher.Init(true, new ParametersWithIV(new KeyParameter(key), iv));

        byte[] enc = new byte[cipher.GetOutputSize(data.Length)];
        int len = cipher.ProcessBytes(data, 0, data.Length, enc, 0);
        len += cipher.DoFinal(enc, len);

        byte[] res = new byte[len];
        Buffer.BlockCopy(enc, 0, res, 0, len);
        return Convert.ToBase64String(res);
    }
    #endregion

    #region HMAC-SM3
    private static string HmacSm3Sign(string openId, string idCard, string realName,
        string startDate, string endDate, string apiKey)
    {
        string msg = idCard + realName + startDate + endDate + apiKey;
        byte[] msgBytes = Encoding.UTF8.GetBytes(msg);
        byte[] keyBytes = Encoding.UTF8.GetBytes(openId);

        HMac hmac = new HMac(new SM3Digest());
        hmac.Init(new KeyParameter(keyBytes));
        hmac.BlockUpdate(msgBytes, 0, msgBytes.Length);

        byte[] sign = new byte[hmac.GetMacSize()];
        hmac.DoFinal(sign, 0);
        return Convert.ToBase64String(sign);
    }
    #endregion

    #region 工具
    private static byte[] GenerateRandomBytes(int length)
    {
        byte[] bytes = new byte[length];
        new SecureRandom().NextBytes(bytes);
        return bytes;
    }

    private static byte[] CombineBytes(byte[] a, byte[] b)
    {
        byte[] res = new byte[a.Length + b.Length];
        Buffer.BlockCopy(a, 0, res, 0, a.Length);
        Buffer.BlockCopy(b, 0, res, a.Length, b.Length);
        return res;
    }

    private static string SendPostForm(string url, Dictionary<string, string> parameters)
    {
        using (WebClient client = new WebClient())
        {
            client.Encoding = Encoding.UTF8;
            var data = new System.Collections.Specialized.NameValueCollection();
            foreach (var p in parameters) data.Add(p.Key, p.Value);
            byte[] resp = client.UploadValues(url, "POST", data);
            return Encoding.UTF8.GetString(resp);
        }
    }
    #endregion

    public static void Main()
    {
        try
        {
            string publicKeyPath = @"C:\publickey550.pem";
            string realName = "项**";
            string idCard = "210*********X";
            string startDate = "20170606";
            string endDate = "20270606";

            VerifyIdCardFourFactors(realName, idCard, startDate, endDate, publicKeyPath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"❌ 失败: {ex.Message}");
        }
        Console.ReadLine();
    }
}

评论区

king
5粉丝

励志做一条安静的咸鱼,从此走上人生巅峰。

1

0

3

举报