.Net Core关于SM4 加密算法 修正版
2022/4/6 17:20:00
本文主要是介绍.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 }
这篇关于.Net Core关于SM4 加密算法 修正版的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2022-03-01沐雪多租宝商城源码从.NetCore3.1升级到.Net6的步骤
- 2024-11-18微软研究:RAG系统的四个层次提升理解与回答能力
- 2024-11-15C#中怎么从PEM格式的证书中提取公钥?-icode9专业技术文章分享
- 2024-11-14云架构设计——如何用diagrams.net绘制专业的AWS架构图?
- 2024-05-08首个适配Visual Studio平台的国产智能编程助手CodeGeeX正式上线!C#程序员必备效率神器!
- 2024-03-30C#设计模式之十六迭代器模式(Iterator Pattern)【行为型】
- 2024-03-29c# datetime tryparse
- 2024-02-21list find index c#
- 2024-01-24convert toint32 c#
- 2024-01-24Advanced .Net Debugging 1:你必须知道的调试工具