C# 使用国密SM4加密解密

news/2024/9/16 23:33:17/ 标签: c#, 开发语言

首先需第三方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编码的字符串。


http://www.ppmy.cn/news/1522618.html

相关文章

MariaDB基本知识汇总

/* MariaDB 1、视图 2、临时表 3、自定义函数 4、存储过程 5、触发器 6、游标 7、变量声明与赋值 8、常用函数&#xff08;日期格式&#xff0c;Guid&#xff0c;判断&#xff0c;循环&#xff0c;XML格式操作&#xff09; 9、动态执行SQL 语句 10、开启执行计划 11、创建登录M…

AI智能分析/智慧安防EasyCVR视频汇聚平台新版本(V3.6.0)播放鉴权与播放限制时长的区别介绍

随着科技的飞速发展&#xff0c;视频技术已成为现代社会不可或缺的一部分&#xff0c;广泛应用于安防监控、娱乐传播、在线教育、电商直播等多个领域。EasyCVR视频汇聚平台作为视频技术的佼佼者&#xff0c;不断推陈出新&#xff0c;通过功能更新迭代&#xff0c;为用户提供更加…

什么是视频缓存服务器,它有哪些作用?

视频缓存服务器通常拥有大容量的存储空间和高速的读写能力&#xff0c;它通过缓存(即临时存储)用户经常访问的视频内容&#xff0c;来优化内容的分发过程。这种服务器通常部署在网络中的关键位置&#xff0c;如靠近用户接入点的位置&#xff0c;以降低用户访问视频内容时的网络…

rtsp服务器逻辑

定时器逻辑&#xff1a;比如H264文件是每隔40ms发送一帧数据。aac文件每隔23ms发送一个音频帧数据。 在sink的子类中有aac和h264的sink&#xff0c;在两个子类的构造函数中需要添加它们各自的触发时间。调用的函数时runEvery()&#xff0c;将这两个触发时间设置到了TimerManag…

【H2O2|全栈】关于HTML(1)认识HTML

HTML相关知识 目录 前言 准备工作 WEB前端是什么&#xff1f; HTML是什么&#xff1f; 如何运行HTML文件&#xff1f; 标签 概念 分类 双标签和单标签 行内标签和块标签 HTML文档结构 预告和回顾 UI设计相关 Markdown | Md文档相关 项目合作管理相关 后话 前…

数据结构之堆的创建

1、堆的概念及结构 1.1堆的概念 如果有一个关键码的集合K{k0,k1,k2,…,kn-1}&#xff0c;把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中&#xff0c;并满足ki<k2i1且ki<k2i2&#xff08;或满足ki>k2i1且ki>k2i2&#xff09;&#xff0c;其中i0…

Vue2项目搭建:Vue2.7+Vite4+Pinia+TailwindCSS+Prettier+ESLint

目前 create-vue 和 Vite 都不提供 Vue2 项目的搭建&#xff0c;不想用 Vue CLI 和 webpack&#xff0c;于是就打算从 0 搭建一个工程化项目&#xff0c;支持组合式 API (Composition API) 写法&#xff0c;没有使用 TypeScript&#xff0c;有朋友需要的话我可以再完善一下。 N…

结构体小知识

目录 前言1.结构体数组1.1结构体数组理解1.2结构体数组知识运用1.3 -> 操作符 2. 知识拓展 前言 本期blog是对上一期指针知识的知识补充&#xff0c;如果各位大佬感兴趣的话&#xff0c;可以结合起来一起看&#xff01; 1.结构体数组 1.1结构体数组理解 结构体数组在本…

pytorch torch.nn.functional.one_hot函数介绍

torch.nn.functional.one_hot 是 PyTorch 中用于生成独热编码&#xff08;one-hot encoding&#xff09;张量的函数。独热编码是一种常用的编码方式&#xff0c;特别适用于分类任务或对离散的类别标签进行处理。该函数将整数张量的每个元素转换为一个独热向量。 函数签名 tor…

notepad++软件介绍(含安装包)

Notepad 是一款开源的文本编辑器&#xff0c;主要用于编程和代码编辑。它是一个功能强大的替代品&#xff0c;常常被用来替代 Windows 系统自带的记事本。 Notepad win系统免费下载地址 以下是 Notepad 的一些主要特点和功能&#xff1a; 多语言支持&#xff1a;Notepad 支持多…

Kafka【八】如何保证消息发送的可靠性、重复性、有序性

【1】消息发送的可靠性保证 对于生产者发送的数据&#xff0c;我们有的时候是不关心数据是否已经发送成功的&#xff0c;我们只要发送就可以了。在这种场景中&#xff0c;消息可能会因为某些故障或问题导致丢失&#xff0c;我们将这种情况称之为消息不可靠。虽然消息数据可能会…

proxy代理解决vue中跨域问题

vue.config.js module.exports {...// webpack-dev-server 相关配置devServer: {host: 0.0.0.0,port: port,open: true,proxy: {/api: {target: https://vfadmin.insistence.tech/prod-api,changeOrigin: true,pathRewrite: {//[^ process.env.VUE_APP_BASE_API]: ^/api: / …

【 html+css 绚丽Loading 】000044 两仪穿行轮

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享htmlcss 绚丽Loading&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495…

【sql】评估数据迁移复杂度调查汇报240904

难度判断标准&#xff1a; - 高难度&#xff1a;使用多个表&#xff08;TBL&#xff09;或有多个join操作的工具 - 低难度&#xff1a;表数量少且没有join操作的简单工具 - 中等难度&#xff1a;介于高低之间&#xff0c;有少量join操作的工具 5. 最后说明不需要仔细…

25届计算机毕业设计:3步打造北部湾助农平台,Java SpringBoot实践

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

AF透明模式/虚拟网线模式组网部署

透明模式组网 实验拓扑 防火墙基本配置 接口配置 eth1 eth3 放通策略 1. 内网用户上班时间&#xff08;9:00-17:00&#xff09;不允许看视频、玩游戏及网上购物&#xff0c;其余时 间访问互联网不受限制&#xff1b;&#xff08;20 分&#xff09; 应用控制策略 2. 互联…

[论文笔记]RAFT: Adapting Language Model to Domain Specific RAG

引言 今天带来一篇结合RAG和微调的论文&#xff1a;RAFT: Adapting Language Model to Domain Specific RAG。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们"。 本文介绍了检索增强微调(Retrieval Augmented Fine Tunin…

【Impala SQL 造数(一)】

前言 SQL 造数即生成测试数据&#xff0c;一般是编码完成之后的测试阶段所需&#xff0c;测试数据可以用于多种目的&#xff0c;包括测试应用程序的功能、业务场景测试、性能测试、数据恢复测试等。在测试阶段特别是数据类需求&#xff0c;需要很多造数场景&#xff0c;像 Hiv…

尚品汇-支付宝支付同步异步回调实现(四十七)

目录&#xff1a; &#xff08;1&#xff09;订单支付码有效时间 &#xff08;2&#xff09;支付后回调—同步回调 &#xff08;3&#xff09;支付宝回调—异步回调 &#xff08;1&#xff09;订单支付码有效时间 &#xff08;2&#xff09;支付后回调—同步回调 static修饰…

【Jupyter Notebook】安装与使用

打开Anaconda Navigator点击"Install"(Launch安装前是Install)点击"Launch"点击"File"-"New"-"Notebook"​ 5.点击"Select"选择Python版本 6.输入测试代码并按"Enter+Shift"运行代码: 代码如下: …