1. 程式人生 > 其它 >.Net Core關於SM4 加密演算法 修正版

.Net Core關於SM4 加密演算法 修正版

最近涉及到了很多關於SM4加密解密的對接要求,說明國密這塊有越來越多的人願意使用了。(題外話)

所以我也網上看了看大家寫的加密解密幫助類。也算是對前輩的程式碼做個實現,最終發現有些地方是有問題的。

這裡引用一個博主的文章內容,

引用地址:https://www.cnblogs.com/LowKeyCXY/p/14207819.html  (侵刪找我哦!)

 

SM4的介紹和邏輯規則我就不多說了,大家可以去引用的博主裡看,程式碼有幾點問題:

1.使用ecb模式時,確實沒問題,但碰到CBC時就發現問題了,最總找到了邏輯錯誤點。

如下圖:

 

 

 

這裡的i,明顯有問題,實際應該是 j * 16 如果使用博主的程式碼,則會報錯(別問我怎麼知道的,狗頭)

2.博主給的加密後使用的是hex.encode 也就是雜湊加密,其實現實中有的地方要求base64.encode也就是base64加密,所以不看程式碼不行啊。(一直和對接方的資料不一致,說多了都是淚)。

如下圖:

 

 

 

好了,話不多說,趕緊上程式碼:

(本程式碼僅對CBC做了優化,小夥伴可以自由發揮哈!)

 

  1 using Org.BouncyCastle.Utilities.Encoders;
  2 using System;
  3 using System.Collections.Generic;
  4 using System.Text;
  5 
  6 namespace
