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();

结果

image

 

posted @ 2025-08-27 10:07  世人皆萌  阅读(123)  评论(0)    收藏  举报