C# + Vue3采用SM4
引用
类库:
using Org.BouncyCastle.Crypto.IO; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities.Encoders;
public static class SM4Util_New { /// <summary> /// 默认编码 /// </summary> private static Encoding DefaultEncoding = Encoding.UTF8; //"GB2312"; public const string ALGORITHM_NAME = "SM4"; //SM4/ECB/NoPadding //SM4/CBC/NoPadding //SM4/CBC/PKCS7Padding /// <summary> /// ECB模式 [pkcs7padding填充方式] /// </summary> public const string ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS7Padding"; /// <summary> /// CBC模式 [pkcs7padding填充方式] /// </summary> public const string ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS7Padding"; //这个不需要 SM4默认就是128位的密钥 //public const int DEFAULT_KEY_SIZE = 128; /// <summary> /// 解密 /// </summary> /// <param name="key">密钥</param> /// <param name="passInput"></param> /// <param name="encoding">编码</param> /// <param name="algorithmMode">默认是ECB模式 [pkcs5padding填充方式]</param> /// <returns></returns> public static string DecryptEcb(string key, string passInput, Encoding encoding, string algorithmMode = ALGORITHM_NAME_ECB_PADDING) { encoding = encoding ?? Encoding.UTF8; byte[] keyBytes = Hex.Decode(key); byte[] input = Hex.Decode(passInput); return encoding.GetString(DecryptEcb(keyBytes, input, algorithmMode)); } /// <summary> /// 解密 /// </summary> /// <param name="key">密钥</param> /// <param name="passInput"></param> /// <param name="algorithmMode">默认是ECB模式 [pkcs5padding填充方式]</param> /// <returns></returns> public static string DecryptEcb(string key, string passInput, string algorithmMode = ALGORITHM_NAME_ECB_PADDING) { return DecryptEcb(key, passInput, DefaultEncoding, algorithmMode); } /// <summary> /// 解密 /// </summary> /// <param name="keyBytes">密钥</param> /// <param name="passInput"></param> /// <param name="algorithmMode">加密方式</param> /// <returns></returns> /// <exception cref="Exception"></exception> public static byte[] DecryptEcb(byte[] keyBytes, byte[] passInput, string algorithmMode) { KeyParameter key = ParameterUtilities.CreateKeyParameter(ALGORITHM_NAME, keyBytes); IBufferedCipher inCipher = CipherUtilities.GetCipher(algorithmMode); //forEncryption位false表示解密 inCipher.Init(false, key); MemoryStream bIn = new MemoryStream(passInput, false); CipherStream cIn = new CipherStream(bIn, inCipher, null); byte[] bytes = new byte[passInput.Length]; byte[] totalBytes; try { #region 此代码一次性读取解密 可行 /*BinaryReader dIn = new BinaryReader(cIn); byte[] extra = dIn.ReadBytes(passInput.Length); Array.Copy(extra, 0, bytes, 0, extra.Length);*/ #endregion #region 官方demo 是先处理一半 再处理剩下的 最后把剩下的复制到bytes剩余部分 BinaryReader dIn = new BinaryReader(cIn); for (int i = 0; i != passInput.Length / 2; i++) { bytes[i] = dIn.ReadByte(); } int remaining = bytes.Length - passInput.Length / 2; byte[] extra = dIn.ReadBytes(remaining); //把为了加密补位的部分去掉 if (extra.Length < remaining) { int len = passInput.Length / 2 + extra.Length; totalBytes = new byte[len]; Array.Copy(bytes, 0, totalBytes, 0, passInput.Length / 2); extra.CopyTo(totalBytes, passInput.Length / 2); return totalBytes; } else { extra.CopyTo(bytes, passInput.Length / 2); } //throw new EndOfStreamException(); #endregion } catch (Exception e) { throw new Exception("SM4 failed encryption - " + e, e); } return bytes; } /// <summary> /// 加密 /// </summary> /// <param name="key">密钥</param> /// <param name="text"></param> /// <param name="encoding">编码</param> /// <param name="algorithmMode">默认是ECB模式 [pkcs5padding填充方式]</param> /// <returns></returns> public static string EncryptEcb(string key, string text, Encoding encoding, string algorithmMode = ALGORITHM_NAME_ECB_PADDING) { encoding = encoding ?? Encoding.UTF8; byte[] keyBytes = Hex.Decode(key); byte[] input = encoding.GetBytes(text); return Hex.ToHexString(EncryptEcb(keyBytes, input, algorithmMode)); } /// <summary> /// 加密 /// </summary> /// <param name="key">密钥</param> /// <param name="text"></param> /// <param name="algorithmMode"></param> /// <returns></returns> public static string EncryptEcb(string key, string text, string algorithmMode = ALGORITHM_NAME_ECB_PADDING) { return EncryptEcb(key, text, DefaultEncoding, algorithmMode); } /// <summary> /// 加密 /// </summary> /// <param name="keyBytes">密钥</param> /// <param name="input"></param> /// <returns></returns> /// <exception cref="Exception"></exception> public static byte[] EncryptEcb(byte[] keyBytes, byte[] input, string algorithmMode) { KeyParameter key = ParameterUtilities.CreateKeyParameter(ALGORITHM_NAME, keyBytes); IBufferedCipher outCipher = CipherUtilities.GetCipher(algorithmMode); //forEncryption位true表示加密 outCipher.Init(true, key); MemoryStream bOut = new MemoryStream(); CipherStream cOut = new CipherStream(bOut, null, outCipher); try { //处理前一半 for (int i = 0; i != input.Length / 2; i++) { cOut.WriteByte(input[i]); } //处理后一半 cOut.Write(input, input.Length / 2, input.Length - input.Length / 2); cOut.Close(); } catch (IOException e) { throw new Exception("SM4 failed encryption - " + e, e); } byte[] bytes = bOut.ToArray(); return bytes; } }
调用:
var encrypt = SM4Util_New.EncryptEcb(SM4_KEY, "[{\"id\":\"4c5849f2.4369f8\",\"type\":\"tab\",\"label\":\"1111\",\"disabled\":false,\"info\":\"\"}]"); var decrypt = SM4Util_New.DecryptEcb(SM4_KEY,encrypt );
密钥生成方法:
var SM4_KEY = Guid.NewGuid().ToString("N");//生成key 32位 16进制字符串
解密方法:
public static String decrypt() { var iv = Hex.Decode("fedcba98765432100123456789abcdef"); //byte[] plain = Hex.Decode("0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210"); byte[] key = Hex.Decode("0123456789abcdeffedcba9876543210"); var base64Str = Base64Util.Base64Decrypt("MGQ2Y2ZhNzNjODIzYjJhYzBkNmE5MmM1NjQxNzE4OTJmYTk1ZmE0Yjc1OTEwMzIwMjBjNGFhNDlkMmI0MmJjNQ=="); byte[] cipher = Hex.Decode(base64Str); var bs = GmUtil.Sm4DecryptCBC(key, cipher, iv, GmUtil.SM4_CBC_PKCS7PADDING); var encrypt = Encoding.UTF8.GetString(bs); System.Console.Out.WriteLine(encrypt); return encrypt; //log.Info("testSm4EncEcb: " + Hex.ToHexString(bs)); ; //bs = Sm4DecryptECB(key, bs, GmUtil.SM4_ECB_NOPADDING); }
Vue3
npm install --save sm-crypto
--https://www.npmjs.com/package/sm-crypto
npm install --save js-base64
import { sm4, sm3, sm2 } from "sm-crypto"; import { Base64 } from "js-base64";
function doDecryptSm4() { const encryptData = "1118b5cec13c3bcd39e7006f9c155017948d2025fe716b45746e9b04559e77d52f2345cf9d1cf55abee0adca8f971a0693a7ea8a26f67a377a24a26a8d5f60c4f5c145149bd4de7144bbb1093374549f3c813d0b36b2284e33aacac91e5d2f62"; const key = "8abf369903e94857bcadbcf7068d68e3"; let decryptData = sm4.decrypt(encryptData, key); // 解密,不使用 padding console.log(decryptData); console.log(Base64.encode(decryptData)); } function doEncryptSm4() { //后端返回的数据格式 var json = [ { id: "4c5849f2.4369f8", type: "tab", label: "1111", disabled: false, info: "", }, ]; const escapedJson = JSON.stringify(json); console.log(escapedJson); const msg = escapedJson; //"{'urlquerystring':'partner=XunChaApplet&channel=ApiApplet&clientversion=1.0×tamp=1701956563','Sign':'0aa77898ea0181d2c0addbaff953a633'}"; // 可以为 utf8 串或字节数组 const key = "8abf369903e94857bcadbcf7068d68e3"; // 可以为 16 进制串或字节数组,要求为 128 比特 let encryptData = sm4.encrypt(msg, key); // 加密,默认输出 16 进制字符串,默认使用 pkcs#7 填充(传 pkcs#5 也会走 pkcs#7 填充) 默认走 ECB 模式 //let encryptData = sm4.encrypt(msg, key, {padding: 'none'}) // 加密,不使用 padding //let encryptData = sm4.encrypt(msg, key, {padding: 'none', output: 'array'}) // 加密,不使用 padding,输出为字节数组 //let encryptData = sm4.encrypt(msg, key, { mode: "cbc",iv: "fedcba98765432100123456789abcdef", }); // 加密,cbc 模式 默认使用 pkcs#7 填充 console.log(encryptData); console.log(Base64.encode(encryptData)); }