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

C# 使用国密SM4加密解密

首先需第三方Nuget包:Portable.BouncyCastle (源码来自http://www.bouncycastle.org/csharp/),支持.NET 4,.NET Standard 2.0

目录

目录

使用BouncyCastle指定填充方案

零填充(Zero Padding)

PKCS7填充(PKCS7 Padding)

示例(SM4 CBC 模式加密(使用 Zero Padding))

代码解析


使用BouncyCastle指定填充方案

在BouncyCastle中,可以通过选择不同的PaddedBufferedBlockCipher实现来指定填充方案。这里我们将展示如何使用零填充(Zero Padding)和PKCS7填充(PKCS7 Padding)。

零填充(Zero Padding)

对于零填充,您需要自定义填充处理,因为BouncyCastle不直接提供零填充实现。

PKCS7填充(PKCS7 Padding)

如果你想使用PKCS7填充,可以使用BouncyCastle中的PaddedBufferedBlockCipher类直接指定Pkcs7Padding

示例(SM4 CBC 模式加密(使用 Zero Padding))

采用国密SM4 CBC 模式加密(使用 Zero Padding)

SM4HttpUtilsV2.cs

using System;
using System.Collections.Generic;
using System.Security;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json;
using System.Net.Http;
using System.Threading.Tasks;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Paddings;public class SM4HttpUtilsV2
{public static Dictionary<string, string> CreateCommonParam(string appKey, string appSecret, string codeData, string iv){// 时间戳long timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();// 签名 appKey + apiAppInfo.getAppSecret() + encryptString + timestampConsole.WriteLine($"签名: {appKey}{appSecret}{codeData}{timestamp}{iv}");Console.WriteLine($"appKey={appKey}");Console.WriteLine($"appSecret={appSecret}");Console.WriteLine($"encryptStr={codeData}");Console.WriteLine($"timestamp={timestamp}");Console.WriteLine($"iv={iv}");// 使用 MD5 生成签名string sig = CalculateMD5Hash(appKey + appSecret + codeData + timestamp + iv);Console.WriteLine($"签名值: {sig}");var requestParam = new Dictionary<string, string>{{ "timestamp", timestamp.ToString() },{ "sign", sig },{ "iv", iv }};return requestParam;}private static string CalculateMD5Hash(string input){using (var md5 = MD5.Create()){byte[] inputBytes = Encoding.UTF8.GetBytes(input);byte[] hashBytes = md5.ComputeHash(inputBytes);StringBuilder sb = new StringBuilder();for (int i = 0; i < hashBytes.Length; i++){sb.Append(hashBytes[i].ToString("x2"));}return sb.ToString();}}/// <summary>/// Post请求数据/// </summary>/// <param name="url"></param>/// <param name="appKey"></param>/// <param name="appSecret"></param>/// <param name="parameters"></param>/// <returns></returns>public static async Task<string> PostJsonAsync(string url, string appKey, string appSecret, Dictionary<string, object> obj){string requestBody = JsonConvert.SerializeObject(obj);Console.WriteLine($"原始数据: {requestBody}");// 生成随机的 IV(初始化向量)byte[] ivBytes = new byte[16];using (var rng = RandomNumberGenerator.Create()){rng.GetBytes(ivBytes);}string ivBase64 = Convert.ToBase64String(ivBytes);// 使用 SM4 进行加密(需要实现 SM4 加密算法)string codeData = EncryptSM4CBC(appSecret, requestBody, ivBytes);Console.WriteLine($"原始: {codeData}");var requestParam = new Dictionary<string, object>{{ "appKey", appKey },{ "version", "1" },{ "encryptStr", codeData }};foreach (var param in CreateCommonParam(appKey, appSecret, codeData, ivBase64)){requestParam.Add(param.Key, param.Value);}Console.WriteLine($"请求数据: {JsonConvert.SerializeObject(requestParam)}");using (var httpClient = new HttpClient()){var content = new StringContent(JsonConvert.SerializeObject(requestParam), Encoding.UTF8, "application/json");HttpResponseMessage response = await httpClient.PostAsync(url, content);string result = await response.Content.ReadAsStringAsync();Console.WriteLine(result);var ret = JsonConvert.DeserializeObject<Dictionary<string, object>>(result);if (ret.ContainsKey("data") && !string.IsNullOrEmpty(ret["data"]?.ToString())){string data = ret["data"].ToString();return DecryptSM4CBC(appSecret, data, ivBytes);}else{return result;}}}/// <summary>/// SM4 加密 CBC 模式/// </summary>/// <param name="key"></param>/// <param name="data"></param>/// <param name="iv"></param>/// <returns></returns>public static string EncryptSM4CBC(string key, string data, byte[] iv){byte[] keyBytes = Encoding.UTF8.GetBytes(key);byte[] dataBytes = Encoding.UTF8.GetBytes(data);// 应用 Zero PaddingdataBytes = ApplyZeroPadding(dataBytes, 16); // SM4 的块大小是 16 字节SM4Engine engine = new SM4Engine();CbcBlockCipher cipher = new CbcBlockCipher(engine);PaddedBufferedBlockCipher bufferedCipher = new PaddedBufferedBlockCipher(cipher);KeyParameter keyParam = new KeyParameter(keyBytes);ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, iv);bufferedCipher.Init(true, keyParamWithIV);byte[] outputBytes = new byte[bufferedCipher.GetOutputSize(dataBytes.Length)];int length = bufferedCipher.ProcessBytes(dataBytes, 0, dataBytes.Length, outputBytes, 0);length += bufferedCipher.DoFinal(outputBytes, length);// 直接返回加密后的Base64字符串return Convert.ToBase64String(outputBytes, 0, length);}/// <summary>/// 自定义 Zero Padding 方法/// </summary>/// <param name="input"></param>/// <param name="blockSize"></param>/// <returns></returns>public static byte[] ApplyZeroPadding(byte[] input, int blockSize){int paddingLength = blockSize - (input.Length % blockSize);if (paddingLength == blockSize){return input; // 无需填充}byte[] paddedData = new byte[input.Length + paddingLength];Array.Copy(input, paddedData, input.Length);return paddedData;}/// <summary>/// SM4 解密 CBC 模式/// </summary>/// <param name="key"></param>/// <param name="encryptedData"></param>/// <param name="iv"></param>/// <returns></returns>public static string DecryptSM4CBC(string key, string encryptedData, byte[] iv){byte[] keyBytes = Encoding.UTF8.GetBytes(key);byte[] encryptedBytes = Convert.FromBase64String(encryptedData);// 应用 Zero PaddingencryptedBytes = ApplyZeroPadding(encryptedBytes, 16); // SM4 的块大小是 16 字节SM4Engine engine = new SM4Engine();CbcBlockCipher cipher = new CbcBlockCipher(engine);BufferedBlockCipher bufferedCipher = new BufferedBlockCipher(cipher);KeyParameter keyParam = new KeyParameter(keyBytes);ParametersWithIV keyParamWithIV = new ParametersWithIV(keyParam, iv);bufferedCipher.Init(false, keyParamWithIV);byte[] outputBytes = new byte[bufferedCipher.GetOutputSize(encryptedBytes.Length)];int length = bufferedCipher.ProcessBytes(encryptedBytes, 0, encryptedBytes.Length, outputBytes, 0);try{length += bufferedCipher.DoFinal(outputBytes, length);}catch (InvalidCipherTextException e){throw new Exception("解密失败:密文损坏或密钥/IV不正确", e);}return Encoding.UTF8.GetString(outputBytes, 0, length);}
}

当您遇到 Org.BouncyCastle.Crypto.InvalidCipherTextException: "pad block corrupted" 错误时,这通常意味着解密过程中,填充的块(pad block)不符合预期的格式或长度。

需要添加如下代码,指定填充方案

/// <summary>/// 自定义 Zero Padding 方法/// </summary>/// <param name="input"></param>/// <param name="blockSize"></param>/// <returns></returns>public static byte[] ApplyZeroPadding(byte[] input, int blockSize){int paddingLength = blockSize - (input.Length % blockSize);if (paddingLength == blockSize){return input; // 无需填充}byte[] paddedData = new byte[input.Length + paddingLength];Array.Copy(input, paddedData, input.Length);return paddedData;}

代码解析

  1. 自定义 Zero Padding:

    • ApplyZeroPadding方法用于将数据填充到符合块大小(16字节)的倍数。
    • 如果数据已经是块大小的倍数,则不进行填充。
  2. 加密过程:

    • 创建一个SM4Engine实例,并将其包装在CbcBlockCipher中以使用CBC模式。
    • 使用BufferedBlockCipher来处理加密操作。
    • 使用提供的密钥和IV参数初始化加密器。
  3. 处理填充后的数据:

    • 在加密之前,调用ApplyZeroPadding方法对数据进行零填充。
    • 加密完成后,输出结果为Base64编码的字符串。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【如何在MacOS升级ruby版本】
  • 根据子网前缀的长度计算ip范围
  • 搭建数据库启前后端环境
  • Cobalt Strike 4.8 用户指南-第六节-Payload Artifacts和反病毒规避
  • 3分钟带你了解什么是元数据管理
  • 基于Python的网络编程
  • docker安装配置、docker命令
  • git 更改分支名称
  • 公司网站设计方案
  • 【Linux】进程周边:进程概念
  • 给已有的.so库重新封装一个新的库,并能使用新旧库中的函数
  • Java Socket tcp udp 使用
  • Android 13 aosp 恢复出厂设置流程
  • 【MySQL】Ubuntu22.04安装MySQL8.0.39及修改默认用户名和密码
  • 如何操作可以有效的防止其他人修改Excel文件?
  • 【从零开始安装kubernetes-1.7.3】2.flannel、docker以及Harbor的配置以及作用
  • 78. Subsets
  • create-react-app项目添加less配置
  • css选择器
  • flutter的key在widget list的作用以及必要性
  • HTTP那些事
  • JavaScript创建对象的四种方式
  • js 实现textarea输入字数提示
  • VUE es6技巧写法(持续更新中~~~)
  • Windows Containers 大冒险: 容器网络
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 最近的计划
  • HanLP分词命名实体提取详解
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • # 执行时间 统计mysql_一文说尽 MySQL 优化原理
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • (C语言)二分查找 超详细
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (LeetCode 49)Anagrams
  • (Redis使用系列) Springboot 实现Redis消息的订阅与分布 四
  • (Redis使用系列) Springboot 使用redis实现接口Api限流 十
  • (附源码)springboot宠物医疗服务网站 毕业设计688413
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (四)Linux Shell编程——输入输出重定向
  • (图文详解)小程序AppID申请以及在Hbuilderx中运行
  • (一)Neo4j下载安装以及初次使用
  • (原創) 如何解决make kernel时『clock skew detected』的warning? (OS) (Linux)
  • (转载)CentOS查看系统信息|CentOS查看命令
  • (轉)JSON.stringify 语法实例讲解
  • *++p:p先自+,然后*p,最终为3 ++*p:先*p,即arr[0]=1,然后再++,最终为2 *p++:值为arr[0],即1,该语句执行完毕后,p指向arr[1]
  • ../depcomp: line 571: exec: g++: not found
  • .NET Core IdentityServer4实战-开篇介绍与规划
  • .NET Remoting学习笔记(三)信道
  • .NET面试题(二)
  • @Async注解的坑,小心
  • @EventListener注解使用说明
  • @vue/cli脚手架
  • [ 网络通信基础 ]——网络的传输介质(双绞线,光纤,标准,线序)
  • [AI]文心一言出圈的同时,NLP处理下的ChatGPT-4.5最新资讯