c#的SM4的加密和解密
直接上代码
#region 加密解密 /// <summary> /// 加密 /// </summary> public static class OSDClient { public static string X_ACCOUNT_KEY = "x-account-key"; public static string X_ACCOUNT_SECRET = "x-account-secret"; public static string X_NONCE = "x-nonce"; public static string X_CREATED = "x-created"; public static string X_SIGN = "x-sign"; private static MD5 digest; private static MD5 GetDigest() { if (digest == null) { digest = MD5.Create(); } return digest; } /// <summary> /// md5加密 /// </summary> /// <param name="accountKey"></param> /// <param name="accountSecret"></param> /// <param name="nonce"></param> /// <param name="created"></param> /// <returns></returns> public static string signCredential(string accountKey, string accountSecret, string nonce, long created) { if (string.IsNullOrEmpty(accountKey)) return "accountKey不能为空"; if (string.IsNullOrEmpty(accountSecret)) return "accountSecret不能为空"; if (string.IsNullOrEmpty(nonce)) return "nonce不能为空"; if (created == 0) return "无效的签名时间搓"; StringBuilder builder = new StringBuilder(); string str = builder.Append("x-account-key").Append("=").Append(accountKey).Append("&").Append("x-account-secret").Append("=").Append(accountSecret).Append("&").Append("x-created").Append("=").Append(created).Append("&").Append("x-nonce").Append("=").Append(nonce).ToString(); byte[] digest = GetDigest().ComputeHash(Encoding.UTF8.GetBytes(str)); StringBuilder sb = new StringBuilder(); for (int i = 0; i < digest.Length; i++) { sb.Append((digest[i] & 0xFF | 0x100).ToString("x2").Substring(1, 2)); } return sb.ToString(); } } #region 解密 public class SM4Context { public int mode = SM4.SM4_ENCRYPT; public bool isPadding = true; public uint[] sk = new uint[32]; } public class SM4 { public const int SM4_ENCRYPT = 1; public const int SM4_DECRYPT = 0; private uint GET_ULONG_BE(byte[] b, int i) { //uint n = (uint)((b[i] & 0xFF) << 24 | ((b[i + 1] & 0xFF) << 16) | ((b[i + 2] & 0xFF) << 8) | (b[i + 3] & 0xFF) & 0xFF); //return n; return (uint)((b[i] & 0xFF) << 24 | (b[i + 1] & 0xFF) << 16 | (b[i + 2] & 0xFF) << 8 | (b[i + 3] & 0xFF)); } private void PUT_ULONG_BE(uint n, byte[] b, int i) { b[i] = (byte)(n >> 24); b[i + 1] = (byte)(n >> 16); b[i + 2] = (byte)(n >> 8); b[i + 3] = (byte)(n); } private uint SHL(uint x, int n) { return x << n; } private uint ROTL(uint x, int n) { return SHL(x, n) | x >> 32 - n; } private void SWAP(uint[] sk, int i) { uint t = sk[i]; sk[i] = sk[31 - i]; sk[31 - i] = t; } public static readonly byte[] SboxTable = new byte[] { 0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05, 0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99, 0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62, 0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6, 0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8, 0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35, 0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x21, 0x78, 0x87, 0xD4, 0x00, 0x46, 0x57, 0x9F, 0xD3, 0x27, 0x52, 0x4C, 0x36, 0x02, 0xE7, 0xA0, 0xC4, 0xC8, 0x9E, 0xEA, 0xBF, 0x8A, 0xD2, 0x40, 0xC7, 0x38, 0xB5, 0xA3, 0xF7, 0xF2, 0xCE, 0xF9, 0x61, 0x15, 0xA1, 0xE0, 0xAE, 0x5D, 0xA4, 0x9B, 0x34, 0x1A, 0x55, 0xAD, 0x93, 0x32, 0x30, 0xF5, 0x8C, 0xB1, 0xE3, 0x1D, 0xF6, 0xE2, 0x2E, 0x82, 0x66, 0xCA, 0x60, 0xC0, 0x29, 0x23, 0xAB, 0x0D, 0x53, 0x4E, 0x6F, 0xD5, 0xDB, 0x37, 0x45, 0xDE, 0xFD, 0x8E, 0x2F, 0x03, 0xFF, 0x6A, 0x72, 0x6D, 0x6C, 0x5B, 0x51, 0x8D, 0x1B, 0xAF, 0x92, 0xBB, 0xDD, 0xBC, 0x7F, 0x11, 0xD9, 0x5C, 0x41, 0x1F, 0x10, 0x5A, 0xD8, 0x0A, 0xC1, 0x31, 0x88, 0xA5, 0xCD, 0x7B, 0xBD, 0x2D, 0x74, 0xD0, 0x12, 0xB8, 0xE5, 0xB4, 0xB0, 0x89, 0x69, 0x97, 0x4A, 0x0C, 0x96, 0x77, 0x7E, 0x65, 0xB9, 0xF1, 0x09, 0xC5, 0x6E, 0xC6, 0x84, 0x18, 0xF0, 0x7D, 0xEC, 0x3A, 0xDC, 0x4D, 0x20, 0x79, 0xEE, 0x5F, 0x3E, 0xD7, 0xCB, 0x39, 0x48 }; public static readonly uint[] FK = new uint[] { 0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC }; public static readonly uint[] CK = new uint[] { 0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269, 0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9, 0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249, 0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9, 0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229, 0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299, 0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209, 0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279 }; private byte sm4Sbox(byte inch) { int i = inch & 0xFF; return SboxTable[i]; } private uint sm4Lt(uint ka) { byte[] a = new byte[4]; byte[] b = new byte[4]; PUT_ULONG_BE(ka, a, 0); b[0] = sm4Sbox(a[0]); b[1] = sm4Sbox(a[1]); b[2] = sm4Sbox(a[2]); b[3] = sm4Sbox(a[3]); uint bb = GET_ULONG_BE(b, 0); return bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24); } private uint sm4F(uint x0, uint x1, uint x2, uint x3, uint rk) { return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk); } private uint sm4CalciRK(uint ka) { byte[] a = new byte[4]; byte[] b = new byte[4]; PUT_ULONG_BE(ka, a, 0); b[0] = sm4Sbox(a[0]); b[1] = sm4Sbox(a[1]); b[2] = sm4Sbox(a[2]); b[3] = sm4Sbox(a[3]); uint bb = GET_ULONG_BE(b, 0); return bb ^ ROTL(bb, 13) ^ ROTL(bb, 23); } private void sm4_setkey(uint[] SK, byte[] key) { uint[] MK = new uint[4]; uint[] k = new uint[36]; MK[0] = GET_ULONG_BE(key, 0); MK[1] = GET_ULONG_BE(key, 4); MK[2] = GET_ULONG_BE(key, 8); MK[3] = GET_ULONG_BE(key, 12); k[0] = MK[0] ^ FK[0]; k[1] = MK[1] ^ FK[1]; k[2] = MK[2] ^ FK[2]; k[3] = MK[3] ^ FK[3]; for (int i = 0; i < 32; i++) { k[i + 4] = k[i] ^ sm4CalciRK(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ CK[i]); SK[i] = k[i + 4]; } } private void sm4_one_round(uint[] sk, byte[] input, byte[] output) { uint[] ulbuf = new uint[36]; ulbuf[0] = GET_ULONG_BE(input, 0); ulbuf[1] = GET_ULONG_BE(input, 4); ulbuf[2] = GET_ULONG_BE(input, 8); ulbuf[3] = GET_ULONG_BE(input, 12); for (int i = 0; i < 32; i++) { ulbuf[i + 4] = sm4F(ulbuf[i], ulbuf[i + 1], ulbuf[i + 2], ulbuf[i + 3], sk[i]); } PUT_ULONG_BE(ulbuf[35], output, 0); PUT_ULONG_BE(ulbuf[34], output, 4); PUT_ULONG_BE(ulbuf[33], output, 8); PUT_ULONG_BE(ulbuf[32], output, 12); } private byte[] padding(byte[] input, int mode) { if (input == null) return null; if (mode == SM4_ENCRYPT) { int p = 16 - input.Length % 16; byte[] ret = new byte[input.Length + p]; System.Array.Copy(input, 0, ret, 0, input.Length); for (int i = 0; i < p; i++) ret[input.Length + i] = (byte)p; return ret; } else { int p = input[input.Length - 1]; byte[] ret = new byte[input.Length - p]; System.Array.Copy(input, 0, ret, 0, input.Length - p); return ret; } } public void sm4_setkey_enc(SM4Context ctx, byte[] key) { if (ctx == null) throw new ArgumentException("ctx is null!"); if (key == null || key.Length != 16) throw new ArgumentException("key error!"); ctx.mode = SM4_ENCRYPT; sm4_setkey(ctx.sk, key); } public void sm4_setkey_dec(SM4Context ctx, byte[] key) { if (ctx == null) throw new ArgumentException("ctx is null!"); if (key == null || key.Length != 16) throw new ArgumentException("key error!"); ctx.mode = SM4_DECRYPT; sm4_setkey(ctx.sk, key); for (int i = 0; i < 16; i++) SWAP(ctx.sk, i); } public byte[] sm4_crypt_ecb(SM4Context ctx, byte[] input) { if (input == null) throw new Exception("input is null!"); if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) input = padding(input, SM4_ENCRYPT); int length = input.Length; using (MemoryStream bins = new MemoryStream(input)) using (MemoryStream bous = new MemoryStream()) { while (length > 0) { byte[] inBytes = new byte[16]; byte[] outBytes = new byte[16]; bins.Read(inBytes, 0, 16); sm4_one_round(ctx.sk, inBytes, outBytes); bous.Write(outBytes, 0, 16); length -= 16; } byte[] output = bous.ToArray(); if (ctx.isPadding && ctx.mode == SM4_DECRYPT) output = padding(output, SM4_DECRYPT); return output; } } public byte[] sm4_crypt_cbc(SM4Context ctx, byte[] iv, byte[] input) { if (iv == null || iv.Length != 16) throw new Exception("iv error!"); if (input == null) throw new Exception("input is null!"); if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) input = padding(input, SM4_ENCRYPT); int length = input.Length; using (MemoryStream bins = new MemoryStream(input)) using (MemoryStream bous = new MemoryStream()) { byte[] ivBytes = new byte[16]; System.Array.Copy(iv, ivBytes, 16); if (ctx.mode == SM4_ENCRYPT) { while (length > 0) { byte[] inBytes = new byte[16]; byte[] outBytes = new byte[16]; byte[] out1Bytes = new byte[16]; bins.Read(inBytes, 0, 16); for (int i = 0; i < 16; i++) outBytes[i] = (byte)(inBytes[i] ^ ivBytes[i]); sm4_one_round(ctx.sk, outBytes, out1Bytes); System.Array.Copy(out1Bytes, 0, ivBytes, 0, 16); bous.Write(out1Bytes, 0, 16); length -= 16; } } else { byte[] temp = new byte[16]; while (length > 0) { byte[] inBytes = new byte[16]; byte[] outBytes = new byte[16]; byte[] out1Bytes = new byte[16]; bins.Read(inBytes, 0, 16); System.Array.Copy(inBytes, 0, temp, 0, 16); sm4_one_round(ctx.sk, inBytes, outBytes); for (int i = 0; i < 16; i++) out1Bytes[i] = (byte)(outBytes[i] ^ ivBytes[i]); System.Array.Copy(temp, 0, ivBytes, 0, 16); bous.Write(out1Bytes, 0, 16); length -= 16; } } byte[] output = bous.ToArray(); if (ctx.isPadding && ctx.mode == SM4_DECRYPT) output = padding(output, SM4_DECRYPT); return output; } } } public class SM4Utils { public string GenerateKeyOrIV() { byte[] key = new byte[16]; RandomNumberGenerator.Fill(key); return Util.ByteToHex(key); } public byte[] EncryptECB(byte[] src, byte[] keyBytes) { SM4Context ctx = new SM4Context(); ctx.isPadding = true; ctx.mode = SM4.SM4_ENCRYPT; SM4 sm4 = new SM4(); sm4.sm4_setkey_enc(ctx, keyBytes); return sm4.sm4_crypt_ecb(ctx, src); } public byte[] DecryptECB(byte[] cipher, byte[] keyBytes) { SM4Context ctx = new SM4Context(); ctx.isPadding = true; ctx.mode = SM4.SM4_DECRYPT; SM4 sm4 = new SM4(); sm4.sm4_setkey_dec(ctx, keyBytes); return sm4.sm4_crypt_ecb(ctx, cipher); } public byte[] EncryptCBC(byte[] src, byte[] keyBytes, byte[] ivBytes) { SM4Context ctx = new SM4Context(); ctx.isPadding = true; ctx.mode = SM4.SM4_ENCRYPT; SM4 sm4 = new SM4(); sm4.sm4_setkey_enc(ctx, keyBytes); return sm4.sm4_crypt_cbc(ctx, ivBytes, src); } public byte[] DecryptCBC(byte[] cipher, byte[] keyBytes, byte[] ivBytes) { SM4Context ctx = new SM4Context(); ctx.isPadding = true; ctx.mode = SM4.SM4_DECRYPT; SM4 sm4 = new SM4(); sm4.sm4_setkey_dec(ctx, keyBytes); return sm4.sm4_crypt_cbc(ctx, ivBytes, cipher); } } public class Util { public static byte[] IntToBytes(int num) { byte[] bytes = new byte[4]; bytes[0] = (byte)(num & 0xFF); bytes[1] = (byte)((num >> 8) & 0xFF); bytes[2] = (byte)((num >> 16) & 0xFF); bytes[3] = (byte)((num >> 24) & 0xFF); return bytes; } public static int ByteToInt(byte[] bytes) { return bytes[0] & 0xFF | (bytes[1] & 0xFF) << 8 | (bytes[2] & 0xFF) << 16 | (bytes[3] & 0xFF) << 24; } public static string GetHexString(byte[] bytes, bool upperCase = true) { StringBuilder ret = new StringBuilder(); foreach (byte b in bytes) ret.Append(b.ToString(upperCase ? "X2" : "x2")); return ret.ToString(); } public static byte[] HexStringToBytes(string hexString) { if (string.IsNullOrEmpty(hexString)) return null; hexString = hexString.ToUpper(); int length = hexString.Length / 2; char[] hexChars = hexString.ToCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte)(Convert.ToByte(hexChars[pos]) << 4 | Convert.ToByte(hexChars[pos + 1])); } return d; } public static string ByteToHex(byte[] b) { if (b == null) throw new ArgumentException("Argument b (byte array) is null!"); return BitConverter.ToString(b).Replace("-", ""); } public static byte[] HexToByte(string hex) { if (hex.Length % 2 != 0) throw new ArgumentException("Hex string must have even length"); byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < bytes.Length; i++) bytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); return bytes; } } public class ClientBasedCipher { private byte[] key; private byte[] iv; private string clientSecret; private SM4Utils utils = new SM4Utils(); public ClientBasedCipher(string clientSecret) { SetClientSecret(clientSecret); } private byte[] Normalize(byte[] ba) { if (ba.Length != 16) { int balen = ba.Length; byte[] tba = new byte[16]; System.Array.Copy(ba, 0, tba, 0, Math.Min(balen, 16)); return tba; } return ba; } private byte[] Clone(byte[] ba) { byte[] ret = new byte[16]; System.Array.Copy(ba, 0, ret, 0, 16); return ret; } private byte[] Decode(string secret) { byte[] ret; try { // Base64Url 解码 string base64 = secret.Replace('-', '+').Replace('_', '/'); switch (base64.Length % 4) { case 2: base64 += "=="; break; case 3: base64 += "="; break; } ret = Convert.FromBase64String(base64); } catch (Exception) { ret = Encoding.UTF8.GetBytes(secret); } return Normalize(ret); } private byte[] Secret2Key(string secret) { return Decode(secret); } private byte[] Secret2Iv(string secret) { return Decode(secret.ToLower()); } public ClientBasedCipher SetClientSecret(string clientSecret) { if (clientSecret == null) throw new ArgumentException("clientSecret missing"); this.clientSecret = clientSecret; this.key = Secret2Key(clientSecret); this.iv = Secret2Iv(clientSecret); return this; } public byte[] Encrypt(byte[] src) { return utils.EncryptCBC(src, Clone(key), Clone(iv)); } public byte[] Decrypt(byte[] cipher) { return utils.DecryptCBC(cipher, Clone(key), Clone(iv)); } public string EncryptToBase64(string src) { byte[] encrypted = Encrypt(Encoding.UTF8.GetBytes(src)); // Base64Url 编码 string base64 = Convert.ToBase64String(encrypted) .TrimEnd('=') .Replace('+', '-') .Replace('/', '_'); return base64; } public string DecryptFromBase64(string cipher) { // Base64Url 解码 string base64 = cipher.Replace('-', '+').Replace('_', '/'); switch (base64.Length % 4) { case 2: base64 += "=="; break; case 3: base64 += "="; break; } byte[] decoded = Convert.FromBase64String(base64); return Encoding.UTF8.GetString(Decrypt(decoded)); } } #endregion #endregion
使用方式
string originalText = "Hello, SM4 CBC加密测试! 这是一段测试文本。"; string key = "mySecretKey12345"; // 自动处理为16字节 Console.WriteLine($"原文: {originalText}"); Console.WriteLine($"密钥: {key}"); ClientBasedCipher cipher = new ClientBasedCipher(key); // 加密 string encrypted = cipher.EncryptToBase64(originalText); Console.WriteLine($"加密后 (Base64): {encrypted}"); // 解密 string decrypted = cipher.DecryptFromBase64(encrypted); Console.WriteLine($"解密后: {decrypted}"); // 验证 Console.WriteLine($"加解密成功: {originalText == decrypted}"); Console.ReadKey();
结果

                    
                
                
            
        
浙公网安备 33010602011771号