AirtuBus.Infrastructure.Encryp 7 { 8 public class SM4Helper 9 { 10 /// <summary> 11 /// 加密ECB模式 12 /// </summary> 13 /// <param name="secretKey">金鑰</param> 14 /// <param name="hexString">明文是否是十六進位制</param> 15 /// <param name="plainText">
明文</param> 16 /// <returns>返回密文</returns> 17 public static string Encrypt_ECB(string secretKey, bool hexString, string plainText) 18 { 19 SM4Context ctx = new SM4Context(); 20 ctx.isPadding = true; 21 ctx.mode = Sm4.SM4_ENCRYPT; 22 byte[] keyBytes; 23 if (hexString) 24 { 25 keyBytes = Hex.Decode(secretKey); 26 } 27 else 28 { 29 keyBytes = Encoding.UTF8.GetBytes(secretKey); 30 } 31 Sm4 sm4 = new Sm4(); 32 sm4.sm4_setkey_enc(ctx, keyBytes); 33 byte[] encrypted = sm4.sm4_crypt_ecb(ctx, Encoding.UTF8.GetBytes(plainText)); 34 string cipherText = Encoding.UTF8.GetString(Hex.Encode(encrypted)); 35 return cipherText; 36 } 37 38         /// <summary> 39         /// 解密ECB模式 40         /// </summary> 41         /// <param name="secretKey">金鑰</param> 42         /// <param name="hexString">明文是否是十六進位制</param> 43         /// <param name="cipherText">密文</param> 44         /// <returns>返回明文</returns> 45         public static string Decrypt_ECB(string secretKey, bool hexString, string cipherText) 46 { 47 SM4Context ctx = new SM4Context(); 48 ctx.isPadding = true; 49 ctx.mode = Sm4.SM4_DECRYPT; 50 51 byte[] keyBytes; 52 if (hexString) 53 { 54 keyBytes = Hex.Decode(secretKey); 55 } 56 else 57 { 58 keyBytes = Encoding.UTF8.GetBytes(secretKey); 59 } 60 61 Sm4 sm4 = new Sm4(); 62 sm4.sm4_setkey_dec(ctx, keyBytes); 63 byte[] decrypted = sm4.sm4_crypt_ecb(ctx, Hex.Decode(cipherText)); 64 return Encoding.UTF8.GetString(decrypted); 65 } 66 67         /// <summary> 68         /// 加密CBC模式 69         /// </summary> 70         /// <param name="secretKey">金鑰</param> 71         /// <param name="hexString">明文是否是十六進位制</param> 72         /// <param name="iv"></param> 73         /// <param name="plainText">明文</param> 74         /// <returns>返回密文</returns> 75         public static string Encrypt_CBC(string secretKey, bool hexString, string iv, string plainText, string encoding = "utf-8", int needEnCode = 1) 76 { 77 if (encoding.IsNullOrEmpty()) 78 { 79 encoding = "utf-8"; 80 } 81 if (encoding == "GBK" || encoding == "GB2312") 82 { 83 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); 84 } 85 SM4Context ctx = new SM4Context(); 86 ctx.isPadding = true; 87 ctx.mode = Sm4.SM4_ENCRYPT; 88 byte[] keyBytes; 89 byte[] ivBytes; 90 if (hexString) 91 { 92 keyBytes = Hex.Decode(secretKey); 93 ivBytes = Hex.Decode(iv); 94 } 95 else 96 { 97 keyBytes = Encoding.UTF8.GetBytes(secretKey); 98 ivBytes = Encoding.UTF8.GetBytes(iv); 99 } 100 Sm4 sm4 = new Sm4(); 101 sm4.sm4_setkey_enc(ctx, keyBytes); 102 byte[] encrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Encoding.GetEncoding(encoding).GetBytes(plainText)); 103 if (needEnCode == 1) 104 { 105 encrypted = Hex.Encode(encrypted); 106 }else if(needEnCode == 2) 107 { 108 encrypted = Base64.Encode(encrypted); 109 } 110 string cipherText = Encoding.UTF8.GetString(encrypted); 111 return cipherText; 112 } 113 114         /// <summary> 115         /// 解密CBC模式 116         /// </summary> 117         /// <param name="secretKey">金鑰</param> 118         /// <param name="hexString">明文是否是十六進位制</param> 119         /// <param name="iv"></param> 120         /// <param name="cipherText">密文</param> 121         /// <returns>返回明文</returns> 122         public static string Decrypt_CBC(string secretKey, bool hexString, string iv, string cipherText) 123 { 124 SM4Context ctx = new SM4Context(); 125 ctx.isPadding = true; 126 ctx.mode = Sm4.SM4_DECRYPT; 127 byte[] keyBytes; 128 byte[] ivBytes; 129 if (hexString) 130 { 131 keyBytes = Hex.Decode(secretKey); 132 ivBytes = Hex.Decode(iv); 133 } 134 else 135 { 136 keyBytes = Encoding.UTF8.GetBytes(secretKey); 137 ivBytes = Encoding.UTF8.GetBytes(iv); 138 } 139 Sm4 sm4 = new Sm4(); 140 sm4.sm4_setkey_dec(ctx, keyBytes); 141 byte[] decrypted = sm4.sm4_crypt_cbc(ctx, ivBytes, Hex.Decode(cipherText)); 142 return Encoding.UTF8.GetString(decrypted); 143 } 144 145 /// <summary> 146 /// 加密ECB模式 BASE64 147 /// </summary> 148 /// <param name="secretKey"></param> 149 /// <param name="hexString"></param> 150 /// <param name="plainText"></param> 151 /// <returns></returns> 152 public static string Encrypt_ECB_Base64(string secretKey, bool hexString, string plainText) 153 { 154 SM4Context ctx = new SM4Context(); 155 ctx.isPadding = true; 156 ctx.mode = Sm4.SM4_ENCRYPT; 157 byte[] keyBytes; 158 if (hexString) 159 { 160 keyBytes = Hex.Decode(secretKey); 161 } 162 else 163 { 164 keyBytes = Encoding.UTF8.GetBytes(secretKey); 165 } 166 Sm4 sm4 = new Sm4(); 167 sm4.sm4_setkey_enc(ctx, keyBytes); 168 byte[] encrypted = sm4.sm4_crypt_ecb(ctx, Encoding.UTF8.GetBytes(plainText)); 169 string cipherText = Encoding.UTF8.GetString(Base64.Encode(encrypted)); 170 return cipherText; 171 } 172 173 /// <summary> 174 /// 解密ECB模式 BASE64 175 /// </summary> 176 /// <param name="secretKey"></param> 177 /// <param name="hexString"></param> 178 /// <param name="plainText"></param> 179 /// <returns></returns> 180 public static string Decrypt_ECB_Base64(string secretKey, bool hexString, string cipherText) 181 { 182 if (cipherText.IsNullOrEmpty() || secretKey.IsNullOrEmpty()) return null; 183 SM4Context ctx = new SM4Context(); 184 ctx.isPadding = true; 185 ctx.mode = Sm4.SM4_DECRYPT; 186 187 byte[] keyBytes; 188 if (hexString) 189 { 190 keyBytes = Hex.Decode(secretKey); 191 } 192 else 193 { 194 keyBytes = Encoding.UTF8.GetBytes(secretKey); 195 } 196 197 Sm4 sm4 = new Sm4(); 198 sm4.sm4_setkey_dec(ctx, keyBytes); 199 byte[] decrypted = sm4.sm4_crypt_ecb(ctx, Base64.Decode(cipherText)); 200 return Encoding.UTF8.GetString(decrypted); 201 } 202 } 203 204 public class Sm4 205 { 206 public const int SM4_ENCRYPT = 1; 207 public const int SM4_DECRYPT = 0; 208 209 private long GET_ULONG_BE(byte[] b, int i) 210 { 211 long n = (long)(b[i] & 0xff) << 24 | (long)((b[i + 1] & 0xff) << 16) | (long)((b[i + 2] & 0xff) << 8) | (long)(b[i + 3] & 0xff) & 0xffffffffL; 212 return n; 213 } 214 private void PUT_ULONG_BE(long n, byte[] b, int i) 215 { 216 b[i] = (byte)(int)(0xFF & n >> 24); 217 b[i + 1] = (byte)(int)(0xFF & n >> 16); 218 b[i + 2] = (byte)(int)(0xFF & n >> 8); 219 b[i + 3] = (byte)(int)(0xFF & n); 220 } 221 private long SHL(long x, int n) 222 { 223 return (x & 0xFFFFFFFF) << n; 224 } 225 private long ROTL(long x, int n) 226 { 227 return SHL(x, n) | x >> (32 - n); 228 } 229 private void SWAP(long[] sk, int i) 230 { 231 long t = sk[i]; 232 sk[i] = sk[(31 - i)]; 233 sk[(31 - i)] = t; 234 } 235 public byte[] SboxTable = new byte[] { (byte) 0xd6, (byte) 0x90, (byte) 0xe9, (byte) 0xfe, 236 (byte) 0xcc, (byte) 0xe1, 0x3d, (byte) 0xb7, 0x16, (byte) 0xb6, 237 0x14, (byte) 0xc2, 0x28, (byte) 0xfb, 0x2c, 0x05, 0x2b, 0x67, 238 (byte) 0x9a, 0x76, 0x2a, (byte) 0xbe, 0x04, (byte) 0xc3, 239 (byte) 0xaa, 0x44, 0x13, 0x26, 0x49, (byte) 0x86, 0x06, 240 (byte) 0x99, (byte) 0x9c, 0x42, 0x50, (byte) 0xf4, (byte) 0x91, 241 (byte) 0xef, (byte) 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 242 (byte) 0xed, (byte) 0xcf, (byte) 0xac, 0x62, (byte) 0xe4, 243 (byte) 0xb3, 0x1c, (byte) 0xa9, (byte) 0xc9, 0x08, (byte) 0xe8, 244 (byte) 0x95, (byte) 0x80, (byte) 0xdf, (byte) 0x94, (byte) 0xfa, 245 0x75, (byte) 0x8f, 0x3f, (byte) 0xa6, 0x47, 0x07, (byte) 0xa7, 246 (byte) 0xfc, (byte) 0xf3, 0x73, 0x17, (byte) 0xba, (byte) 0x83, 247 0x59, 0x3c, 0x19, (byte) 0xe6, (byte) 0x85, 0x4f, (byte) 0xa8, 248 0x68, 0x6b, (byte) 0x81, (byte) 0xb2, 0x71, 0x64, (byte) 0xda, 249 (byte) 0x8b, (byte) 0xf8, (byte) 0xeb, 0x0f, 0x4b, 0x70, 0x56, 250 (byte) 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, (byte) 0xd1, 251 (byte) 0xa2, 0x25, 0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, (byte) 0x87, 252 (byte) 0xd4, 0x00, 0x46, 0x57, (byte) 0x9f, (byte) 0xd3, 0x27, 253 0x52, 0x4c, 0x36, 0x02, (byte) 0xe7, (byte) 0xa0, (byte) 0xc4, 254 (byte) 0xc8, (byte) 0x9e, (byte) 0xea, (byte) 0xbf, (byte) 0x8a, 255 (byte) 0xd2, 0x40, (byte) 0xc7, 0x38, (byte) 0xb5, (byte) 0xa3, 256 (byte) 0xf7, (byte) 0xf2, (byte) 0xce, (byte) 0xf9, 0x61, 0x15, 257 (byte) 0xa1, (byte) 0xe0, (byte) 0xae, 0x5d, (byte) 0xa4, 258 (byte) 0x9b, 0x34, 0x1a, 0x55, (byte) 0xad, (byte) 0x93, 0x32, 259 0x30, (byte) 0xf5, (byte) 0x8c, (byte) 0xb1, (byte) 0xe3, 0x1d, 260 (byte) 0xf6, (byte) 0xe2, 0x2e, (byte) 0x82, 0x66, (byte) 0xca, 261 0x60, (byte) 0xc0, 0x29, 0x23, (byte) 0xab, 0x0d, 0x53, 0x4e, 0x6f, 262 (byte) 0xd5, (byte) 0xdb, 0x37, 0x45, (byte) 0xde, (byte) 0xfd, 263 (byte) 0x8e, 0x2f, 0x03, (byte) 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 264 0x51, (byte) 0x8d, 0x1b, (byte) 0xaf, (byte) 0x92, (byte) 0xbb, 265 (byte) 0xdd, (byte) 0xbc, 0x7f, 0x11, (byte) 0xd9, 0x5c, 0x41, 266 0x1f, 0x10, 0x5a, (byte) 0xd8, 0x0a, (byte) 0xc1, 0x31, 267 (byte) 0x88, (byte) 0xa5, (byte) 0xcd, 0x7b, (byte) 0xbd, 0x2d, 268 0x74, (byte) 0xd0, 0x12, (byte) 0xb8, (byte) 0xe5, (byte) 0xb4, 269 (byte) 0xb0, (byte) 0x89, 0x69, (byte) 0x97, 0x4a, 0x0c, 270 (byte) 0x96, 0x77, 0x7e, 0x65, (byte) 0xb9, (byte) 0xf1, 0x09, 271 (byte) 0xc5, 0x6e, (byte) 0xc6, (byte) 0x84, 0x18, (byte) 0xf0, 272 0x7d, (byte) 0xec, 0x3a, (byte) 0xdc, 0x4d, 0x20, 0x79, 273 (byte) 0xee, 0x5f, 0x3e, (byte) 0xd7, (byte) 0xcb, 0x39, 0x48 }; 274 public uint[] FK = { 0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc }; 275 public uint[] CK = { 0x00070e15,0x1c232a31,0x383f464d,0x545b6269, 276 0x70777e85,0x8c939aa1,0xa8afb6bd,0xc4cbd2d9, 277 0xe0e7eef5,0xfc030a11,0x181f262d,0x343b4249, 278 0x50575e65,0x6c737a81,0x888f969d,0xa4abb2b9, 279 0xc0c7ced5,0xdce3eaf1,0xf8ff060d,0x141b2229, 280 0x30373e45,0x4c535a61,0x686f767d,0x848b9299, 281 0xa0a7aeb5,0xbcc3cad1,0xd8dfe6ed,0xf4fb0209, 282 0x10171e25,0x2c333a41,0x484f565d,0x646b7279 }; 283 private byte sm4Sbox(byte inch) 284 { 285 int i = inch & 0xFF; 286 byte retVal = SboxTable[i]; 287 return retVal; 288 } 289 private long sm4Lt(long ka) 290 { 291 long bb = 0L; 292 long c = 0L; 293 byte[] a = new byte[4]; 294 byte[] b = new byte[4]; 295 PUT_ULONG_BE(ka, a, 0); 296 b[0] = sm4Sbox(a[0]); 297 b[1] = sm4Sbox(a[1]); 298 b[2] = sm4Sbox(a[2]); 299 b[3] = sm4Sbox(a[3]); 300 bb = GET_ULONG_BE(b, 0); 301 c = bb ^ ROTL(bb, 2) ^ ROTL(bb, 10) ^ ROTL(bb, 18) ^ ROTL(bb, 24); 302 return c; 303 } 304 private long sm4F(long x0, long x1, long x2, long x3, long rk) 305 { 306 return x0 ^ sm4Lt(x1 ^ x2 ^ x3 ^ rk); 307 } 308 309 private long sm4CalciRK(long ka) 310 { 311 long bb = 0L; 312 long rk = 0L; 313 byte[] a = new byte[4]; 314 byte[] b = new byte[4]; 315 PUT_ULONG_BE(ka, a, 0); 316 b[0] = sm4Sbox(a[0]); 317 b[1] = sm4Sbox(a[1]); 318 b[2] = sm4Sbox(a[2]); 319 b[3] = sm4Sbox(a[3]); 320 bb = GET_ULONG_BE(b, 0); 321 rk = bb ^ ROTL(bb, 13) ^ ROTL(bb, 23); 322 return rk; 323 } 324 325 private void sm4_setkey(long[] SK, byte[] key) 326 { 327 long[] MK = new long[4]; 328 long[] k = new long[36]; 329 int i = 0; 330 MK[0] = GET_ULONG_BE(key, 0); 331 MK[1] = GET_ULONG_BE(key, 4); 332 MK[2] = GET_ULONG_BE(key, 8); 333 MK[3] = GET_ULONG_BE(key, 12); 334 k[0] = MK[0] ^ (long)FK[0]; 335 k[1] = MK[1] ^ (long)FK[1]; 336 k[2] = MK[2] ^ (long)FK[2]; 337 k[3] = MK[3] ^ (long)FK[3]; 338 for (; i < 32; i++) 339 { 340 k[(i + 4)] = (k[i] ^ sm4CalciRK(k[(i + 1)] ^ k[(i + 2)] ^ k[(i + 3)] ^ (long)CK[i])); 341 SK[i] = k[(i + 4)]; 342 } 343 } 344 345 private void sm4_one_round(long[] sk, byte[] input, byte[] output) 346 { 347 int i = 0; 348 long[] ulbuf = new long[36]; 349 ulbuf[0] = GET_ULONG_BE(input, 0); 350 ulbuf[1] = GET_ULONG_BE(input, 4); 351 ulbuf[2] = GET_ULONG_BE(input, 8); 352 ulbuf[3] = GET_ULONG_BE(input, 12); 353 while (i < 32) 354 { 355 ulbuf[(i + 4)] = sm4F(ulbuf[i], ulbuf[(i + 1)], ulbuf[(i + 2)], ulbuf[(i + 3)], sk[i]); 356 i++; 357 } 358 PUT_ULONG_BE(ulbuf[35], output, 0); 359 PUT_ULONG_BE(ulbuf[34], output, 4); 360 PUT_ULONG_BE(ulbuf[33], output, 8); 361 PUT_ULONG_BE(ulbuf[32], output, 12); 362 } 363 364 private byte[] padding(byte[] input, int mode) 365 { 366 if (input == null) 367 { 368 return null; 369 } 370 371 byte[] ret = (byte[])null; 372 if (mode == SM4_ENCRYPT) 373 { 374 int p = 16 - input.Length % 16; 375 ret = new byte[input.Length + p]; 376 Array.Copy(input, 0, ret, 0, input.Length); 377 for (int i = 0; i < p; i++) 378 { 379 ret[input.Length + i] = (byte)p; 380 } 381 } 382 else 383 { 384 int p = input[input.Length - 1]; 385 ret = new byte[input.Length - p]; 386 Array.Copy(input, 0, ret, 0, input.Length - p); 387 } 388 return ret; 389 } 390 391 public void sm4_setkey_enc(SM4Context ctx, byte[] key) 392 { 393 ctx.mode = SM4_ENCRYPT; 394 sm4_setkey(ctx.sk, key); 395 } 396 397 public void sm4_setkey_dec(SM4Context ctx, byte[] key) 398 { 399 int i = 0; 400 ctx.mode = SM4_DECRYPT; 401 sm4_setkey(ctx.sk, key); 402 for (i = 0; i < 16; i++) 403 { 404 SWAP(ctx.sk, i); 405 } 406 } 407 408 public byte[] sm4_crypt_ecb(SM4Context ctx, byte[] input) 409 { 410 if ((ctx.isPadding) && (ctx.mode == SM4_ENCRYPT)) 411 { 412 input = padding(input, SM4_ENCRYPT); 413 } 414 415 int length = input.Length; 416 byte[] bins = new byte[length]; 417 Array.Copy(input, 0, bins, 0, length); 418 byte[] bous = new byte[length]; 419 for (int i = 0; length > 0; length -= 16, i++) 420 { 421 byte[] inBytes = new byte[16]; 422 byte[] outBytes = new byte[16]; 423 Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length); 424 sm4_one_round(ctx.sk, inBytes, outBytes); 425 Array.Copy(outBytes, 0, bous, i * 16, length > 16 ? 16 : length); 426 } 427 428 if (ctx.isPadding && ctx.mode == SM4_DECRYPT) 429 { 430 bous = padding(bous, SM4_DECRYPT); 431 } 432 return bous; 433 } 434 435 public byte[] sm4_crypt_cbc(SM4Context ctx, byte[] iv, byte[] input) 436 { 437 if (ctx.isPadding && ctx.mode == SM4_ENCRYPT) 438 { 439 input = padding(input, SM4_ENCRYPT); 440 } 441 442 int i = 0; 443 int length = input.Length; 444 byte[] bins = new byte[length]; 445 Array.Copy(input, 0, bins, 0, length); 446 byte[] bous = null; 447 List<byte> bousList = new List<byte>(); 448 if (ctx.mode == SM4_ENCRYPT) 449 { 450 for (int j = 0; length > 0; length -= 16, j++) 451 { 452 byte[] inBytes = new byte[16]; 453 byte[] outBytes = new byte[16]; 454 byte[] out1 = new byte[16]; 455 Array.Copy(bins, j * 16, inBytes, 0, length > 16 ? 16 : length); 456 for (i = 0; i < 16; i++) 457 { 458 outBytes[i] = ((byte)(inBytes[i] ^ iv[i])); 459 } 460 sm4_one_round(ctx.sk, outBytes, out1); 461 Array.Copy(out1, 0, iv, 0, 16); 462 for (int k = 0; k < 16; k++) 463 { 464 bousList.Add(out1[k]); 465 } 466 } 467 } 468 else 469 { 470 byte[] temp = new byte[16]; 471 for (int j = 0; length > 0; length -= 16, j++) 472 { 473 byte[] inBytes = new byte[16]; 474 byte[] outBytes = new byte[16]; 475 byte[] out1 = new byte[16]; 476 477 478 Array.Copy(bins, i * 16, inBytes, 0, length > 16 ? 16 : length); 479 Array.Copy(inBytes, 0, temp, 0, 16); 480 sm4_one_round(ctx.sk, inBytes, outBytes); 481 for (i = 0; i < 16; i++) 482 { 483 out1[i] = ((byte)(outBytes[i] ^ iv[i])); 484 } 485 Array.Copy(temp, 0, iv, 0, 16); 486 for (int k = 0; k < 16; k++) 487 { 488 bousList.Add(out1[k]); 489 } 490 } 491 } 492 if (ctx.isPadding && ctx.mode == SM4_DECRYPT) 493 { 494 bous = padding(bousList.ToArray(), SM4_DECRYPT); 495 return bous; 496 } 497 else 498 { 499 return bousList.ToArray(); 500 } 501 } 502 } 503 504 public class SM4Context 505 { 506 public int mode; 507 public long[] sk; 508 public bool isPadding; 509 public SM4Context() 510 { 511 this.mode = 1; 512 this.isPadding = true; 513 this.sk = new long[32]; 514 } 515 } 516 }