-
Notifications
You must be signed in to change notification settings - Fork 93
/
Program.cs
1250 lines (1159 loc) · 52.8 KB
/
Program.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace com.github.xiangyuecn.rsacsharp {
/// <summary>
/// RSA、RSA_PEM测试控制台主程序,.NET Core、.NET Framework均可测试
/// GitHub: https://github.com/xiangyuecn/RSA-csharp
/// </summary>
class Program {
static public void Main(string[] args) {
//【请在这里编写你自己的测试代码】
ShowMenu(args);
}
static void RSATest(bool fast) {
//新生成一个RSA密钥,也可以通过已有的pem、xml文本密钥来创建RSA
var rsa = new RSA_Util(512);
// var rsa = new RSA_Util("pem或xml文本密钥");
// var rsa = new RSA_Util(RSA_PEM.FromPEM("pem文本密钥"));
// var rsa = new RSA_Util(RSA_PEM.FromXML("xml文本密钥"));
//得到pem对象
RSA_PEM pem = rsa.ToPEM(false);
//提取密钥pem字符串
string pem_pkcs1 = pem.ToPEM_PKCS1();
string pem_pkcs8 = pem.ToPEM_PKCS8();
//提取密钥xml字符串
string xml = rsa.ToXML();
AssertMsg(T("【" + rsa.KeySize + "私钥(XML)】:", "[ " + rsa.KeySize + " Private Key (XML) ]:"), rsa.KeySize == 512);
S(xml);
S();
ST("【" + rsa.KeySize + "私钥(PKCS#1)】:", "[ " + rsa.KeySize + " Private Key (PKCS#1) ]:");
S(pem_pkcs1);
S();
ST("【" + rsa.KeySize + "公钥(PKCS#8)】:", "[ " + rsa.KeySize + " Public Key (PKCS#8) ]:");
S(pem.ToPEM_PKCS8(true));
S();
var str = T("abc内容123", "abc123");
var en = rsa.Encrypt("PKCS1", str);
ST("【加密】:", "[ Encrypt ]:");
S(en);
ST("【解密】:", "[ Decrypt ]:");
var de = rsa.Decrypt("PKCS1", en);
AssertMsg(de, de == str);
if (!fast) {
var str2 = str; for (var i = 0; i < 15; i++) str2 += str2;
ST("【长文本加密解密】:", "[ Long text encryption and decryption ]:");
AssertMsg(str2.Length + T("个字 OK", " characters OK"), rsa.Decrypt("PKCS1", rsa.Encrypt("PKCS1", str2)) == str2);
}
ST("【签名SHA1】:", "[ Signature SHA1 ]:");
var sign = rsa.Sign("SHA1", str);
Console.WriteLine(sign);
AssertMsg(T("校验 OK", "Verify OK"), rsa.Verify("SHA1", sign, str));
Console.WriteLine();
//用pem文本创建RSA
var rsa2 = new RSA_Util(RSA_PEM.FromPEM(pem_pkcs8));
ST("【用PEM新创建的RSA是否和上面的一致】:", "[ Is the newly created RSA with PEM consistent with the above ]:");
Assert("XML:", rsa2.ToXML() == rsa.ToXML());
Assert("PKCS1:", rsa2.ToPEM().ToPEM_PKCS1() == pem.ToPEM_PKCS1());
Assert("PKCS8:", rsa2.ToPEM().ToPEM_PKCS8() == pem.ToPEM_PKCS8());
//用xml文本创建RSA
var rsa3 = new RSA_Util(RSA_PEM.FromXML(xml));
ST("【用XML新创建的RSA是否和上面的一致】:", "[ Is the newly created RSA with XML consistent with the above ]:");
Assert("XML:", rsa3.ToXML() == rsa.ToXML());
Assert("PKCS1:", rsa3.ToPEM().ToPEM_PKCS1() == pem.ToPEM_PKCS1());
Assert("PKCS8:", rsa3.ToPEM().ToPEM_PKCS8() == pem.ToPEM_PKCS8());
//--------RSA_PEM私钥验证---------
//使用PEM全量参数构造pem对象
RSA_PEM pemX = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, pem.Key_D, pem.Val_P, pem.Val_Q, pem.Val_DP, pem.Val_DQ, pem.Val_InverseQ);
ST("【RSA_PEM是否和原始RSA一致】:", "[ Is RSA_PEM consistent with the original RSA ]:");
S(pemX.KeySize + T("位", " bits"));
Assert("XML:", pemX.ToXML(false) == pem.ToXML(false));
Assert("PKCS1:", pemX.ToPEM_PKCS1() == pem.ToPEM_PKCS1());
Assert("PKCS8:", pemX.ToPEM_PKCS8() == pem.ToPEM_PKCS8());
ST("仅公钥:", "Public Key Only:");
Assert("XML:", pemX.ToXML(true) == pem.ToXML(true));
Assert("PKCS1:", pemX.ToPEM_PKCS1(true) == pem.ToPEM_PKCS1(true));
Assert("PKCS8:", pemX.ToPEM_PKCS8(true) == pem.ToPEM_PKCS8(true));
//--------RSA_PEM公钥验证---------
RSA_PEM pemY = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, null);
ST("【RSA_PEM仅公钥是否和原始RSA一致】:", "[ RSA_PEM only public key is consistent with the original RSA ]:");
S(pemY.KeySize + T("位", " bits"));
Assert("XML:", pemY.ToXML(false) == pem.ToXML(true));
Assert("PKCS1:", pemY.ToPEM_PKCS1() == pem.ToPEM_PKCS1(true));
Assert("PKCS8:", pemY.ToPEM_PKCS8() == pem.ToPEM_PKCS8(true));
if (!fast) {
//使用n、e、d构造pem对象
RSA_PEM pem4 = new RSA_PEM(pem.Key_Modulus, pem.Key_Exponent, pem.Key_D);
RSA_Util rsa4 = new RSA_Util(pem4);
ST("【用n、e、d构造解密】", "[ Construct decryption with n, e, d ]");
de = rsa4.Decrypt("PKCS1", en);
AssertMsg(de, de == str);
AssertMsg(T("校验 OK", "Verify OK"), rsa4.Verify("SHA1", sign, str));
//对调交换公钥私钥
ST("【Unsafe|对调公钥私钥,私钥加密公钥解密】", "[ Unsafe | Swap the public key and private key, private key encryption and public key decryption ]");
var rsaPri = rsa.SwapKey_Exponent_D__Unsafe();
var rsaPub = new RSA_Util(rsa.ToPEM(true)).SwapKey_Exponent_D__Unsafe();
if (!RSA_Util.IsUseBouncyCastle) {
rsaPub = rsaPri;
ST(".NET自带的RSA不支持仅含公钥的密钥进行解密和签名,使用NoPadding填充方式或IsUseBouncyCastle时无此问题", "The RSA that comes with .NET does not support decryption and signing with keys containing only public keys. This problem does not occur when using NoPadding or IsUseBouncyCastle.");
}
try {
var enPri = rsaPri.Encrypt("PKCS1", str);
var signPub = rsaPub.Sign("SHA1", str);
de = rsaPub.Decrypt("PKCS1", enPri);
AssertMsg(de, de == str);
AssertMsg(T("校验 OK", "Verify OK"), rsaPri.Verify("SHA1", signPub, str));
} catch (Exception e) {
if (!RSA_Util.IS_CoreOr46 && !RSA_Util.IsUseBouncyCastle) {
S(T("不支持在RSACryptoServiceProvider中使用:", "Not supported in RSACryptoServiceProvider: ") + e.Message);
} else {
throw e;
}
}
rsa4 = rsaPri.SwapKey_Exponent_D__Unsafe();
de = rsa4.Decrypt("PKCS1", en);
AssertMsg(de, de == str);
AssertMsg(T("校验 OK", "Verify OK"), rsa4.Verify("SHA1", sign, str));
}
if (!fast) {
S();
ST("【测试一遍所有的加密、解密填充方式】 按回车键继续测试...", "[ Test all the encryption and decryption padding mode ] Press Enter to continue testing...");
ReadIn();
RSA_Util rsa5 = new RSA_Util(2048);
testPaddings(false, rsa5, new RSA_Util(rsa5.ToPEM(true)), true);
}
}
static Type Type_RuntimeInformation(Type[] outOSPlatform) {
#if (RSA_BUILD__NET_CORE || NETCOREAPP || NETSTANDARD || NET) //csproj:PropertyGroup.DefineConstants + https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/preprocessor-directives
if (outOSPlatform != null) outOSPlatform[0] = typeof(OSPlatform);
return typeof(RuntimeInformation);
#else
//.NET Framework 4.7.1 才有 都在mscorlib.dll里面
var type = typeof(ComVisibleAttribute).Assembly.GetType("System.Runtime.InteropServices.RuntimeInformation");
if (type != null && outOSPlatform != null) outOSPlatform[0] = type.Assembly.GetType("System.Runtime.InteropServices.OSPlatform");
return type;
#endif
}
static bool NET_IsWindows() {
Type[] typeOSPlatform = new Type[1];
Type type = Type_RuntimeInformation(typeOSPlatform);
if (type != null) {
dynamic a1 = typeOSPlatform[0].GetProperty("Windows").GetValue(null);
return (bool)RSA_Util.FindFunc(type, "IsOSPlatform", new string[] { "os" }).Invoke(null, new object[] { a1 });
}
var ver = Environment.OSVersion.VersionString.ToLower();
return ver.Contains("microsoft") && ver.Contains("windows");
}
static string NET_Ver() {
string val, os;
Type type = Type_RuntimeInformation(null);
if (type != null) {
val = (string)type.GetProperty("FrameworkDescription").GetValue(null);
os = (string)type.GetProperty("OSDescription").GetValue(null);
} else {
val = "EnvVer-" + Environment.Version;
os = (NET_IsWindows() ? "Windows" : "Linux?");
}
val += " | " + os;
return val;
}
static void Assert(string msg, bool check) {
AssertMsg(msg + check, check);
}
static void AssertMsg(string msg, bool check) {
if (!check) throw new Exception(msg);
Console.WriteLine(msg);
}
/// <summary>控制台输出一个换行</summary>
static private void S() {
Console.WriteLine();
}
/// <summary>控制台输出内容</summary>
static private void S(string s) {
Console.WriteLine(s);
}
/// <summary>控制台输出内容 + 简版多语言支持,根据当前语言返回中文或英文,简化调用<see cref="RSA_PEM.T(string, string)"/> </summary>
static private void ST(string zh, string en) {
Console.WriteLine(T(zh, en));
}
/// <summary>简版多语言支持,根据当前语言返回中文或英文,简化调用<see cref="RSA_PEM.T(string, string)"/> </summary>
static private string T(string zh, string en) {
return RSA_PEM.T(zh, en);
}
static string ReadIn() {
return Console.ReadLine();
}
static string ReadPath(string tips, string tips2) {
while (true) {
ST("请输入" + tips + "路径" + tips2 + ": ", "Please enter " + tips + " path" + tips2 + ":");
Console.Write("> ");
string path = ReadIn().Trim();
if (path.Length == 0 || path.StartsWith("+")) {
return path;
}
if (!File.Exists(path) && !Directory.Exists(path)) {
ST("文件[" + path + "]不存在", "File [" + path + "] does not exist");
continue;
}
return path;
}
}
static byte[] ReadFile(string path) {
return File.ReadAllBytes(path);
}
static void WriteFile(string path, byte[] val) {
File.WriteAllBytes(path, val);
}
static readonly string HR = "---------------------------------------------------------";
static private Assembly Bc__Assembly = null;
static private string[] Bc__Dlls = new string[] {
"BouncyCastle.Crypto.dll", "BouncyCastle.Cryptography.dll"
};
static bool CanLoad_BouncyCastle() {
if (Bc__Assembly != null) return true;
Assembly bc = null;
foreach (var dll in Bc__Dlls) {
try {
bc = Assembly.LoadFrom(dll);
Bc__Assembly = bc;
break;
} catch { }
}
return bc != null;
}
static void printEnv() {
S(".NET Version: " + NET_Ver() + " RSA_PEM.Lang=" + RSA_PEM.Lang);
if (RSA_Util.IsUseBouncyCastle) return;
var errs = "";
if (!RSA_Util.IS_CoreOr46) {
errs += errs.Length > 0 ? T("、", ", ") : "";
errs += T("除OAEP+SHA1以外的所有OAEP加密填充模式", "All OAEP encryption padding modes except for OAEP+SHA1");
errs += errs.Length > 0 ? T("、", ", ") : "";
errs += T("PSS签名填充模式(其他填充模式不影响)", "PSS signature padding mode (other padding modes do not affect)");
}
bool _;
if (!SupportHash("SHA-512/256", false, out _)) {
errs += errs.Length > 0 ? T("、", ", ") : "";
errs += T("SHA-512/224(/256)摘要算法", "SHA-512/224 (/256) digest algorithm");
}
if (!SupportHash("SHA3-256", false, out _)) {
errs += errs.Length > 0 ? T("、", ", ") : "";
errs += T("SHA3系列摘要算法", "SHA3 series digest algorithm");
}
ST("*** .NET不支持NoPadding加密填充模式、不支持SHA-512/224(/256)摘要算法、需要.NET8以上才支持SHA3系列摘要算法,可通过引入BouncyCastle加密增强库来扩充.NET加密功能。", "*** .NET does not support the NoPadding encryption padding mode, does not support the SHA-512/224 (/256) digest algorithm, and requires .NET8 or higher to support the SHA3 series digest algorithm. You can expand the .NET encryption function by introducing the BouncyCastle encryption enhancement library.");
if (errs.Length > 0) {
ST("*** 当前.NET版本太低,不支持:" + errs + ";如需获得这些功能支持,解决办法1:升级使用高版本.NET来运行本测试程序(可能支持);解决办法2:引入BouncyCastle即可得到全部支持。", "*** The current .NET version is too low and does not support: " + errs + "; if you need to obtain support for these functions, solution 1: upgrade to a higher version of .NET to run this test program (may be supported); solution 2: Full support is available with the introduction of BouncyCastle.");
}
ST("*** 如需获得全部加密签名模式支持,可按此方法引入BouncyCastle加密增强库:到 https://www.nuget.org/packages/Portable.BouncyCastle 下载得到NuGet包(或使用 BouncyCastle.Cryptography 包),用压缩软件提取其中lib目录内对应.NET版本下的BouncyCastle.Crypto.dll,放到本测试程序目录内,然后通过测试菜单B进行注册即可得到全部支持。", "*** If you need full encryption and signature mode support, you can introduce BouncyCastle encryption enhancement library in this way: Go to https://www.nuget.org/packages/Portable.BouncyCastle to download the NuGet package (or use the BouncyCastle.Cryptography package), and use compression software to extract it The lib directory corresponds to BouncyCastle.Crypto.dll under the .NET version, place it in the directory of this test program, and then register it through test menu B to get full support.");
}
static bool SupportHash(string hash, bool checkBc, out bool isBc) {
object obj = RSA_Util.HashFromName(hash);
var val = obj != null;
isBc = false;
if (val || !checkBc) {
return val;
}
if (BcAssembly != null) {
try {
obj = BcAssembly.GetType("Org.BouncyCastle.Security.DigestUtilities").GetMethod("GetDigest", new Type[] { typeof(string) }).Invoke(null, new object[] { hash });
} catch {
obj = null;
}
if (obj != null) {
isBc = true;
return true;
}
}
return val;
}
static Assembly BcAssembly = null;
static void testProvider(bool checkOpenSSL) {
if (CanLoad_BouncyCastle()) {
if (BcAssembly == null) {
ST("检测到BouncyCastle加密增强库,是否要进行注册?(Y/N) Y", "The BouncyCastle encryption enhancement library is detected, do you want to register? (Y/N) Y");
} else {
ST("已注册BouncyCastle加密增强库,是否要保持注册?(Y/N) Y", "BouncyCastle encryption enhancement library has been registered, do you want to keep it registered? (Y/N) Y");
}
Console.Write("> ");
string val = ReadIn().Trim().ToUpper();
try {
if (BcAssembly == null && "N" != val) {
BcAssembly = Bc__Assembly;
RSA_Util.UseBouncyCastle(BcAssembly);
ST("已注册BouncyCastle加密增强库", "BouncyCastle encryption enhancement library registered");
}
if (BcAssembly != null && "N" == val) {
RSA_Util.UseBouncyCastle(null);
BcAssembly = null;
ST("已取消注册BouncyCastle加密增强库", "Unregistered BouncyCastle encryption enhancement library");
}
} catch (Exception e) {
S(T("BouncyCastle操作失败:", "BouncyCastle operation failed: ") + e.Message);
}
}
printEnv();
S();
RSA_Util rsa = new RSA_Util(2048);
string[] Hashs = new string[] {
"SHA-1","SHA-256","SHA-224","SHA-384","SHA-512"
,"SHA3-256","SHA3-224","SHA3-384","SHA3-512"
,"SHA-512/224","SHA-512/256","MD5"
};
S("MessageDigest" + T("支持情况:", " support status:"));
{
var Ss = new List<string>(Hashs);
Ss.Add("MD2");
Ss.Add("SHAKE128"); Ss.Add("SHAKE256");//https://blog.csdn.net/weixin_42579622/article/details/111644921
foreach (var s in Ss) {
var key = s; bool isBc;
if (SupportHash(key, true, out isBc)) {
S(" " + key + " | Provider: " + (isBc ? "BouncyCastle" : ".NET"));
} else {
S(" [x] " + key);
}
}
}
S("Encrypt Padding Mode" + T("支持情况:", " support status:"));
for (int i = 0; i < 1; i++) {
var v1 = i == 9999 ? "NONE" : "ECB";
var Ss = new List<string>(new string[] {"NoPadding"
,"PKCS1Padding"
,"OAEPPadding"});
foreach (var s in Hashs) {
Ss.Add("OAEPwith" + s + "andMGF1Padding");
}
foreach (var s in Ss) {
string key = "RSA/" + v1 + "/" + s, key2 = key;
RSA_Util.UseBouncyCastle(null);
for (var n = 0; n < 2; n++) {
try {
rsa.Encrypt(key, "123");
S(" " + key + " | Provider: " + (n == 1 ? "BouncyCastle" : ".NET"));
} catch {
if (n == 0 && BcAssembly != null) {
RSA_Util.UseBouncyCastle(BcAssembly);
continue;
}
S(" [x] " + key);
}
break;
}
RSA_Util.UseBouncyCastle(BcAssembly);
}
}
S("Signature Padding Mode" + T("支持情况:", " support status:"));
for (int i = 0; i < 3; i++) {
string v2 = i == 1 ? "/PSS" : "";
string[] Ss = i == 2 ? new string[] { "RSASSA-PSS" } : Hashs;
foreach (var s in Ss) {
string key = i == 2 ? s : (s.Replace("SHA-", "SHA") + "withRSA" + v2), key2 = key;
RSA_Util.UseBouncyCastle(null);
for (var n = 0; n < 2; n++) {
try {
rsa.Sign(key, "123");
S(" " + key + " | Provider: " + (n == 1 ? "BouncyCastle" : ".NET"));
} catch {
if (n == 0 && BcAssembly != null) {
RSA_Util.UseBouncyCastle(BcAssembly);
continue;
}
S(" [x] " + key);
}
break;
}
RSA_Util.UseBouncyCastle(BcAssembly);
}
}
S(HR);
ST("测试一遍所有的加密、解密填充方式:", "Test all the encryption and decryption padding mode:");
testPaddings(checkOpenSSL, rsa, new RSA_Util(rsa.ToPEM(true)), true);
S(HR);
ST("Unsafe|是否要对调公钥私钥(私钥加密公钥解密)重新测试一遍?(Y/N) N", "Unsafe | Do you want to swap the public and private keys (private key encryption and public key decryption) and test again? (Y/N) N");
Console.Write("> ");
string yn = ReadIn().Trim().ToUpper();
if (yn == "Y") {
var rsaPri = rsa.SwapKey_Exponent_D__Unsafe();
var rsaPub = new RSA_Util(rsa.ToPEM(true)).SwapKey_Exponent_D__Unsafe();
testPaddings(checkOpenSSL, rsaPub, rsaPri, true);
}
}
/// <summary>测试一遍所有的加密、解密填充方式</summary>
static int testPaddings(bool checkOpenSSL, RSA_Util rsaPri, RSA_Util rsaPub, bool log) {
int errCount = 0;
var errMsgs = new List<string>();
var txt = "1234567890";
if (!checkOpenSSL) {
txt += txt + txt + txt + txt; txt += txt;//100
txt += txt + txt + txt + txt; txt += txt + "a";//1001
}
byte[] txtData = Encoding.UTF8.GetBytes(txt);
if (checkOpenSSL) {
try {
runOpenSSL(rsaPri.HasPrivate ? rsaPri : rsaPub, txtData);
} catch (Exception e) {
S(T("运行OpenSSL失败:", "Failed to run OpenSSL: ") + e.Message);
return errCount;
}
}
var encKeys = RSA_Util.RSAPadding_Enc_DefaultKeys();
foreach (var type in encKeys) {
var errMsg = "";
try {
{
byte[] enc = rsaPub.Encrypt(type, txtData);
byte[] dec = rsaPri.Decrypt(type, enc);
bool isOk = true;
if (dec.Length != txtData.Length) {
isOk = false;
} else {
for (int i = 0; i < dec.Length; i++) {
if (dec[i] != txtData[i]) {
isOk = false; break;
}
}
}
if (!isOk) {
errMsg = T("解密结果不一致", "Decryption results are inconsistent");
throw new Exception(errMsg);
}
}
if (checkOpenSSL) {
byte[] enc;
try {
enc = testOpenSSL(true, type);
} catch (Exception e) {
errMsg = "+OpenSSL: " + T("OpenSSL加密出错", "OpenSSL encryption error");
throw e;
}
byte[] dec = rsaPri.Decrypt(type, enc);
bool isOk = true;
if (dec.Length != txtData.Length) {
isOk = false;
} else {
for (int i = 0; i < dec.Length; i++) {
if (dec[i] != txtData[i]) {
isOk = false; break;
}
}
}
if (!isOk) {
errMsg = "+OpenSSL: " + T("解密结果不一致", "Decryption results are inconsistent");
throw new Exception(errMsg);
}
}
if (log) {
S(" " + (checkOpenSSL ? " [+OpenSSL]" : "") + " " + T("加密解密:", "Encryption decryption: ") + type + " | " + RSA_Util.RSAPadding_Enc(type));
}
} catch (Exception e) {
if (!log && RSA_Util.IsDotNetSupportError(e.Message)) {
//NOOP
} else {
errCount++;
if (errMsg.Length == 0) errMsg = T("加密解密出现异常", "An exception occurred in encryption decryption");
errMsg = " [x] " + errMsg + ": " + type + " | " + RSA_Util.RSAPadding_Enc(type);
S(errMsg);
errMsgs.Add(errMsg + T("。", ". ") + e.Message);
}
}
}
var signKeys = RSA_Util.RSAPadding_Sign_DefaultKeys();
foreach (var type in signKeys) {
var errMsg = "";
try {
{
byte[] sign = rsaPri.Sign(type, txtData);
var isOk = rsaPub.Verify(type, sign, txtData);
if (!isOk) {
errMsg = T("未通过校验", "Failed verification");
throw new Exception(errMsg);
}
}
if (checkOpenSSL) {
byte[] sign;
try {
sign = testOpenSSL(false, type);
} catch (Exception e) {
errMsg = "+OpenSSL: " + T("OpenSSL签名出错", "OpenSSL signature error");
throw e;
}
var isOk = rsaPub.Verify(type, sign, txtData);
if (!isOk) {
errMsg = "+OpenSSL: " + T("未通过校验", "Failed verification");
throw new Exception(errMsg);
}
}
if (log) {
S(" " + (checkOpenSSL ? " [+OpenSSL]" : "") + " " + T("签名验证:", "Signature verification: ") + type + " | " + RSA_Util.RSAPadding_Sign(type));
}
} catch (Exception e) {
if (!log && RSA_Util.IsDotNetSupportError(e.Message)) {
//NOOP
} else {
errCount++;
if (errMsg.Length == 0) errMsg = T("签名验证出现异常", "An exception occurred in signature verification");
errMsg = " [x] " + errMsg + ": " + type + " | " + RSA_Util.RSAPadding_Sign(type);
S(errMsg);
errMsgs.Add(errMsg + T("。", ". ") + e.Message);
}
}
}
if (log) {
if (errMsgs.Count == 0) {
ST("填充方式全部测试通过。", "All padding mode tests passed.");
} else {
ST("按回车键显示详细错误消息...", "Press Enter to display detailed error message...");
ReadIn();
}
}
if (errMsgs.Count > 0) {
S(string.Join("\n", errMsgs));
}
closeOpenSSL();
return errCount;
}
/// <summary>多线程并发调用同一个RSA</summary>
static void threadRun() {
int ThreadCount = Math.Max(5, Environment.ProcessorCount - 1);
bool Abort = false;
int Count = 0;
int ErrCount = 0;
RSA_Util rsa = new RSA_Util(2048);
RSA_Util rsaPub = new RSA_Util(rsa.ToPEM(true));
S(T("正在测试中,线程数:", "Under test, number of threads: ") + ThreadCount + T(",按回车键结束测试...", ", press enter to end the test..."));
for (int i = 0; i < ThreadCount; i++) {
new Thread(() => {
while (!Abort) {
int err = testPaddings(false, rsa, rsaPub, false);
if (err > 0) {
Interlocked.Add(ref ErrCount, err);
}
Interlocked.Increment(ref Count);
}
}).Start();
}
long t1 = DateTime.Now.Ticks;
new Thread(() => {
while (!Abort) {
Console.Write("\r" + T("已测试" + Count + "次,", "Tested " + Count + " times, ")
+ ErrCount + T("个错误,", " errors, ")
+ T("耗时", "") + (DateTime.Now.Ticks - t1) / 10000 / 1000 + T("秒", " seconds total"));
try {
Thread.Sleep(1000);
} catch { }
}
}).Start();
ReadIn();
Abort = true;
ST("多线程并发调用同一个RSA测试已结束。", "Multiple threads concurrently calling the same RSA test is over.");
S();
}
static void keyTools() {
ST("===== RSA密钥工具:生成密钥、转换密钥格式 ====="
, "===== RSA key tool: generate key, convert key format =====");
ST("请使用下面可用命令进行操作,命令[]内的为可选参数,参数可用\"\"包裹。", "Please use the following commands to operate. The parameters in the command `[]` are optional parameters, and the parameters can be wrapped with \"\".");
S(HR);
S("`new 1024 [-pkcs8] [saveFile [puboutFile]]`: " + T("生成新的RSA密钥,指定位数和格式:xml、pkcs1、或pkcs8(默认),提供saveFile可保存私钥到文件,提供puboutFile可额外保存一个公钥文件", "Generate a new RSA key, specify the number of digits and format: xml, pkcs1, or pkcs8 (default), provide saveFile to save the private key to a file, and provide puboutFile to save an additional public key file"));
S(HR);
S("`convert -pkcs1 [-pubout] [-swap] oldFile [newFile]`: " + T("转换密钥格式,提供已有密钥文件oldFile(支持xml、pem格式公钥或私钥),指定要转换成的格式:xml、pkcs1、或pkcs8,提供了-pubout时只导出公钥,提供了-swap时交换公钥指数私钥指数(非常规的:私钥加密公钥解密),提供newFile可保存到文件", "To convert the key format, provide the existing key file oldFile (support xml, pem format public key or private key), specify the format to be converted into: xml, pkcs1, or pkcs8, only export the public key when -pubout is provided, swap public key exponent and private key exponent when -swap is provided (unconventional: private key encryption and public key decryption), and provide newFile Can save to file"));
S(HR);
S("`exit`: " + T("输入 exit 退出工具", "Enter exit to quit the tool"));
while (true) {
loop:
Console.Write("> ");
var inStr = ReadIn().Trim();
if (inStr.Length == 0) {
ST("输入为空,请重新输入!如需退出请输入exit", "The input is empty, please re-enter! If you need to exit, please enter exit");
continue;
}
if (inStr.ToLower() == "exit") {
ST("bye! 已退出。", "bye! has exited.");
S();
return;
}
var args = new List<string>();
Regex exp = new Regex("(-?)(?:([^\"\\s]+)|\"(.*?)\")\\s*");
var sb = exp.Replace(inStr, (m) => {
var m1 = m.Groups[1].Value;
var m2 = m.Groups[2] == null ? "" : m.Groups[2].Value;
if (m2.Length > 0) {
args.Add(m1 + m2);
} else {
args.Add(m1 + m.Groups[3].Value);
}
return "";
});
if (sb.Length > 0) {
ST("参数无效:" + sb, "Invalid parameter: " + sb);
continue;
}
var cmdName = args[0].ToLower(); args.RemoveAt(0);
bool nextSave = false;
RSA_Util rsa = null; string type = "", save = "", save2 = ""; bool pubOut = false;
if (cmdName == "new") {// 生成新的pem密钥
type = "pkcs8"; string len = "";
while (args.Count > 0) {
string param = args[0], p = param.ToLower(); args.RemoveAt(0);
var m = new Regex("^(\\d+)$").Match(p);
if (m.Success) { len = m.Groups[1].Value; continue; }
m = new Regex("^-(xml|pkcs1|pkcs8)$").Match(p);
if (m.Success) { type = m.Groups[1].Value; continue; }
if (save.Length == 0 && !p.StartsWith("-")) { save = param; continue; }
if (save2.Length == 0 && !p.StartsWith("-")) { save2 = param; continue; }
ST("未知参数:" + param, "Unknown parameter: " + param);
goto loop;
}
if (len.Length == 0) { ST("请提供密钥位数!", "Please provide key digits!"); goto loop; }
try {
rsa = new RSA_Util(Convert.ToInt32(len));
} catch (Exception e) {
S(T("生成密钥出错:", "Error generating key: ") + e.Message);
goto loop;
}
nextSave = true;
}
if (cmdName == "convert") {// 转换密钥格式
string old = ""; bool swap = false;
while (args.Count > 0) {
string param = args[0], p = param.ToLower(); args.RemoveAt(0);
var m = new Regex("^-(xml|pkcs1|pkcs8)$").Match(p);
if (m.Success) { type = m.Groups[1].Value; continue; }
if (p == "-pubout") { pubOut = true; continue; }
if (p == "-swap") { swap = true; continue; }
if (old.Length == 0 && !p.StartsWith("-")) { old = param; continue; }
if (save.Length == 0 && !p.StartsWith("-")) { save = param; continue; }
ST("未知参数:" + param, "Unknown parameter: " + param);
goto loop;
}
if (type.Length == 0) { ST("请提供要转换成的格式!", "Please provide the format to convert to!"); goto loop; }
if (old.Length == 0) { ST("请提供已有密钥文件!", "Please provide an existing key file!"); goto loop; }
try {
var oldTxt = Encoding.UTF8.GetString(ReadFile(old));
rsa = new RSA_Util(oldTxt);
if (swap) rsa = rsa.SwapKey_Exponent_D__Unsafe();
} catch (Exception e) {
S(T("读取密钥文件出错", "Error reading key file ") + " (" + old + "): " + e.Message);
goto loop;
}
nextSave = true;
}
while (nextSave) {
string val;
if (type == "xml") {
val = rsa.ToXML(pubOut);
} else {
bool pkcs8 = type == "pkcs8";
val = rsa.ToPEM(false).ToPEM(pubOut, pkcs8, pkcs8);
}
if (save.Length == 0) {
S(val);
} else {
save = Path.GetFullPath(save);
try {
WriteFile(save, Encoding.UTF8.GetBytes(val));
} catch (Exception e) {
S(T("保存文件出错", "Error saving file ") + " (" + save + "): " + e.Message);
}
S(T("密钥文件已保存到:", "The key file has been saved to: ") + save);
}
if (save2.Length > 0) {
save = save2; save2 = "";
pubOut = true;
continue;
}
S();
goto loop;
}
ST("未知命令:" + cmdName, "Unknown command: " + cmdName);
}
}
static RSA_PEM loadKey = null; static string loadKeyFile = "";
/// <summary>设置:加载密钥PEM文件</summary>
static void setLoadKey() {
string path = ReadPath(T("密钥文件", "Key File")
, T(",或文件夹(内含private.pem、test.txt)。或输入'+1024 pkcs8'生成一个新密钥(填写位数、pkcs1、pkcs8)", ", or a folder (containing private.pem, test.txt). Or enter '+1024 pkcs8' to generate a new key (fill in digits, pkcs1, pkcs8) "));
if (path.StartsWith("+")) {//创建一个新密钥
Match m = new Regex("^\\+(\\d+)\\s+pkcs([18])$", RegexOptions.IgnoreCase).Match(path);
if (!m.Success) {
ST("格式不正确,请重新输入!", "The format is incorrect, please re-enter!");
setLoadKey();
} else {
int keySize = Convert.ToInt32(m.Groups[1].Value);
RSA_Util rsa = new RSA_Util(keySize);
bool isPkcs8 = m.Groups[2].Value == "8";
RSA_PEM pem = rsa.ToPEM(false);
S(keySize + T("位私钥已生成,请复制此文本保存到private.pem文件:", " bit private key has been generated. Please copy this text and save it to the private.pem file:"));
S(pem.ToPEM(false, isPkcs8, isPkcs8));
S(keySize + T("位公钥已生成,请复制此文本保存到public.pem文件:", " bit public key has been generated. Please copy this text and save it to the public.pem file:"));
S(pem.ToPEM(true, isPkcs8, isPkcs8));
waitAnyKey = true;
}
return;
}
if (path.Length == 0 && loadKeyFile.Length == 0) {
ST("未输入文件,已取消操作", "No file input, operation cancelled");
return;
}
if (path.Length == 0) {
path = loadKeyFile;
ST("重新加载密钥文件", "Reload key file");
}
if (Directory.Exists(path)) {
string txtPath = path + Path.DirectorySeparatorChar + "test.txt";
path = path + Path.DirectorySeparatorChar + "private.pem";
if (!File.Exists(path)) {
ST("此文件夹中没有private.pem文件!", "There is no private.pem file in this folder!");
setLoadKey();
return;
}
if (File.Exists(txtPath)) {//顺带加载文件夹里面的目标源文件
loadSrcBytes = ReadFile(txtPath);
loadSrcFile = txtPath;
}
}
string txt = Encoding.UTF8.GetString(ReadFile(path));
loadKey = RSA_PEM.FromPEM(txt);
loadKeyFile = path;
}
static byte[] loadSrcBytes = null; static string loadSrcFile = "";
/// <summary>设置:加载目标源文件</summary>
static void setLoadSrcBytes() {
string path = ReadPath(T("目标源文件", "Target Source File"), "");
if (path.Length == 0 && loadSrcFile.Length == 0) {
ST("未输入文件,已取消操作", "No file input, operation cancelled");
return;
}
if (path.Length == 0) {
path = loadSrcFile;
ST("重新加载目标源文件", "Reload target source file");
}
loadSrcBytes = ReadFile(path);
loadSrcFile = path;
}
static string encType = "";
/// <summary>设置加密填充模式</summary>
static bool setEncType() {
S(T("请输入加密填充模式", "Please enter the encryption Padding mode")
+ (encType.Length > 0 ? T(",回车使用当前值", ", press Enter to use the current value ") + encType : "")
+ T(";填充模式取值可选:", "; Padding mode values: ") + string.Join(", ", RSA_Util.RSAPadding_Enc_DefaultKeys())
+ T(", 或其他支持的值", ", or other supported values"));
Console.Write("> ");
string val = ReadIn().Trim();
if (val.Length > 0) {
encType = val;
}
if (encType.Length == 0) {
ST("未设置,已取消操作", "Not set, operation canceled");
}
return encType.Length > 0;
}
/// <summary>加密</summary>
static void execEnc() {
string save = loadSrcFile + ".enc.bin";
S(T("密钥文件:", "Key file: ") + loadKeyFile);
S(T("目标文件:", "Target file: ") + loadSrcFile);
S(T("填充模式:", "Padding mode: ") + encType + " | " + RSA_Util.RSAPadding_Enc(encType));
ST("正在加密目标源文件...", "Encrypting target source file...");
RSA_Util rsa = new RSA_Util(loadKey);
long t1 = DateTime.Now.Ticks;
byte[] data = rsa.Encrypt(encType, loadSrcBytes);
S(T("加密耗时:", "Encryption time: ") + (DateTime.Now.Ticks - t1) / 10000 + "ms");
WriteFile(save, data);
S(T("已加密,结果已保存:", "Encrypted, the result is saved: ") + save);
}
/// <summary>解密对比</summary>
static void execDec() {
string encPath = loadSrcFile + ".enc.bin";
S(T("密钥文件:", "Key file: ") + loadKeyFile);
S(T("密文文件:", "Ciphertext file: ") + encPath);
S(T("对比文件:", "Compare files: ") + loadSrcFile);
S(T("填充模式:", "Padding mode: ") + encType + " | " + RSA_Util.RSAPadding_Enc(encType));
byte[]
data = ReadFile(encPath);
ST("正在解密文件...", "Decrypting file...");
RSA_Util rsa = new RSA_Util(loadKey);
long t1 = DateTime.Now.Ticks;
byte[] val = rsa.Decrypt(encType, data);
S(T("解密耗时:", "Decryption time: ") + (DateTime.Now.Ticks - t1) / 10000 + "ms");
WriteFile(loadSrcFile + ".dec.txt", val);
bool isOk = true;
if (val.Length != loadSrcBytes.Length) {
isOk = false;
} else {
for (int i = 0; i < val.Length; i++) {
if (val[i] != loadSrcBytes[i]) {
isOk = false; break;
}
}
}
if (isOk) {
ST("解密成功,和对比文件的内容一致。", "The decryption is successful, which is consistent with the content of the comparison file.");
return;
}
throw new Exception(T("解密结果和对比文件的内容不一致!", "The decryption result is inconsistent with the content of the comparison file!"));
}
static string signType = "";
/// <summary>设置签名hash+填充模式</summary>
static bool setSignType() {
S(T("请输入签名Hash+填充模式", "Please enter the signature Hash+Padding mode")
+ (signType.Length > 0 ? T(",回车使用当前值", ", press Enter to use the current value ") + signType : "")
+ T(";签名模式取值可选:", "; Signature mode values: ") + string.Join(", ", RSA_Util.RSAPadding_Sign_DefaultKeys())
+ T(", 或其他支持的值", ", or other supported values"));
Console.Write("> ");
string val = ReadIn().Trim();
if (val.Length > 0) {
signType = val;
}
if (signType.Length == 0) {
ST("未设置,已取消操作", "Not set, operation canceled");
}
return signType.Length > 0;
}
/// <summary>签名</summary>
static void execSign() {
string save = loadSrcFile + ".sign.bin";
S(T("密钥文件:", "Key file: ") + loadKeyFile);
S(T("目标文件:", "Target file: ") + loadSrcFile);
S(T("签名模式:", "Signature mode: ") + signType + " | " + RSA_Util.RSAPadding_Sign(signType));
ST("正在给目标源文件签名...", "Signing target source file...");
RSA_Util rsa = new RSA_Util(loadKey);
byte[] data = rsa.Sign(signType, loadSrcBytes);
WriteFile(save, data);
S(T("已签名,结果已保存:", "Signed, results saved: ") + save);
}
/// <summary>验证签名</summary>
static void execVerify() {
string binPath = loadSrcFile + ".sign.bin";
S(T("密钥文件:", "Key file: ") + loadKeyFile);
S(T("目标文件:", "Target file: ") + loadSrcFile);
S(T("签名文件:", "Signature file: ") + binPath);
S(T("签名模式:", "Signature mode: ") + signType + " | " + RSA_Util.RSAPadding_Sign(signType));
byte[] data = ReadFile(binPath);
ST("正在验证签名...", "Verifying signature...");
RSA_Util rsa = new RSA_Util(loadKey);
bool val = rsa.Verify(signType, data, loadSrcBytes);
if (val) {
ST("签名验证成功。", "Signature verification successful.");
return;
}
throw new Exception(T("签名验证失败!", "Signature verification failed!"));
}
/// <summary>调用openssl相关测试代码</summary>
static void runOpenSSL(RSA_Util rsa, byte[] data) {
var shell = "/bin/bash";
if (NET_IsWindows()) {
shell = "cmd";
}
S(T("正在打开OpenSSL...", "Opening OpenSSL...") + " Shell: " + shell);
closeOpenSSL();
openSSLProc = new Process();
ProcessStartInfo info = openSSLProc.StartInfo;
info.FileName = shell;
info.UseShellExecute = false;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.RedirectStandardOutput = true;
info.CreateNoWindow = true;
openSSLProc.Start();
openSSLBuffer = new StringBuilder();
openSSLErrBuffer = new StringBuilder();
var threadSync = openSSLThreadSync;
openSSLThread1 = new Thread(() => {
try {
while (openSSLThreadSync == threadSync) {
var line = openSSLProc.StandardOutput.ReadLine();
if (line != null) {
openSSLBuffer.Append(line).Append('\n');
}
}
} catch { }
});
openSSLThread2 = new Thread(() => {
try {
while (openSSLThreadSync == threadSync) {
var line = openSSLProc.StandardError.ReadLine();
if (line != null) {
openSSLErrBuffer.Append(line).Append('\n');
}
}
} catch { }
});
openSSLThread1.Start();
openSSLThread2.Start();
WriteFile("test_openssl_key.pem", Encoding.UTF8.GetBytes(rsa.ToPEM(false).ToPEM_PKCS8(false)));
WriteFile("test_openssl_data.txt", data);
byte[] no = new byte[rsa.KeySize / 8];
Array.Copy(data, 0, no, no.Length - data.Length, data.Length);
WriteFile("test_openssl_data.txt.nopadding.txt", no);
openSSLProc.StandardInput.Write("openssl version\necho " + openSSLBoundary + "\n");
openSSLProc.StandardInput.Flush();
while (true) {
if (openSSLBuffer.ToString().IndexOf(openSSLBoundary) != -1) {
if (openSSLErrBuffer.Length > 0) {
closeOpenSSL();
throw new Exception(T("打开OpenSSL出错:", "Error opening OpenSSL: ") + openSSLErrBuffer.ToString().Trim());
}
S("OpenSSL Version: " + openSSLBuffer.ToString().Trim());
break;
}
Thread.Sleep(10);
}
}
static private Process openSSLProc;
static private StringBuilder openSSLBuffer, openSSLErrBuffer;
static private Thread openSSLThread1, openSSLThread2;
static private int openSSLThreadSync;
static private readonly string openSSLBoundary = "--openSSL boundary--";
static void closeOpenSSL() {
openSSLThreadSync++;
if (openSSLProc == null) return;
try {
openSSLProc.Kill();
openSSLProc.Dispose();
} catch { }
openSSLProc = null;
}
static byte[] testOpenSSL(bool encOrSign, string mode) {
bool debug = false; string cmd = "";
string keyFile = "test_openssl_key.pem", txtFile = "test_openssl_data.txt";