From 95ece9749de2032f24bfd52fc4672d3585dd1c7e Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Tue, 17 Aug 2021 21:35:49 -0400 Subject: [PATCH 01/52] Create README.md --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..31606a5 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# Org.Security.Cryptography.X509Extensions +`X509Certificate2` Extensions for Encrypting and Signing using X509 certs. + +# Getting started + +- Clone or download the repo +- Compile Org.Security.Cryptography.X509Extensions.csproj to get the assembly. +- Refer the assembly in your project. + +## Usage (Encryption) + + ```C# + var x509Certificate = GetCertificateUsingYourWay(); // This certificate doesn't need to have private key. + Stream yourStreamToEncrypt = GetYourStreamToEncrypt(); + var encryptedStream = new MemoryStream(); + x509Certificate.EncryptStream(yourStreamToEncrypt,encryptedStream); + ``` + +## Usage (Decryption) + + ```C# + var x509Certificate = GetCertificateWithPrivateKeyUsingYourWay(); + Stream yourStreamToDecrypt = GetYourStreamToDecrypt(); + var decryptedStream = new MemoryStream(); + x509Certificate.DecryptStream(yourStreamToDecrypt, decryptedStream); + ``` From 5a2b6ec122f95b4dfb5023b7937a70ad2f042056 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Wed, 18 Aug 2021 18:20:54 -0400 Subject: [PATCH 02/52] Added test certificates into test project and added tests and some refactoring. --- .gitignore | 1 - src/UnitTests/MyConfig.cs | 5 ++ .../TestCertificates/hello.world.2048.net.cer | Bin 0 -> 829 bytes .../TestCertificates/hello.world.2048.net.pfx | Bin 0 -> 2678 bytes src/UnitTests/UnitTests.csproj | 10 +++ src/UnitTests/X509EncryptionTests.cs | 58 +++++++++++------- .../CertificateLoader.cs | 17 +++++ .../TestDataGenerator.cs | 29 +++++++++ src/X509.EnduranceTest.Shared/TestMain.cs | 24 +------- 9 files changed, 99 insertions(+), 45 deletions(-) create mode 100644 src/UnitTests/TestCertificates/hello.world.2048.net.cer create mode 100644 src/UnitTests/TestCertificates/hello.world.2048.net.pfx create mode 100644 src/X509.EnduranceTest.Shared/CertificateLoader.cs create mode 100644 src/X509.EnduranceTest.Shared/TestDataGenerator.cs diff --git a/.gitignore b/.gitignore index dfcfd56..1000b0d 100644 --- a/.gitignore +++ b/.gitignore @@ -226,7 +226,6 @@ ClientBin/ *.dbmdl *.dbproj.schemaview *.jfm -*.pfx *.publishsettings orleans.codegen.cs diff --git a/src/UnitTests/MyConfig.cs b/src/UnitTests/MyConfig.cs index 467d930..5b79d23 100644 --- a/src/UnitTests/MyConfig.cs +++ b/src/UnitTests/MyConfig.cs @@ -9,5 +9,10 @@ internal static class MyConfig internal static string TestCertThumbPrint => ConfigurationManager.AppSettings["X509.ThumbPrint"] ?? throw new Exception($"AppSetting 'X509.ThumbPrint' not defined."); + /// + /// Hardcoded password of test certificate files that are inside /TestCertificates folder + /// + /// Never hard code passwords in the code like this. This is just test + internal static string TestCertficatePassword => "MyP@ssw0rd"; } } diff --git a/src/UnitTests/TestCertificates/hello.world.2048.net.cer b/src/UnitTests/TestCertificates/hello.world.2048.net.cer new file mode 100644 index 0000000000000000000000000000000000000000..0dd3a0d1ca3558ec921efd211a55ee1cd75b4554 GIT binary patch literal 829 zcmXqLVzxABVp3ed%*4pVBv94T*BaKkc)N%HiWQfwznw7PW#iOp^Jx3d%gD&h%3vUG zC~F|i#vIDR%p;PKnv;{SSDs&#lcHy2U}B+{ms(;VC(dhRXkcMzZeU^n1W`b)F%p-s z0Zoib$d)p)GB7tW@-rATF>*0AF)}h7xT3XU@q(QUikS|9-WqHNM1!7XYS&%R+v;$& zitk2Lb2YD|JF9uFfwsi*U8}=le0DhN2masqK;YB)Qt?YmPgLAEB>Lc5@WuNVxEne> zyA^*4uIEe?Wl63*oS6{*uug81;5F{L=GKXcN;0|^G=n)V#3e>N&&^5Z?|oEQKGS2a z*}7LJqQ32#(>rC7d8_63<9UtiuUXD2NqXb-TE#Rs!OC^1Cu{ZIcq{oV^Tc)A4^2q) zw0ov6r=%P4n2A?v?Im;J>Y#&Diq8tTRv3vNS?e%?XRhIFwu+aG;r1L8IUn)HJ@IQe z(Av0n_Z0h0&ODnoh88xSQ1Uoob#msjTc4t~#Pek`F*7nSE-p04H{b)tgDgKI<9`+w zW+v7J2C^W&DvOwb2pfks8zU<#J2MlU#b_W8k``o-d@N!tA{CPtcCcUi zxbbAS(=4NR+6{@?#OaQ&P` zN8NbDCHPiwu=KND+x3{M`P*i}+rOtq-f$BBSkB17QvB}OIlHHsORp63oSe3C&zC>9 z#O`sqHLfzRx$O~WxaY>l<;u5XQUjaoCbZ@mt97{xJmgs0lRxd^jrSf8*Os`t?SJs4 z??Hdo`n8Xa&psZ(Gv(j^3*~NAO%tYbc}$<>xp99AgQ`dRoad+HPWtAk`}_FEuzcoM zwBMu=v1Uf3MMiw-)#d%7(Hmp8yuRKV&A0tGv)A1dPvZoOxW7s99lp}{VKMhVA`K-fw=NB0Ep4H}PFJArJu1aXj<@ literal 0 HcmV?d00001 diff --git a/src/UnitTests/TestCertificates/hello.world.2048.net.pfx b/src/UnitTests/TestCertificates/hello.world.2048.net.pfx new file mode 100644 index 0000000000000000000000000000000000000000..3f844f6d8848c3bc366fa7624852f8d0b58498ea GIT binary patch literal 2678 zcmZXUcT^L|7RHkh0t7@h^s;mjK``_ts3Zc?i_)9)js%n@f*~NiNtGhftCST%q)L|> zM0&452-VQbi=KVE=bd-v%*;K%@7{0buMdufqCh|rI2tMpp%4gF2|c_5Bm-unp&Vc| zl$D4X;b;ipepa?^mrv{U6nraU@ zktSh4AhIDa8a!Sy52LkX@(Go! zK7qQd)PeOlfuC3mr?=Gg`e(GKvx|LC7Uf&ypIW7gXlh@Qf(0L~w|4kjQ7chbSLI*r z#i;5pPH@PRFxSg{Z+;yd+zBcw^QPmPR4)yDPvudVP`re=%aJl4q7t{--yz50wE zM=vuXb;EnAvD%4fwv;~A1uX)HbZ_i!Y^+-bL7p9PLXSg?7=0PIsTPaXu6FUd{UCtN zLMW^64mA3tYAl?c%kO;dx(#V-A~%{5IkSw1#_-Xi46< zxq2}m+~a84gG)XNWZjBAm-MzM;~K9~Wll(zU8%KQ8v{~?glEJCfeK{nIUF*qJ77R^ zr5hzhDMz{?RiCu`m7~OebgpdAHO>wF2t0OUaL7(xDsNizAao+&==58`B|V!KGzfd!1=xT&2l^LyyoWZKZ*mZ^J}Tz&OP;7 zjMLjz&#ZVz$1LDu-)l4qu*oGjd!x8(d-og57pGU(#_$t%Njg%N;bi<~ZMDj`*6=@7 z$H8O|B;13pFIY+Q0%pz>S#CFGj!#t_4b{1_ z2hzdruHQir)zp@U`JQ9-@|^aHrq|LpbDM4dWLh!dpMH zYE(G18OPXF$+~~2+n-WA&_a%l4$|; z5W-0N{e%F#CR{`c{#qUShA}M#mhT`oJG&5T^#T(L}U9?6^1q4bf?Cl z_|%agfW|dA16()#nE<)TXMUa1g~@n^uxflsB=e$jcG(OPT$nr9uirwkqrov3Y3HwV zaXu zmfkCZEw50?Wp+eQ)j2Pj8BGPs#th5`O%8L0>dcZ`%1ZBScBVTpx2jcaHj8d(`_lw! z)`)wf66}tq9A=kOQ2_z?Zgyvxmu<$zVvWNvzIS%eCALjw7(qax#Q7 zbiowh$pcCDU<#-N#duguI(eRyYT-rvCD|>Eh-QM>ZsbW+_iFw`)U}l=t=?1nl+SqS zyp<;%S0UqA3WT`1u%{qIxFg`v)C}f~aUu;f%W}iO_Z@29Ub2vNJAi~DGVgs6zaFi~ z%WrVnf8vsYUIYRL0|5X4-0WXSC!)=+3Gf2g0)zmP02zQ4Knx%SuqIX#L?Q*SAu5lF zl{mnTNa4gvhR8&JGfCoYMWhnM9y=lzAue&i<6riI5Fi+cmihzu+g=0UNNhs^+yS1% zZAW~L6QK8ZmvNC0RKPu^ZTBXs43Kn{tEseG)Z?iP^42a!1iN>YknIV-;pN*Q_ugPSOVi= zO-rWq+23;Lc`joLt8zsvq_>9^aME8JxmlzPHA@xHoI--W1IymII5hG*2Ju(}X>iT(yT`#k8`G*8arU0?zK z@z)vlpAp;wC4yDHo^|_m7nBx{4pt-D6X&rUyiKKx%i(J^y=h@H*&xBV)v!-n3r=+B zUz=$Zl4A@;RuF7}`dHUPK;OtE)Z9YFNASvl-N~Xhp0alI z$OYwWC`=^ZmdiMB4VL zqS3Z+WS&!n<0+H>jDJ&bw77k__ve$ywTChHPci$R>+MiPCaYmq=wr{k?KN2XS03A> z9n~p6XZ9uE+c}zVCVQKos+IoCnplo)m5J#uj2m^M^4LJ^=$OUYjl_Q&u~!P_wPJcu z=z!Zg)|LQMDwL}D8E#15NU58rlrS%$I~B+D;=Db(@QeJzuTCPB>O9bVNTYb{S$9~# zH8g=@c1mp0#U`hfj)S5s$u9g24;`H6lD7NoCdvj5(l_nz2#-E*`M+TY!AyLHpX~&-H%w(j5JzZXbfuK3^RL_Wb+7> z4A&`N*4(#(7*MtLvC7~dIqos!ww-cq2TCO(Uu0QZVy>tdhSku_Kd*apI$gt+TWF{Q z6N(`Z(^KK3USb!ObFa+#5^_$5(4gc~U?j{rm&%8X9)vloO3I`xF&#F|vJZw&FQ@Wf zQ5Sz98bV0Hdg=*9PRDbUCh(;^S~h9n{@}^xGJF0GvfG?9yk_yFO+&i^v9112P00Jf z{YKpTy@f(cTQ`}k8N5<}WsqmM)awweW%BwtY+d>O#m;+_oHA0ZW!=%ba)Si{UuLbb zzAIL#6crl$L$1M?!8>Tu+7u211i z#=4gJg2;2jvCj{~Di`IkoC%(}?MiP3YhE4P_ri59+R{)>x@63g(O)qShMWLzAv;~U zN4%f4hzmPBgUi7=;A9X|L242Z3z*JLyBaYX%v$`J#;3^C_Ahczs<$$6hPt U{=Nmr4kXFl%-!|O<6lAWAA+*ge*gdg literal 0 HcmV?d00001 diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index a4bb841..f04983e 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -16,6 +16,16 @@ + + + + + + Always + + + Always + + - + --> - + From a81fc0abecc9a677d28d9793e9d5fe9ea0e79bc2 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 19 Aug 2021 16:39:58 -0400 Subject: [PATCH 14/52] Added coverlet.msbuild to generate the coverage report. Excluded the shared test assembly and made 100% coverage requirement --- .github/workflows/dotnet.yml | 2 +- .gitignore | 2 +- src/UnitTests/UnitTests.csproj | 4 ++++ src/X509.EnduranceTest.Shared/X509CertificateCache.cs | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 335b465..ac7898a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -24,4 +24,4 @@ jobs: - name: Build Tests run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - name: Test - run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=100 /p:ThresholdType=line --verbosity normal + run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=100 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1000b0d..c49d5b6 100644 --- a/.gitignore +++ b/.gitignore @@ -140,7 +140,7 @@ _TeamCity* # Visual Studio code coverage results *.coverage *.coveragexml - +*.opencover.xml # NCrunch _NCrunch_* .*crunch*.local.xml diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 79e3b3c..7ba7ee4 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -7,6 +7,10 @@ + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/X509.EnduranceTest.Shared/X509CertificateCache.cs b/src/X509.EnduranceTest.Shared/X509CertificateCache.cs index f67cc87..e214ee1 100644 --- a/src/X509.EnduranceTest.Shared/X509CertificateCache.cs +++ b/src/X509.EnduranceTest.Shared/X509CertificateCache.cs @@ -42,7 +42,7 @@ using System.Linq; using System.Security.Cryptography.X509Certificates; -namespace Org.Security.Cryptography +namespace X509.EnduranceTest.Shared { /// /// Per-thread-cache of X509Certificate2 instances, identified by StoreName, StoreLocation, Thumbprint and an optional cache name. From 6fca5aaf3dfe1fba1518e8dfb983d5c76c1ee3a5 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 19 Aug 2021 17:32:24 -0400 Subject: [PATCH 15/52] GH Actions - added step to upload coverage report to codecov.io --- .github/workflows/dotnet.yml | 9 +++++++-- .gitignore | 4 ++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ac7898a..2f6d44e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -23,5 +23,10 @@ jobs: run: dotnet build src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj --no-restore - name: Build Tests run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - - name: Test - run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=100 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal \ No newline at end of file + - name: Test With Code Coverage + run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=100 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal + - name: UploadToCodeCov + uses: codecov/codecov-action@v2 + with: + files: src/UnitTests/coverage.Net5.0.opencover.xml + verbose: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index c49d5b6..33583d1 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,10 @@ project.lock.json project.fragment.lock.json artifacts/ +#donet tools + +tools +.config # StyleCop StyleCopReport.xml From dff0b944af3b5a8eff055eef878e87db8ded9ce2 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 19 Aug 2021 17:51:06 -0400 Subject: [PATCH 16/52] GH Actions - Reduced the coverage threshold temporarily to get the report uploaded to codecov. Updated readme.md --- .github/workflows/dotnet.yml | 2 +- README.md | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 2f6d44e..ceaaedb 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -24,7 +24,7 @@ jobs: - name: Build Tests run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - name: Test With Code Coverage - run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=100 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal + run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=50 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal - name: UploadToCodeCov uses: codecov/codecov-action@v2 with: diff --git a/README.md b/README.md index 31606a5..b7ec2e2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +[![codecov](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions/branch/master/graph/badge.svg?token=AS2FV3ACUI)](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions) + # Org.Security.Cryptography.X509Extensions `X509Certificate2` Extensions for Encrypting and Signing using X509 certs. @@ -24,3 +26,21 @@ var decryptedStream = new MemoryStream(); x509Certificate.DecryptStream(yourStreamToDecrypt, decryptedStream); ``` + +# Running tests + +Use `dotnet test` command or use the "Test Explorer" windows of Visual Studio. + +In order to view coverage, use any of the below methods. + +## Commandline + +Below command has codecoverage threshold 100. It will fail as of now. + +`dotnet test "src/UnitTests/UnitTests.csproj" --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=100 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*"` + +It is excluding the shared test library. + +## Visual Studio + +Use the "Run Coverlet Report" extension as mentioned [here](https://www.code4it.dev/blog/code-coverage-vs-2019-coverlet) \ No newline at end of file From 0f153b332bff0a2bdb986b907a71f6df3441274c Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 19 Aug 2021 18:37:27 -0400 Subject: [PATCH 17/52] Test Coverage - added tests and refactoring --- .../X509CertificateBasedDecryptor.cs | 26 +++++---- .../X509CertificateBasedEncryptor.cs | 21 ++++++- ...sedDecryptor_DecryptBase64EncodedString.cs | 55 +++++++++++++++++++ ...CertificateBasedDecryptor_DecryptStream.cs | 49 +++++++++++++++++ ...teBasedEncryptor_EncryptStringToBase64.cs} | 8 +-- ...ptor_EncryptStringToBase64WithTimestamp.cs | 27 +++++++++ 6 files changed, 170 insertions(+), 16 deletions(-) create mode 100644 src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs create mode 100644 src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs rename src/UnitTests/{X509CertificateBasedEncryptor_EncryptStringToBase64EncodedString.cs => X509CertificateBasedEncryptor_EncryptStringToBase64.cs} (91%) create mode 100644 src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs index 542f4c9..e127ac5 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs @@ -16,11 +16,7 @@ public void DecryptStream( string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName) { ValidateDecryptParamsAndThrowException( inputStream, outputStream, dataEncryptionAlgorithmName); - - var thumprintArray = inputStream.ReadLengthAndBytes(maxBytes: 2048); - var certificateThumbprint = Encoding.UTF8.GetString(thumprintArray); - var certificateForKeyEncryption = certificateSelector(certificateThumbprint); - if (null == certificateForKeyEncryption) throw new ArgumentNullException("The certificate cannot be null"); + var certificateForKeyEncryption = GetCertificateFromStream(inputStream, certificateSelector); certificateForKeyEncryption.DecryptStreamWithTimestampValidation(inputStream, outputStream, false, dataEncryptionAlgorithmName); } @@ -62,12 +58,11 @@ public void DecryptStreamWithTimestampValidation( { ValidateDecryptParamsAndThrowException(inputStream, outputStream, dataEncryptionAlgorithmName); - var thumprintArray = inputStream.ReadLengthAndBytes(maxBytes: 2048); - var certificateThumbprint = Encoding.UTF8.GetString(thumprintArray); - var certificateForKeyEncryption = certificateSelector(certificateThumbprint); - if (null == certificateForKeyEncryption) throw new ArgumentNullException("The certificate cannot be null"); + X509Certificate2 certificateForKeyEncryption = GetCertificateFromStream(inputStream, certificateSelector); + certificateForKeyEncryption.DecryptStreamWithTimestampValidation(inputStream, outputStream, true, lifeSpanOfInput, dataEncryptionAlgorithmName); } + public string DecryptBase64EncodedStringWithTimestampValidation( string valueToDecode, Func certificateSelector) @@ -76,18 +71,29 @@ public string DecryptBase64EncodedStringWithTimestampValidation( using (var input = new MemoryStream(inputData)) using (var output = new MemoryStream(inputData.Length)) { - this.DecryptStream(input, output, certificateSelector); + this.DecryptStreamWithTimestampValidation(input, output, certificateSelector); output.Flush(); var outputArray = output.ToArray(); return Encoding.UTF8.GetString(outputArray); } } #endregion + + #region Private helpers + private static X509Certificate2 GetCertificateFromStream(Stream inputStream, Func certificateSelector) + { + var thumprintArray = inputStream.ReadLengthAndBytes(maxBytes: 2048); + var certificateThumbprint = Encoding.UTF8.GetString(thumprintArray); + var certificateForKeyEncryption = certificateSelector(certificateThumbprint); + if (null == certificateForKeyEncryption) throw new ArgumentNullException("The certificate cannot be null"); + return certificateForKeyEncryption; + } private static void ValidateDecryptParamsAndThrowException(Stream inputStream, Stream outputStream, string dataEncryptionAlgorithmName) { if (null == inputStream) throw new ArgumentNullException(nameof(inputStream)); if (null == outputStream) throw new ArgumentNullException(nameof(outputStream)); if (null == dataEncryptionAlgorithmName) throw new ArgumentNullException(nameof(dataEncryptionAlgorithmName)); } + #endregion } } \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index 6fe7145..8f369e6 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -31,7 +31,7 @@ public void EncryptStreamWithTimestamp( outputStream.WriteLengthAndBytes(thumbprintArray); x509Cert.EncryptStream(inputStream, outputStream, true, dataEncryptionAlgorithmName, keySize, blockSize); } - #endregion + /// /// Encrypt input stream using the symmetric algorithm provided. /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given certificate. @@ -71,7 +71,7 @@ public void EncryptStream(X509Certificate2 x509Cert, /// The thumbprint of the certificate is attached as first thing in the encrypted data. /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. /// - public string EncryptStringToBase64EncodedString(X509Certificate2 x509Cert, + public string EncryptStringToBase64(X509Certificate2 x509Cert, string valueToEncode, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, int keySize = Defaults.DEF_KeySize, @@ -87,5 +87,22 @@ public string EncryptStringToBase64EncodedString(X509Certificate2 x509Cert, return Convert.ToBase64String(outputArray); } } + public string EncryptStringToBase64WithTimestamp(X509Certificate2 x509Cert, + string valueToEncode, + string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, + int keySize = Defaults.DEF_KeySize, + int blockSize = Defaults.DEF_BlockSize) + { + var inputData = Encoding.UTF8.GetBytes(valueToEncode); + using (var input = new MemoryStream(inputData)) + using (var output = new MemoryStream(inputData.Length)) + { + this.EncryptStreamWithTimestamp(x509Cert, input, output, dataEncryptionAlgorithmName, keySize, blockSize); + output.Flush(); + var outputArray = output.ToArray(); + return Convert.ToBase64String(outputArray); + } + } } + #endregion } \ No newline at end of file diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs new file mode 100644 index 0000000..f6271cc --- /dev/null +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs @@ -0,0 +1,55 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Org.Security.Cryptography; +using X509.EnduranceTest.Shared; + +namespace UnitTests +{ + [TestClass] + public class X509CertificateBasedDecryptor_DecryptBase64EncodedString + { + [TestMethod] + public void WhenItIsCalledWithProperParameters_ShouldEncrypt() + { + //Arrange + const string TEST = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + //Act + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, TEST); + var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedString( + encryptedBase64, + thumbprint => x509DecryptionCert); + //Assert + Assert.IsTrue(TEST.Equals(decryptedOutput)); + } + #region Encrypted contentSize tests + [TestMethod] + public void WhenSigleLetterAIsEncrypted_ResultSizeWillBe536() + { + //Arrange + const string TEST = "A"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + //Act + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, TEST); + //Assert + int expectedEncryptedArraySize = 776; + Assert.AreEqual(expectedEncryptedArraySize, encryptedBase64.Length, $"Expected encrypted size for letter A is {expectedEncryptedArraySize}, but actual {encryptedBase64.Length}"); + } + [TestMethod] + public void WhenPersonFullNameJoyGeorgeKunjikkuruIsEncrypted_ResultSizeWillBe552() + { + //Arrange + const string input = "JoyGeorgeKunjikkuru"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + //Act + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, input); + //Assert + int expectedEncryptedArraySize = 796; + Assert.AreEqual(expectedEncryptedArraySize, encryptedBase64.Length, $"Expected encrypted size for letter A is {expectedEncryptedArraySize}, but actual {encryptedBase64.Length}"); + + } + #endregion + + } +} + diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs new file mode 100644 index 0000000..6c19aea --- /dev/null +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs @@ -0,0 +1,49 @@ + +using System; +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Org.Security.Cryptography; +using X509.EnduranceTest.Shared; + +namespace UnitTests +{ + [TestClass] + public class X509CertificateBasedDecryptor_DecryptStream + { + #region Validation tests + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheInputStreamParameterIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + new X509CertificateBasedDecryptor().DecryptStream(null, new MemoryStream(), + thumbprint => null); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheDecryptionCertificateIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + byte[] encryptedArray = EncryptionDecryptionUtils.EncryptBytesUsingX509CertificateBasedEncryptor(x509EncryptionCert, input); + byte[] decryptedOutput = EncryptionDecryptionUtils.DecryptBytesUsingX509CertificateBasedDecryptor( + encryptedArray, + thumbprint => null); + //Assert + Assert.IsTrue(input.SequenceEqual(decryptedOutput)); + } + #endregion + } +} \ No newline at end of file diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64EncodedString.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs similarity index 91% rename from src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64EncodedString.cs rename to src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs index 3a19688..c01cca7 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64EncodedString.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs @@ -5,7 +5,7 @@ namespace UnitTests { [TestClass] - public class X509CertificateBasedEncryptor_EncryptStringToBase64EncodedString + public class X509CertificateBasedEncryptor_EncryptStringToBase64 { [TestMethod] public void WhenItIsCalledWithProperParameters_ShouldEncrypt() @@ -15,7 +15,7 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); //Act - var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64EncodedString(x509EncryptionCert, TEST); + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, TEST); var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedString( encryptedBase64, thumbprint => x509DecryptionCert); @@ -30,7 +30,7 @@ public void WhenSigleLetterAIsEncrypted_ResultSizeWillBe536() const string TEST = "A"; var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); //Act - var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64EncodedString(x509EncryptionCert, TEST); + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, TEST); //Assert int expectedEncryptedArraySize = 776; Assert.AreEqual(expectedEncryptedArraySize, encryptedBase64.Length, $"Expected encrypted size for letter A is {expectedEncryptedArraySize}, but actual {encryptedBase64.Length}"); @@ -42,7 +42,7 @@ public void WhenPersonFullNameJoyGeorgeKunjikkuruIsEncrypted_ResultSizeWillBe552 const string input = "JoyGeorgeKunjikkuru"; var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); //Act - var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64EncodedString(x509EncryptionCert, input); + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, input); //Assert int expectedEncryptedArraySize = 796; Assert.AreEqual(expectedEncryptedArraySize, encryptedBase64.Length, $"Expected encrypted size for letter A is {expectedEncryptedArraySize}, but actual {encryptedBase64.Length}"); diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs new file mode 100644 index 0000000..c13f205 --- /dev/null +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs @@ -0,0 +1,27 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Org.Security.Cryptography; +using X509.EnduranceTest.Shared; + +namespace UnitTests +{ + [TestClass] + public class X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp + { + [TestMethod] + public void WhenItIsCalledWithProperParameters_ShouldEncrypt() + { + //Arrange + const string input = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + //Act + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64WithTimestamp(x509EncryptionCert, input); + var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedStringWithTimestampValidation( + encryptedBase64, + thumbprint => x509DecryptionCert); + //Assert + Assert.IsTrue(input.Equals(decryptedOutput)); + } + } +} + From 2098cb84b490daad05df854617c5c3f640fbf804 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 09:33:14 -0400 Subject: [PATCH 18/52] Test coverage - Added tests and removed unreachable code. Added runsettings file --- .github/workflows/dotnet.yml | 2 +- .../X509AsymmetricAlgorithmExtensions.cs | 5 +- ...Certificate2StreamDecryptionExtensions.cs} | 2 +- ...Certificate2StreamEncryptionExtensions.cs} | 2 +- .../X509CertificateBasedDecryptor.cs | 23 +++++---- .../X509CertificateBasedEncryptor.cs | 24 ++++++--- .../X509Certificate2_EncryptStream.cs | 50 ++++++++++++++++++- ...e64EncodedStringWithTimestampValidation.cs | 27 ++++++++++ ...CertificateBasedDecryptor_DecryptStream.cs | 50 ++++++++++++++++++- ...CertificateBasedEncryptor_EncryptStream.cs | 38 ++++++++++++++ src/UnitTests/test.runsettings | 27 ++++++++++ 11 files changed, 223 insertions(+), 27 deletions(-) rename src/Org.Security.Cryptography.X509Extensions/{X509StreamDecryptionExtensions.cs => X509Certificate2StreamDecryptionExtensions.cs} (98%) rename src/Org.Security.Cryptography.X509Extensions/{X509StreamEncryptionExtensions.cs => X509Certificate2StreamEncryptionExtensions.cs} (98%) create mode 100644 src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs create mode 100644 src/UnitTests/test.runsettings diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ceaaedb..44aa80b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -24,7 +24,7 @@ jobs: - name: Build Tests run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - name: Test With Code Coverage - run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=50 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal + run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=80 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal - name: UploadToCodeCov uses: codecov/codecov-action@v2 with: diff --git a/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs index 7795c2a..3490e3c 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs @@ -12,9 +12,8 @@ internal static class X509AsymmetricAlgorithmExtensions /// internal static AsymmetricAlgorithm GetPublicKeyAsymmetricAlgorithm(this X509Certificate2 x509Cert) { - if (null == x509Cert) throw new ArgumentNullException(nameof(x509Cert)); + // Not sure what scenario the thumnprint will be null. If the cert is loaded it would have thumbprint if (null == x509Cert.Thumbprint) throw new ArgumentNullException("X509Certificate2.Thumbprint was NULL."); - try { try @@ -24,7 +23,7 @@ internal static AsymmetricAlgorithm GetPublicKeyAsymmetricAlgorithm(this X509Cer } catch (CryptographicException) { - // [SLOWER] + // [SLOWER] - Seems rare scenario return x509Cert.GetRSAPublicKey() ?? throw new Exception($"X509Certificate2.GetRSAPublicKey() returned NULL"); } } diff --git a/src/Org.Security.Cryptography.X509Extensions/X509StreamDecryptionExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs similarity index 98% rename from src/Org.Security.Cryptography.X509Extensions/X509StreamDecryptionExtensions.cs rename to src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs index d53a724..208515b 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509StreamDecryptionExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs @@ -6,7 +6,7 @@ namespace Org.Security.Cryptography { - public static class X509StreamDecryptionExtensions + public static class X509Certificate2StreamDecryptionExtensions { #region Public /// diff --git a/src/Org.Security.Cryptography.X509Extensions/X509StreamEncryptionExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamEncryptionExtensions.cs similarity index 98% rename from src/Org.Security.Cryptography.X509Extensions/X509StreamEncryptionExtensions.cs rename to src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamEncryptionExtensions.cs index 5e37c08..ad9477f 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509StreamEncryptionExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamEncryptionExtensions.cs @@ -11,7 +11,7 @@ namespace Org.Security.Cryptography /// Extensions to encrypt/decrypt Streams using X509 Certificates. /// DEFAULT: AES-256/128 for Data encryption. /// - public static class X509StreamEncryptionExtensions + public static class X509Certificate2StreamEncryptionExtensions { #region Public /// diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs index e127ac5..70986e6 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs @@ -7,7 +7,7 @@ namespace Org.Security.Cryptography { public class X509CertificateBasedDecryptor { - + #region public public void DecryptStream( Stream inputStream, @@ -15,11 +15,11 @@ public void DecryptStream( Func certificateSelector, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName) { - ValidateDecryptParamsAndThrowException( inputStream, outputStream, dataEncryptionAlgorithmName); + ValidateDecryptParamsAndThrowException(inputStream, outputStream, dataEncryptionAlgorithmName, certificateSelector); var certificateForKeyEncryption = GetCertificateFromStream(inputStream, certificateSelector); certificateForKeyEncryption.DecryptStreamWithTimestampValidation(inputStream, outputStream, false, dataEncryptionAlgorithmName); } - + public string DecryptBase64EncodedString( string valueToDecode, Func certificateSelector) @@ -28,7 +28,7 @@ public string DecryptBase64EncodedString( using (var input = new MemoryStream(inputData)) using (var output = new MemoryStream(inputData.Length)) { - this.DecryptStream( input, output,certificateSelector); + this.DecryptStream(input, output, certificateSelector); output.Flush(); var outputArray = output.ToArray(); return Encoding.UTF8.GetString(outputArray); @@ -42,10 +42,10 @@ public void DecryptStreamWithTimestampValidation( ) { this.DecryptStreamWithTimestampValidation( - inputStream, - outputStream, - certificateSelector, - TimeSpan.FromMinutes(1), + inputStream, + outputStream, + certificateSelector, + TimeSpan.FromMinutes(1), dataEncryptionAlgorithmName); } public void DecryptStreamWithTimestampValidation( @@ -56,10 +56,10 @@ public void DecryptStreamWithTimestampValidation( string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName ) { - ValidateDecryptParamsAndThrowException(inputStream, outputStream, dataEncryptionAlgorithmName); + ValidateDecryptParamsAndThrowException(inputStream, outputStream, dataEncryptionAlgorithmName, certificateSelector); X509Certificate2 certificateForKeyEncryption = GetCertificateFromStream(inputStream, certificateSelector); - + certificateForKeyEncryption.DecryptStreamWithTimestampValidation(inputStream, outputStream, true, lifeSpanOfInput, dataEncryptionAlgorithmName); } @@ -88,8 +88,9 @@ private static X509Certificate2 GetCertificateFromStream(Stream inputStream, Fun if (null == certificateForKeyEncryption) throw new ArgumentNullException("The certificate cannot be null"); return certificateForKeyEncryption; } - private static void ValidateDecryptParamsAndThrowException(Stream inputStream, Stream outputStream, string dataEncryptionAlgorithmName) + private static void ValidateDecryptParamsAndThrowException(Stream inputStream, Stream outputStream, string dataEncryptionAlgorithmName, Func certificateSelector) { + if (null == certificateSelector) throw new ArgumentNullException(nameof(certificateSelector)); if (null == inputStream) throw new ArgumentNullException(nameof(inputStream)); if (null == outputStream) throw new ArgumentNullException(nameof(outputStream)); if (null == dataEncryptionAlgorithmName) throw new ArgumentNullException(nameof(dataEncryptionAlgorithmName)); diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index 8f369e6..e177945 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -21,17 +21,16 @@ public class X509CertificateBasedEncryptor /// public void EncryptStreamWithTimestamp( X509Certificate2 x509Cert, - MemoryStream inputStream, - MemoryStream outputStream, + Stream inputStream, + Stream outputStream, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, int keySize = Defaults.DEF_KeySize, int blockSize = Defaults.DEF_BlockSize) { - var thumbprintArray = Encoding.UTF8.GetBytes(x509Cert.Thumbprint); - outputStream.WriteLengthAndBytes(thumbprintArray); + ValidateAndWritePlainThumprintToOutputStream(x509Cert, outputStream); x509Cert.EncryptStream(inputStream, outputStream, true, dataEncryptionAlgorithmName, keySize, blockSize); } - + /// /// Encrypt input stream using the symmetric algorithm provided. /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given certificate. @@ -53,8 +52,7 @@ public void EncryptStream(X509Certificate2 x509Cert, int keySize = Defaults.DEF_KeySize, int blockSize = Defaults.DEF_BlockSize) { - var thumbprintArray = Encoding.UTF8.GetBytes(x509Cert.Thumbprint); - outputStream.WriteLengthAndBytes(thumbprintArray); + ValidateAndWritePlainThumprintToOutputStream(x509Cert, outputStream); x509Cert.EncryptStream(inputStream, outputStream, false, dataEncryptionAlgorithmName, keySize, blockSize); } /// @@ -103,6 +101,16 @@ public string EncryptStringToBase64WithTimestamp(X509Certificate2 x509Cert, return Convert.ToBase64String(outputArray); } } + + #endregion + + #region Private helpers + private static void ValidateAndWritePlainThumprintToOutputStream(X509Certificate2 x509Cert, Stream outputStream) + { + if (null == x509Cert) throw new ArgumentNullException(nameof(x509Cert)); + var thumbprintArray = Encoding.UTF8.GetBytes(x509Cert.Thumbprint); + outputStream.WriteLengthAndBytes(thumbprintArray); + } + #endregion } - #endregion } \ No newline at end of file diff --git a/src/UnitTests/X509Certificate2_EncryptStream.cs b/src/UnitTests/X509Certificate2_EncryptStream.cs index d8e35f5..277f4cd 100644 --- a/src/UnitTests/X509Certificate2_EncryptStream.cs +++ b/src/UnitTests/X509Certificate2_EncryptStream.cs @@ -1,12 +1,59 @@ -using System.Text; +using System.Security.Cryptography.X509Certificates; +using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using X509.EnduranceTest.Shared; +using Org.Security.Cryptography; +using System.IO; +using System; +using System.Security.Cryptography; namespace UnitTests { [TestClass] public class X509Certificate2_EncryptStream { + #region Validation tests + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenEncryptionCertificateIsNull_ThrowsArgumentNullException() + { + //Arrange + X509Certificate2 certificate2 = null; + //Act + certificate2.EncryptStream(new MemoryStream(), new MemoryStream()); + //Assert + } + [TestMethod] + [ExpectedException(typeof(CryptographicException))] + public void WhenEncryptionCertificateIsNotLoaded_ThrowsArgumentNullException() + { + //Arrange + X509Certificate2 certificate2 = new X509Certificate2(); + //Act + certificate2.EncryptStream(new MemoryStream(), new MemoryStream()); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenInputStreamIsNull_ThrowsArgumentNullException() + { + //Arrange + //Act + MyConfig.EncryptionCertificate.EncryptStream(null, new MemoryStream()); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenOutputStreamIsNull_ThrowsArgumentNullException() + { + //Arrange + //Act + MyConfig.EncryptionCertificate.EncryptStream( new MemoryStream(),null); + //Assert + } + #endregion + + #region Encrypted contentSize tests [TestMethod] public void WhenSigleLetterAIsEncrypted_ResultSizeWillBe536() { @@ -47,6 +94,7 @@ public void When8KBIsEncrypted_ResultSizeWillBe8728() int expectedEncryptedArraySize = 8728; Assert.AreEqual(expectedEncryptedArraySize, output1.Length, $"Expected encrypted size for letter A is {expectedEncryptedArraySize}, but actual {output1.Length}"); } + #endregion } } diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs new file mode 100644 index 0000000..46dfcfa --- /dev/null +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs @@ -0,0 +1,27 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Org.Security.Cryptography; +using X509.EnduranceTest.Shared; + +namespace UnitTests +{ + [TestClass] + public class X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation + { + [TestMethod] + public void WhenItIsCalledWithProperParameters_ShouldEncrypt() + { + //Arrange + const string input = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + //Act + var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64WithTimestamp(x509EncryptionCert, input); + var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedStringWithTimestampValidation( + encryptedBase64, + thumbprint => x509DecryptionCert); + //Assert + Assert.IsTrue(input.Equals(decryptedOutput)); + } + } +} + diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs index 6c19aea..a98c343 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs @@ -24,11 +24,59 @@ public void WhenTheInputStreamParameterIsNull_ThrowArgumentNullException() byte[] input = Encoding.UTF8.GetBytes(TEST); //Act new X509CertificateBasedDecryptor().DecryptStream(null, new MemoryStream(), - thumbprint => null); + thumbprint => MyConfig.DecryptionCertificate); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheOutputStreamParameterIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + new X509CertificateBasedDecryptor().DecryptStream( new MemoryStream(), null, + thumbprint => MyConfig.DecryptionCertificate); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheDataEncryptionAlgorithmNameParameterIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + new X509CertificateBasedDecryptor().DecryptStream( + new MemoryStream(), + new MemoryStream(), + thumbprint => MyConfig.DecryptionCertificate, + null); //Assert } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheCertificateSelectorParamIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + byte[] encryptedArray = EncryptionDecryptionUtils.EncryptBytesUsingX509CertificateBasedEncryptor(x509EncryptionCert, input); + byte[] decryptedOutput = EncryptionDecryptionUtils.DecryptBytesUsingX509CertificateBasedDecryptor( + encryptedArray, + null); + //Assert + Assert.IsTrue(input.SequenceEqual(decryptedOutput)); + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] public void WhenTheDecryptionCertificateIsNull_ThrowArgumentNullException() { //Arrange diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs index e737800..4dfd312 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs @@ -1,8 +1,10 @@  using System; +using System.IO; using System.Linq; using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; +using Org.Security.Cryptography; using X509.EnduranceTest.Shared; namespace UnitTests @@ -10,6 +12,41 @@ namespace UnitTests [TestClass] public class X509CertificateBasedEncryptor_EncryptStream { + #region Validations + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenEncryptionCertificateParameterIsNull_ThrowArgumentNullException() + { + //Arrange + //Act + new X509CertificateBasedEncryptor().EncryptStream(null, new MemoryStream(), new MemoryStream()); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheInputStreamParameterIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + new X509CertificateBasedEncryptor().EncryptStream(MyConfig.EncryptionCertificate, null, new MemoryStream()); + //Assert + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenTheOutputStreamParameterIsNull_ThrowArgumentNullException() + { + //Arrange + const string TEST = "Hello World!"; + byte[] input = Encoding.UTF8.GetBytes(TEST); + //Act + new X509CertificateBasedEncryptor().EncryptStream(MyConfig.EncryptionCertificate, new MemoryStream(),null); + //Assert + } + #endregion + + #region Positive / happy scenarios [TestMethod] public void WhenItIsCalledWithProperParameters_ShouldEncrypt() { @@ -26,5 +63,6 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() //Assert Assert.IsTrue(input.SequenceEqual(decryptedOutput)); } + #endregion } } \ No newline at end of file diff --git a/src/UnitTests/test.runsettings b/src/UnitTests/test.runsettings new file mode 100644 index 0000000..c1ee804 --- /dev/null +++ b/src/UnitTests/test.runsettings @@ -0,0 +1,27 @@ + + + + + Net5.0 + + + + + + + + + .*Shared.* + + + + True + True + True + False + + + + + + \ No newline at end of file From 8442a49f48fafc23525b16b30d240f42f4969742 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 10:22:13 -0400 Subject: [PATCH 19/52] Code coverage - Refactored and included only library into the runsettings file --- .../X509CertificateBasedEncryptor.cs | 1 - src/UnitTests/MyConfig.cs | 3 +++ src/UnitTests/X509Certificate2_DecryptStream.cs | 2 +- src/UnitTests/X509Certificate2_DecryptStream_PerfTests.cs | 2 +- src/UnitTests/X509Certificate2_EncryptStream.cs | 2 +- src/UnitTests/X509Certificate2_EncryptStream_PerfTests.cs | 2 +- ...ertificateBasedDecryptor_DecryptBase64EncodedString.cs | 2 +- ...r_DecryptBase64EncodedStringWithTimestampValidation.cs | 2 +- .../X509CertificateBasedDecryptor_DecryptStream.cs | 2 +- ...BasedDecryptor_DecryptStreamWithTimestampValidation.cs | 2 +- .../X509CertificateBasedEncryptor_EncryptStream.cs | 2 +- ...ertificateBasedEncryptor_EncryptStreamWithTimestamp.cs | 2 +- ...X509CertificateBasedEncryptor_EncryptStringToBase64.cs | 2 +- ...teBasedEncryptor_EncryptStringToBase64WithTimestamp.cs | 2 +- src/UnitTests/X509SignatureTests.cs | 2 +- src/UnitTests/test.runsettings | 8 ++++---- 16 files changed, 20 insertions(+), 18 deletions(-) diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index e177945..a388891 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -44,7 +44,6 @@ public void EncryptStreamWithTimestamp( /// The thumbprint of the certificate is attached as first thing in the encrypted data. /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. /// - //TODO: Add the timestamp of encryption to avoid replay attacks if its used to transmit authentication details between client and server. public void EncryptStream(X509Certificate2 x509Cert, Stream inputStream, Stream outputStream, diff --git a/src/UnitTests/MyConfig.cs b/src/UnitTests/MyConfig.cs index 143586e..80cec2b 100644 --- a/src/UnitTests/MyConfig.cs +++ b/src/UnitTests/MyConfig.cs @@ -16,6 +16,9 @@ internal static class MyConfig /// /// Never hard code passwords in the code like this. This is just test internal static string TestCertficatePassword => "MyP@ssw0rd"; + /// + /// TODO: Below certificates expire on 2023-08-17. Need to recreate certificate then to continue using this test project + /// internal static X509Certificate2 EncryptionCertificate=> CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); internal static X509Certificate2 DecryptionCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); internal static X509Certificate2 VerifyCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); diff --git a/src/UnitTests/X509Certificate2_DecryptStream.cs b/src/UnitTests/X509Certificate2_DecryptStream.cs index f7bfd9a..7d9d11c 100644 --- a/src/UnitTests/X509Certificate2_DecryptStream.cs +++ b/src/UnitTests/X509Certificate2_DecryptStream.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Decryption { [TestClass] public class X509Certificate2_DecryptStream diff --git a/src/UnitTests/X509Certificate2_DecryptStream_PerfTests.cs b/src/UnitTests/X509Certificate2_DecryptStream_PerfTests.cs index 1e66b74..c6586a6 100644 --- a/src/UnitTests/X509Certificate2_DecryptStream_PerfTests.cs +++ b/src/UnitTests/X509Certificate2_DecryptStream_PerfTests.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Decryption { [TestClass] public class X509Certificate2_DecryptStream_PerfTests diff --git a/src/UnitTests/X509Certificate2_EncryptStream.cs b/src/UnitTests/X509Certificate2_EncryptStream.cs index 277f4cd..2db0f9b 100644 --- a/src/UnitTests/X509Certificate2_EncryptStream.cs +++ b/src/UnitTests/X509Certificate2_EncryptStream.cs @@ -7,7 +7,7 @@ using System; using System.Security.Cryptography; -namespace UnitTests +namespace UnitTests.Encryption { [TestClass] public class X509Certificate2_EncryptStream diff --git a/src/UnitTests/X509Certificate2_EncryptStream_PerfTests.cs b/src/UnitTests/X509Certificate2_EncryptStream_PerfTests.cs index 05842fe..daab0e1 100644 --- a/src/UnitTests/X509Certificate2_EncryptStream_PerfTests.cs +++ b/src/UnitTests/X509Certificate2_EncryptStream_PerfTests.cs @@ -5,7 +5,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Encryption { [TestClass] public class X509Certificate2_EncryptStream_PerfTests diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs index f6271cc..d749c14 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs @@ -2,7 +2,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Decryption { [TestClass] public class X509CertificateBasedDecryptor_DecryptBase64EncodedString diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs index 46dfcfa..b2c7ba1 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs @@ -2,7 +2,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Decryption { [TestClass] public class X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs index a98c343..27ff489 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs @@ -7,7 +7,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Decryption { [TestClass] public class X509CertificateBasedDecryptor_DecryptStream diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs index 8955415..7189b92 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs @@ -5,7 +5,7 @@ using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace UnitTests +namespace UnitTests.Decryption { [TestClass] public class X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs index 4dfd312..bfc0b00 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs @@ -7,7 +7,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Encryption { [TestClass] public class X509CertificateBasedEncryptor_EncryptStream diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStreamWithTimestamp.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStreamWithTimestamp.cs index 63e5bd5..3c869c5 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStreamWithTimestamp.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStreamWithTimestamp.cs @@ -3,7 +3,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Encryption { [TestClass] public class X509CertificateBasedEncryptor_EncryptStreamWithTimestamp diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs index c01cca7..84706bb 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs @@ -2,7 +2,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Encryption { [TestClass] public class X509CertificateBasedEncryptor_EncryptStringToBase64 diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs index c13f205..accc8ca 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs @@ -2,7 +2,7 @@ using Org.Security.Cryptography; using X509.EnduranceTest.Shared; -namespace UnitTests +namespace UnitTests.Encryption { [TestClass] public class X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp diff --git a/src/UnitTests/X509SignatureTests.cs b/src/UnitTests/X509SignatureTests.cs index 61acdf1..414c207 100644 --- a/src/UnitTests/X509SignatureTests.cs +++ b/src/UnitTests/X509SignatureTests.cs @@ -6,7 +6,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace UnitTests.POCs +namespace UnitTests.Signature { [TestClass] public class X509SignatureTests diff --git a/src/UnitTests/test.runsettings b/src/UnitTests/test.runsettings index c1ee804..39ac4c5 100644 --- a/src/UnitTests/test.runsettings +++ b/src/UnitTests/test.runsettings @@ -2,7 +2,7 @@ - Net5.0 + @@ -10,9 +10,9 @@ - - .*Shared.* - + + .*Org.Security.Cryptography.X509Extensions.dll + True From 1099cddedf94cd25346cef2ef5b8f7c928b66e6c Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 10:47:17 -0400 Subject: [PATCH 20/52] Updated readme with badges --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b7ec2e2..9cabdbe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,10 @@ -[![codecov](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions/branch/master/graph/badge.svg?token=AS2FV3ACUI)](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions) + +| Area | Badges | +|:--------------|:-------------| +| Build | ![.Net workflow](https://github.com/dotnet-demos/Org.Security.Cryptography.X509Extensions/actions/workflows/dotnet.yml/badge.svg) | +| Code | ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/dotnet-demos/Org.Security.Cryptography.X509Extensions) ![GitHub repo size](https://img.shields.io/github/repo-size/dotnet-demos/Org.Security.Cryptography.X509Extensions) [![](https://tokei.rs/b1/github/dotnet-demos/Org.Security.Cryptography.X509Extensions)](https://github.com/dotnet-demos/Org.Security.Cryptography.X509Extensions) | +| Code Quality | [![Maintainability](https://api.codeclimate.com/v1/badges/b64e91057b6c905e0347/maintainability)](https://codeclimate.com/github/dotnet-demos/Org.Security.Cryptography.X509Extensions/maintainability) | +| Test | [![codecov](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions/branch/master/graph/badge.svg?token=AS2FV3ACUI)](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions) | # Org.Security.Cryptography.X509Extensions `X509Certificate2` Extensions for Encrypting and Signing using X509 certs. From 7e12e5ab32634cbf4855f4d7ddfe6ad8818ab4b1 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 11:02:30 -0400 Subject: [PATCH 21/52] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9cabdbe..6d99f45 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ var decryptedStream = new MemoryStream(); x509Certificate.DecryptStream(yourStreamToDecrypt, decryptedStream); ``` +For other APIs, please refer the unit tests. # Running tests @@ -49,4 +50,4 @@ It is excluding the shared test library. ## Visual Studio -Use the "Run Coverlet Report" extension as mentioned [here](https://www.code4it.dev/blog/code-coverage-vs-2019-coverlet) \ No newline at end of file +Use the "Run Coverlet Report" extension as mentioned [here](https://www.code4it.dev/blog/code-coverage-vs-2019-coverlet) From 7892f0f20b744a8e4824351dac7633ae2eab483b Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 12:15:39 -0400 Subject: [PATCH 22/52] Added DocFX and corresponding GH Action --- .github/workflows/dotnet.yml | 8 ++- .gitignore | 7 ++ .../.gitignore | 9 +++ ...ecurity.Cryptography.X509Extensions.csproj | 10 +++ ...9Certificate2StreamDecryptionExtensions.cs | 3 +- .../X509CertificateBasedDecryptor.cs | 2 +- .../X509CertificateBasedEncryptor.cs | 1 + .../api/.gitignore | 5 ++ .../api/index.md | 2 + .../articles/intro.md | 1 + .../articles/toc.yml | 2 + .../docfx.json | 69 +++++++++++++++++++ .../index.md | 4 ++ .../log.txt | 3 + .../toc.yml | 5 ++ 15 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 src/Org.Security.Cryptography.X509Extensions/.gitignore create mode 100644 src/Org.Security.Cryptography.X509Extensions/api/.gitignore create mode 100644 src/Org.Security.Cryptography.X509Extensions/api/index.md create mode 100644 src/Org.Security.Cryptography.X509Extensions/articles/intro.md create mode 100644 src/Org.Security.Cryptography.X509Extensions/articles/toc.yml create mode 100644 src/Org.Security.Cryptography.X509Extensions/docfx.json create mode 100644 src/Org.Security.Cryptography.X509Extensions/index.md create mode 100644 src/Org.Security.Cryptography.X509Extensions/log.txt create mode 100644 src/Org.Security.Cryptography.X509Extensions/toc.yml diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 44aa80b..f8e67e0 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -29,4 +29,10 @@ jobs: uses: codecov/codecov-action@v2 with: files: src/UnitTests/coverage.Net5.0.opencover.xml - verbose: true \ No newline at end of file + verbose: true + - name: Deploy + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: $ + BRANCH: gh-pages + FOLDER: _site \ No newline at end of file diff --git a/.gitignore b/.gitignore index 33583d1..d334f24 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,9 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs +#logs +.log + # Mono auto generated files mono_crash.* @@ -170,6 +173,10 @@ DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html +#DocFx files + +/**/_site + # Click-Once directory publish/ diff --git a/src/Org.Security.Cryptography.X509Extensions/.gitignore b/src/Org.Security.Cryptography.X509Extensions/.gitignore new file mode 100644 index 0000000..4378419 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/.gitignore @@ -0,0 +1,9 @@ +############### +# folder # +############### +/**/DROP/ +/**/TEMP/ +/**/packages/ +/**/bin/ +/**/obj/ +_site diff --git a/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj b/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj index 7e8dd40..38f3176 100644 --- a/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj +++ b/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj @@ -16,5 +16,15 @@ MIT + + Docfx-$(TargetFramework).log + Warning + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs index 208515b..1c07945 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509Certificate2StreamDecryptionExtensions.cs @@ -45,7 +45,6 @@ public static void DecryptStreamWithTimestampValidation(this X509Certificate2 x5 public static void DecryptStreamWithTimestampValidation(this X509Certificate2 x509Cert, Stream inputStream, Stream outputStream, - bool validateTimestamp, TimeSpan lifeSpan, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName) { @@ -60,7 +59,7 @@ public static void DecryptStreamWithTimestampValidation(this X509Certificate2 x5 { if (null == dataEncryption) throw new Exception($"SymmetricAlgorithm.Create('{dataEncryptionAlgorithmName}') returned null."); // KeySize/blockSize will be selected when we assign key/IV later. - DecryptStream(inputStream, outputStream, validateTimestamp, lifeSpan, keyEncryption, dataEncryption); + DecryptStream(inputStream, outputStream, true, lifeSpan, keyEncryption, dataEncryption); } } #endregion diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs index 70986e6..364b3a6 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs @@ -60,7 +60,7 @@ public void DecryptStreamWithTimestampValidation( X509Certificate2 certificateForKeyEncryption = GetCertificateFromStream(inputStream, certificateSelector); - certificateForKeyEncryption.DecryptStreamWithTimestampValidation(inputStream, outputStream, true, lifeSpanOfInput, dataEncryptionAlgorithmName); + certificateForKeyEncryption.DecryptStreamWithTimestampValidation(inputStream, outputStream, lifeSpanOfInput, dataEncryptionAlgorithmName); } public string DecryptBase64EncodedStringWithTimestampValidation( diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index a388891..714eec5 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -68,6 +68,7 @@ public void EncryptStream(X509Certificate2 x509Cert, /// The thumbprint of the certificate is attached as first thing in the encrypted data. /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. /// + //TODO: Consider using CryptoStream to convert into base64 https://stackoverflow.com/questions/19134062/encode-a-filestream-to-base64-with-c-sharp public string EncryptStringToBase64(X509Certificate2 x509Cert, string valueToEncode, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, diff --git a/src/Org.Security.Cryptography.X509Extensions/api/.gitignore b/src/Org.Security.Cryptography.X509Extensions/api/.gitignore new file mode 100644 index 0000000..e8079a3 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/api/.gitignore @@ -0,0 +1,5 @@ +############### +# temp file # +############### +*.yml +.manifest diff --git a/src/Org.Security.Cryptography.X509Extensions/api/index.md b/src/Org.Security.Cryptography.X509Extensions/api/index.md new file mode 100644 index 0000000..78dc9c0 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/api/index.md @@ -0,0 +1,2 @@ +# PLACEHOLDER +TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/intro.md b/src/Org.Security.Cryptography.X509Extensions/articles/intro.md new file mode 100644 index 0000000..fd38e3c --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/articles/intro.md @@ -0,0 +1 @@ +# X509Certificate2 Extensions and Classes for cryptography diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml b/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml new file mode 100644 index 0000000..ff89ef1 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml @@ -0,0 +1,2 @@ +- name: Introduction + href: intro.md diff --git a/src/Org.Security.Cryptography.X509Extensions/docfx.json b/src/Org.Security.Cryptography.X509Extensions/docfx.json new file mode 100644 index 0000000..3ae6b14 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/docfx.json @@ -0,0 +1,69 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ + "**.csproj" + ], + "src": "C:\\source\\repos\\dotnet-demos\\Org.Security.Cryptography.X509Extensions\\src\\Org.Security.Cryptography.X509Extensions" + } + ], + "exclude": [ "**/bin/**", "**/obj/**" ], + "dest": "api", + "disableGitFeatures": false, + "disableDefaultFilter": false, + "properties": { + "TargetFramework": "netstandard2.0" + } + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "dest": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default" + ], + "postProcessors": [], + "markdownEngineName": "markdig", + "noLangKeyword": false, + "keepFileLink": false, + "cleanupCacheHistory": false, + "disableGitFeatures": false + } +} \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/index.md b/src/Org.Security.Cryptography.X509Extensions/index.md new file mode 100644 index 0000000..3ae2506 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/index.md @@ -0,0 +1,4 @@ +# This is the **HOMEPAGE**. +Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. +## Quick Start Notes: +1. Add images to the *images* folder if the file is referencing an image. diff --git a/src/Org.Security.Cryptography.X509Extensions/log.txt b/src/Org.Security.Cryptography.X509Extensions/log.txt new file mode 100644 index 0000000..edff8c5 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/log.txt @@ -0,0 +1,3 @@ +{"message":"Workspace failed with: [Failure] Msbuild failed when processing the file 'C:\\source\\repos\\dotnet-demos\\Org.Security.Cryptography.X509Extensions\\src\\Org.Security.Cryptography.X509Extensions\\Org.Security.Cryptography.X509Extensions.csproj' with message: Could not load SDK Resolver. A manifest file exists, but the path to the SDK Resolver DLL file could not be found. Manifest file path 'C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\MSBuild\\15.0\\Bin\\SdkResolvers\\Microsoft.Build.NuGetSdkResolver\\Microsoft.Build.NuGetSdkResolver.xml'. SDK resolver path: C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\BuildTools\\Common7\\IDE\\CommonExtensions\\Microsoft\\NuGet\\Microsoft.Build.NuGetSdkResolver.dll C:\\source\\repos\\dotnet-demos\\Org.Security.Cryptography.X509Extensions\\src\\Org.Security.Cryptography.X509Extensions\\Org.Security.Cryptography.X509Extensions.csproj","source":"MetadataCommand.ExtractMetadata","file":"C:/source/repos/dotnet-demos/Org.Security.Cryptography.X509Extensions/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj","date_time":"2021-08-20T15:24:45.3362903Z","message_severity":"warning","correlation_id":"702BD417-4435-4408-91B4-0A70A2DB163C.1.1.5"} +{"message":"Project 'C:\\source\\repos\\dotnet-demos\\Org.Security.Cryptography.X509Extensions\\src\\Org.Security.Cryptography.X509Extensions\\Org.Security.Cryptography.X509Extensions.csproj' does not contain any documents.","source":"MetadataCommand.ExtractMetadata","date_time":"2021-08-20T15:24:45.7318821Z","message_severity":"warning","correlation_id":"702BD417-4435-4408-91B4-0A70A2DB163C.1.1.8"} +{"message":"No metadata is generated for Org.Security.Cryptography.X509Extensions.","source":"MetadataCommand.ExtractMetadata","date_time":"2021-08-20T15:24:46.0762661Z","message_severity":"warning","correlation_id":"702BD417-4435-4408-91B4-0A70A2DB163C.1.1.13"} diff --git a/src/Org.Security.Cryptography.X509Extensions/toc.yml b/src/Org.Security.Cryptography.X509Extensions/toc.yml new file mode 100644 index 0000000..59f8010 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/toc.yml @@ -0,0 +1,5 @@ +- name: Articles + href: articles/ +- name: Api Documentation + href: api/ + homepage: api/index.md From 19bd95faae0c65f97fb6f4d3a1545caad10a3393 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 12:23:38 -0400 Subject: [PATCH 23/52] Update dotnet.yml --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f8e67e0..52965f0 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -35,4 +35,4 @@ jobs: with: GITHUB_TOKEN: $ BRANCH: gh-pages - FOLDER: _site \ No newline at end of file + FOLDER: src/Org.Security.Cryptography.X509Extensions/_site From a8ce4b8cc7cfde8cb69ca7bbbd947ba797884f2c Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 21:15:16 -0400 Subject: [PATCH 24/52] DocFX - Experiement to get the docs generated --- .github/workflows/dotnet.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f8e67e0..17712d6 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -30,6 +30,10 @@ jobs: with: files: src/UnitTests/coverage.Net5.0.opencover.xml verbose: true + - name: Run docfx to generate doc + uses: nikeee/docfx-action@master + with: + args: src/Org.Security.Cryptography.X509Extensions/docfx.json - name: Deploy uses: JamesIves/github-pages-deploy-action@releases/v3 with: From 4e673117eeed67f75d96eeaf57707d90a776a1a5 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 21:23:19 -0400 Subject: [PATCH 25/52] DocFX - Path not found properly. trial and error fix --- src/Org.Security.Cryptography.X509Extensions/docfx.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Org.Security.Cryptography.X509Extensions/docfx.json b/src/Org.Security.Cryptography.X509Extensions/docfx.json index 3ae6b14..dc1b854 100644 --- a/src/Org.Security.Cryptography.X509Extensions/docfx.json +++ b/src/Org.Security.Cryptography.X509Extensions/docfx.json @@ -6,7 +6,7 @@ "files": [ "**.csproj" ], - "src": "C:\\source\\repos\\dotnet-demos\\Org.Security.Cryptography.X509Extensions\\src\\Org.Security.Cryptography.X509Extensions" + "src": "../" } ], "exclude": [ "**/bin/**", "**/obj/**" ], From fdbcce14635abba5cf6a81a07b13b28f04a5aa22 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 22:22:41 -0400 Subject: [PATCH 26/52] Update dotnet.yml --- .github/workflows/dotnet.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 3fa1f55..ba8bd75 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -9,6 +9,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: Setup .NET Core 2.2 + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 2.2.x - name: Setup .NET Core 3.1 uses: actions/setup-dotnet@v1 with: From 0b49d932258b7aca07d52e89dc1b40a37c945870 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 22:27:35 -0400 Subject: [PATCH 27/52] Update docfx.json --- src/Org.Security.Cryptography.X509Extensions/docfx.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Org.Security.Cryptography.X509Extensions/docfx.json b/src/Org.Security.Cryptography.X509Extensions/docfx.json index dc1b854..602f158 100644 --- a/src/Org.Security.Cryptography.X509Extensions/docfx.json +++ b/src/Org.Security.Cryptography.X509Extensions/docfx.json @@ -6,7 +6,7 @@ "files": [ "**.csproj" ], - "src": "../" + "src": "." } ], "exclude": [ "**/bin/**", "**/obj/**" ], @@ -66,4 +66,4 @@ "cleanupCacheHistory": false, "disableGitFeatures": false } -} \ No newline at end of file +} From 4669f6e05909229573e556cb295dce8fa320a4ba Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 22:38:52 -0400 Subject: [PATCH 28/52] DocFX - Fixed comment and removed unwanted step in GH actions --- .github/workflows/dotnet.yml | 4 ---- .../X509CertificateBasedEncryptor.cs | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ba8bd75..a30cd3f 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -34,10 +34,6 @@ jobs: with: files: src/UnitTests/coverage.Net5.0.opencover.xml verbose: true - - name: Run docfx to generate doc - uses: nikeee/docfx-action@master - with: - args: src/Org.Security.Cryptography.X509Extensions/docfx.json - name: Deploy uses: JamesIves/github-pages-deploy-action@releases/v3 with: diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index 714eec5..2b7db4c 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -68,7 +68,6 @@ public void EncryptStream(X509Certificate2 x509Cert, /// The thumbprint of the certificate is attached as first thing in the encrypted data. /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. /// - //TODO: Consider using CryptoStream to convert into base64 https://stackoverflow.com/questions/19134062/encode-a-filestream-to-base64-with-c-sharp public string EncryptStringToBase64(X509Certificate2 x509Cert, string valueToEncode, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, @@ -82,6 +81,7 @@ public string EncryptStringToBase64(X509Certificate2 x509Cert, this.EncryptStream(x509Cert, input, output, dataEncryptionAlgorithmName, keySize, blockSize); output.Flush(); var outputArray = output.ToArray(); + //TODO: Consider using CryptoStream to convert into base64 https://stackoverflow.com/questions/19134062/encode-a-filestream-to-base64-with-c-sharp return Convert.ToBase64String(outputArray); } } From db31d2a52ffdd2a1a480e8bf1565a062a594c313 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 23:00:05 -0400 Subject: [PATCH 29/52] Added DocFx and removed .net core 3 from GHActions --- .github/workflows/dotnet.yml | 4 ---- .../X509CertificateBasedEncryptor.cs | 2 +- .../articles/intro.md | 2 ++ .../index.md | 15 ++++++++++++--- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index a30cd3f..e43375b 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -13,10 +13,6 @@ jobs: uses: actions/setup-dotnet@v1 with: dotnet-version: 2.2.x - - name: Setup .NET Core 3.1 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 3.1.x - name: Setup .NET 5 uses: actions/setup-dotnet@v1 with: diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index 2b7db4c..a74867c 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -66,7 +66,7 @@ public void EncryptStream(X509Certificate2 x509Cert, /// The encrypted content in base64 format. /// /// The thumbprint of the certificate is attached as first thing in the encrypted data. - /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. + /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. /// public string EncryptStringToBase64(X509Certificate2 x509Cert, string valueToEncode, diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/intro.md b/src/Org.Security.Cryptography.X509Extensions/articles/intro.md index fd38e3c..19ba50e 100644 --- a/src/Org.Security.Cryptography.X509Extensions/articles/intro.md +++ b/src/Org.Security.Cryptography.X509Extensions/articles/intro.md @@ -1 +1,3 @@ # X509Certificate2 Extensions and Classes for cryptography + +This is a library to provide cryptographic functions such as signing and encryption. For more details refer [Api documentation](../api/index.md) \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/index.md b/src/Org.Security.Cryptography.X509Extensions/index.md index 3ae2506..523462f 100644 --- a/src/Org.Security.Cryptography.X509Extensions/index.md +++ b/src/Org.Security.Cryptography.X509Extensions/index.md @@ -1,4 +1,13 @@ -# This is the **HOMEPAGE**. -Refer to [Markdown](http://daringfireball.net/projects/markdown/) for how to write markdown files. +# X509Certificate2 Extensions and Classes for cryptography + +Welcome to the **X509Certificate2 Extensions and Classes for cryptography** + ## Quick Start Notes: -1. Add images to the *images* folder if the file is referencing an image. + +- Compile the libray +- Refer to your project + +## Documentation + +Refer [Api Documentation](api/index.md) + From 4355d9ff5c52f3b375989fcd4b175d1efb87bb1c Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Fri, 20 Aug 2021 23:16:15 -0400 Subject: [PATCH 30/52] DocFX - moved FIPS and added comments --- .github/workflows/dotnet.yml | 4 -- X509Extensions.sln | 1 - .../X509CertificateBasedEncryptor.cs | 22 ++++++-- .../api/index.md | 5 +- .../articles/FIPS.md | 54 +++++++++++++++++++ .../articles/toc.yml | 2 + 6 files changed, 77 insertions(+), 11 deletions(-) create mode 100644 src/Org.Security.Cryptography.X509Extensions/articles/FIPS.md diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index e43375b..4dd60ce 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -9,10 +9,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Setup .NET Core 2.2 - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 2.2.x - name: Setup .NET 5 uses: actions/setup-dotnet@v1 with: diff --git a/X509Extensions.sln b/X509Extensions.sln index 0fbc9ad..7f494dc 100644 --- a/X509Extensions.sln +++ b/X509Extensions.sln @@ -6,7 +6,6 @@ MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EAA2E090-9112-4E79-B25C-030FF4B6D25D}" ProjectSection(SolutionItems) = preProject .github\workflows\dotnet.yml = .github\workflows\dotnet.yml - ReadMe-FIPS.md = ReadMe-FIPS.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Org.Security.Cryptography.X509Extensions", "src\Org.Security.Cryptography.X509Extensions\Org.Security.Cryptography.X509Extensions.csproj", "{1EEFA765-F0B2-4C93-A543-F3DC6410F60E}" diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index a74867c..2c7886b 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -5,6 +5,9 @@ namespace Org.Security.Cryptography { + /// + /// Provides capabilities to encrypt data using X509 certificate. + /// public class X509CertificateBasedEncryptor { #region public @@ -13,7 +16,7 @@ public class X509CertificateBasedEncryptor /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given certificate. /// It writes the UTC timestamp of encryption. This can be used to validate during decryption to avoid replay attacks in client server scenarios. /// - /// + /// Certificate to encrypt /// /// /// @@ -35,7 +38,7 @@ public void EncryptStreamWithTimestamp( /// Encrypt input stream using the symmetric algorithm provided. /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given certificate. /// - /// + /// Certificate to encrypt /// /// /// @@ -58,12 +61,12 @@ public void EncryptStream(X509Certificate2 x509Cert, /// Encrypt input string using the symmetric algorithm provided. /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given . /// - /// + /// Certificate to encrypt /// /// /// /// - /// The encrypted content in base64 format. + /// The encrypted content in base64 format. /// /// The thumbprint of the certificate is attached as first thing in the encrypted data. /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. @@ -85,6 +88,17 @@ public string EncryptStringToBase64(X509Certificate2 x509Cert, return Convert.ToBase64String(outputArray); } } + /// + /// Encrypt input string using the symmetric algorithm provided including the timestamp of emcryption. + /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given . + /// + /// Certificate to encrypt + /// + /// + /// + /// + /// The encrypted content in base64 format. + /// The timestamped encrypted payload is good in client-server or internet scenarios to avoid re-play attacks. public string EncryptStringToBase64WithTimestamp(X509Certificate2 x509Cert, string valueToEncode, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, diff --git a/src/Org.Security.Cryptography.X509Extensions/api/index.md b/src/Org.Security.Cryptography.X509Extensions/api/index.md index 78dc9c0..7e715d8 100644 --- a/src/Org.Security.Cryptography.X509Extensions/api/index.md +++ b/src/Org.Security.Cryptography.X509Extensions/api/index.md @@ -1,2 +1,3 @@ -# PLACEHOLDER -TODO: Add .NET projects to the *src* folder and run `docfx` to generate **REAL** *API Documentation*! +# Welcome to Org.Security.Cryptography + +This library contains classes and extension methods to work with cryptography in .Net. Feel free to explore the APIs. \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/FIPS.md b/src/Org.Security.Cryptography.X509Extensions/articles/FIPS.md new file mode 100644 index 0000000..89c0586 --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/articles/FIPS.md @@ -0,0 +1,54 @@ + +# About Federal Information Processing Standard + +Is this package FIPS compliant? Depends. + +### .NET Core Federal Information Processing Standard (FIPS) compliance +* [.NET Core and FIPS](https://docs.microsoft.com/en-us/dotnet/standard/security/fips-compliance) +* Does not enforce the use of FIPS Approved algorithms or key sizes in .NET Core apps. +* The developer is responsible for ensuring that non-compliant FIPS algorithms aren't used. + +### Microsoft’s approach to FIPS 140-2 validation + +* [Microsoft’s approach to FIPS 140-2 validation](https://docs.microsoft.com/en-us/windows/security/threat-protection/fips-140-validation) +* [Using Windows in a FIPS 140-2 approved mode](https://docs.microsoft.com/en-us/windows/security/threat-protection/fips-140-validation#using-windows-in-a-fips-140-2-approved-mode-of-operation) +* [Use FIPS compliant algorithms for encryption, hashing, and signing](https://docs.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/system-cryptography-use-fips-compliant-algorithms-for-encryption-hashing-and-signing) +* [Why We’re Not Recommending FIPS Mode Anymore](https://docs.microsoft.com/en-us/archive/blogs/secguide/why-were-not-recommending-fips-mode-anymore) + +### Windows FIPS Mode +> This policy setting determines whether the TLS/SSL security provider supports only the FIPS-compliant strong cipher suite known as TLS_RSA_WITH_3DES_EDE_CBC_SHA, which means that +> * the provider only supports the TLS protocol as a client computer and as a server, if applicable. +> * uses only the Triple Data Encryption Standard (3DES) encryption algorithm for the TLS traffic encryption, +> * only the Rivest-Shamir-Adleman (RSA) public key algorithm for the TLS key exchange and authentication, +> * and only the Secure Hash Algorithm version 1 (SHA-1) hashing algorithm for the TLS hashing requirements. + +## More + +Credit: [AesCryptoServiceProvider and FIPS mode](https://social.msdn.microsoft.com/Forums/vstudio/en-US/521b669d-09d8-46c9-812b-843b611f42e4/aescryptoserviceprovider-and-fips-mode) + +Aes algorithm (as in "the algorithm") is FIPS 140-2 compliant. +Aes algorithm implementation by Microsoft (Enhanced Cryptographic Provider in rsaenh.dll) is also FIPS 140-2 compliant. +System.Security.Cryptography.AesCryptoServiceProvider uses rsaenh.dll CSP, hence is its also FIPS 140-2 compliant. + +As an example, AesManaged DOESN'T use rsaenh.dll CSP. +AesManaged checks for FIPS mode and will throw an exception is FIPS compliance is turned on. + +Strictly speaking it's not the AesCryptoServiceProvider or AesManaged that are FIPS 140-2 compliant. +Its the underlying libraries accessed through the CAPI (like the Enhanced Cryptographic Provider in rsaenh.dll). +All other .NET CSPs, e.g. AesManaged or MD5CryptoServiceProvider, that do not rely on this libraries are not compliant. + +The security policy FIPS mode simply turns on a flag in the registry, nothing more +REF: HKLM\SYSTEM\CurrentControlSet\Control\Lsa\fipsalgorithmpolicy) + +The AesManaged class for example checks the flag in its constructor and +if it's set it simply throws an exception that tells the user that this is not a FIPS compliant algorithm, +because it doesn't call into the compliant libraries. + +Turning the flag in the registry ON suggestes the use of FIPS compliant algorithms. +But does not trigger any other system-side processing. +By enabling this flag, you'll get an exception every single time an application attempts to use a non-compliant algorithm. + +Since rsaenh.dll is FIPS compliant, the AesCryptoServiceProvider will not throw such an exception. +After all, it is only a thin wrapper around the CAPI (advapi32.dll, crypt32.dll). + + diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml b/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml index ff89ef1..87cf7d6 100644 --- a/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml +++ b/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml @@ -1,2 +1,4 @@ - name: Introduction href: intro.md +- name: Federal Information Processing Standard + href: FIPS.md \ No newline at end of file From b9e891b2eb6658d74548444f6a095ea0af1a035c Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Sat, 21 Aug 2021 00:03:28 -0400 Subject: [PATCH 31/52] Diagrams added and updated readme.md --- README.md | 2 +- diagrams/simple.puml | 19 +++++++++++ diagrams/thumbprint.puml | 21 ++++++++++++ diagrams/timestamp.puml | 21 ++++++++++++ .../X509CertificateBasedEncryptor.cs | 32 +++++++++++-------- 5 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 diagrams/simple.puml create mode 100644 diagrams/thumbprint.puml create mode 100644 diagrams/timestamp.puml diff --git a/README.md b/README.md index 6d99f45..3b3787c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ var decryptedStream = new MemoryStream(); x509Certificate.DecryptStream(yourStreamToDecrypt, decryptedStream); ``` -For other APIs, please refer the unit tests. +For other APIs, please refer the unit tests or the [API documentation](https://dotnet-demos.github.io/Org.Security.Cryptography.X509Extensions/api/index.html) # Running tests diff --git a/diagrams/simple.puml b/diagrams/simple.puml new file mode 100644 index 0000000..07f5725 --- /dev/null +++ b/diagrams/simple.puml @@ -0,0 +1,19 @@ +@startuml +object Sender { + Encrypt data +} +package payload { + object Contents{ + Length Of DEK (Int32) + Asymmemetric Encrypted DEK(Data Encryption Key) + Length Of IV (Int32) + Asymmemetric Encrypted IV (Initialization Vector) + Symmetric Encrypted Data using DEK + } +} +object Receiver { + Encrypt data +} +Sender -right-> Contents +Contents -right-> Receiver +@enduml diff --git a/diagrams/thumbprint.puml b/diagrams/thumbprint.puml new file mode 100644 index 0000000..9bf3437 --- /dev/null +++ b/diagrams/thumbprint.puml @@ -0,0 +1,21 @@ +@startuml +object Sender { + Encrypt data +} +package payload { + object Contents{ + Length Of certiticate thumbprint (Int32) + Unencrypted certificate thumbprint + Length Of DEK (Int32) + Asymmemetric Encrypted DEK(Data Encryption Key) + Length Of IV (Int32) + Asymmemetric Encrypted IV (Initialization Vector) + Symmetric Encrypted Data using DEK + } +} +object Receiver { + Encrypt data +} +Sender -right-> Contents +Contents -right-> Receiver +@enduml diff --git a/diagrams/timestamp.puml b/diagrams/timestamp.puml new file mode 100644 index 0000000..9bf3437 --- /dev/null +++ b/diagrams/timestamp.puml @@ -0,0 +1,21 @@ +@startuml +object Sender { + Encrypt data +} +package payload { + object Contents{ + Length Of certiticate thumbprint (Int32) + Unencrypted certificate thumbprint + Length Of DEK (Int32) + Asymmemetric Encrypted DEK(Data Encryption Key) + Length Of IV (Int32) + Asymmemetric Encrypted IV (Initialization Vector) + Symmetric Encrypted Data using DEK + } +} +object Receiver { + Encrypt data +} +Sender -right-> Contents +Contents -right-> Receiver +@enduml diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs index 2c7886b..c49dd83 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedEncryptor.cs @@ -12,16 +12,19 @@ public class X509CertificateBasedEncryptor { #region public /// - /// Encrypt input stream using the symmetric algorithm provided. - /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given certificate. + /// Encrypt input stream using the symmetric algorithm provided in + /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given /// Certificate to encrypt /// /// - /// + /// Name of symmetric algorithm. Defaults to 'Aes' /// /// + /// The thumbprint of the certificate is attached as first thing in the encrypted data. + /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. + /// public void EncryptStreamWithTimestamp( X509Certificate2 x509Cert, Stream inputStream, @@ -35,13 +38,13 @@ public void EncryptStreamWithTimestamp( } /// - /// Encrypt input stream using the symmetric algorithm provided. - /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given certificate. + /// Encrypt input stream using the symmetric algorithm provided in . + /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given /// Certificate to encrypt /// /// - /// + /// Name of symmetric algorithm. Defaults to 'Aes' /// /// /// The thumbprint of the certificate is attached as first thing in the encrypted data. @@ -58,12 +61,12 @@ public void EncryptStream(X509Certificate2 x509Cert, x509Cert.EncryptStream(inputStream, outputStream, false, dataEncryptionAlgorithmName, keySize, blockSize); } /// - /// Encrypt input string using the symmetric algorithm provided. + /// Encrypt using the symmetric algorithm provided in . /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given . /// /// Certificate to encrypt - /// - /// + /// The input string to encode + /// Name of symmetric algorithm. Defaults to 'Aes' /// /// /// The encrypted content in base64 format. @@ -89,16 +92,19 @@ public string EncryptStringToBase64(X509Certificate2 x509Cert, } } /// - /// Encrypt input string using the symmetric algorithm provided including the timestamp of emcryption. + /// Encrypt using the symmetric algorithm provided including the timestamp of emcryption. /// The key of symmetric algorithm is encrypted using the Asymmetric algorithm available on the given . /// /// Certificate to encrypt - /// - /// + /// The input string to encode + /// Name of symmetric algorithm. Defaults to 'Aes' /// /// /// The encrypted content in base64 format. - /// The timestamped encrypted payload is good in client-server or internet scenarios to avoid re-play attacks. + /// The thumbprint of the certificate is attached as first thing in the encrypted data. + /// Use this if decryptor doesn't know what certificate encryptor used. Mainly in internet/web/distributed systems scenarios where the certificate rotated out of sync. + /// The timestamped encrypted payload is good in client-server or internet scenarios to avoid re-play attacks. + /// public string EncryptStringToBase64WithTimestamp(X509Certificate2 x509Cert, string valueToEncode, string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName, From fd5114e0c5280e26aaf5de572d01766066a84ed8 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Sat, 21 Aug 2021 00:23:51 -0400 Subject: [PATCH 32/52] Added images to articles --- diagrams/simple.puml | 4 ++-- diagrams/thumbprint.puml | 4 ++-- diagrams/timestamp.puml | 6 ++++-- .../articles/simple-encryption.md | 15 +++++++++++++++ .../articles/toc.yml | 6 ++++++ .../articles/with-thumbprint.md | 14 ++++++++++++++ .../articles/with-timestamp.md | 14 ++++++++++++++ 7 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 src/Org.Security.Cryptography.X509Extensions/articles/simple-encryption.md create mode 100644 src/Org.Security.Cryptography.X509Extensions/articles/with-thumbprint.md create mode 100644 src/Org.Security.Cryptography.X509Extensions/articles/with-timestamp.md diff --git a/diagrams/simple.puml b/diagrams/simple.puml index 07f5725..0251c8a 100644 --- a/diagrams/simple.puml +++ b/diagrams/simple.puml @@ -4,9 +4,9 @@ object Sender { } package payload { object Contents{ - Length Of DEK (Int32) + Length Of encrypted DEK (Int32) Asymmemetric Encrypted DEK(Data Encryption Key) - Length Of IV (Int32) + Length Of encrypted IV (Int32) Asymmemetric Encrypted IV (Initialization Vector) Symmetric Encrypted Data using DEK } diff --git a/diagrams/thumbprint.puml b/diagrams/thumbprint.puml index 9bf3437..5563add 100644 --- a/diagrams/thumbprint.puml +++ b/diagrams/thumbprint.puml @@ -6,9 +6,9 @@ package payload { object Contents{ Length Of certiticate thumbprint (Int32) Unencrypted certificate thumbprint - Length Of DEK (Int32) + Length Of encrypted DEK (Int32) Asymmemetric Encrypted DEK(Data Encryption Key) - Length Of IV (Int32) + Length Of encrypted IV (Int32) Asymmemetric Encrypted IV (Initialization Vector) Symmetric Encrypted Data using DEK } diff --git a/diagrams/timestamp.puml b/diagrams/timestamp.puml index 9bf3437..676b844 100644 --- a/diagrams/timestamp.puml +++ b/diagrams/timestamp.puml @@ -6,9 +6,11 @@ package payload { object Contents{ Length Of certiticate thumbprint (Int32) Unencrypted certificate thumbprint - Length Of DEK (Int32) + Length Of encrypted timestamp in utc (Int32) + Asymmemetric Encrypted DEK(Data Encryption Key) + Length Of encrypted DEK (Int32) Asymmemetric Encrypted DEK(Data Encryption Key) - Length Of IV (Int32) + Length Of encrypted IV (Int32) Asymmemetric Encrypted IV (Initialization Vector) Symmetric Encrypted Data using DEK } diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/simple-encryption.md b/src/Org.Security.Cryptography.X509Extensions/articles/simple-encryption.md new file mode 100644 index 0000000..88b2bee --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/articles/simple-encryption.md @@ -0,0 +1,15 @@ +# Simple encryption + +## Use cases +- Same application decrypting the content. +- Not transmitted over wire. +- The certificate is known to the decrypting application. + +## Payload +The basic encryption includes the below contents in the payload. + +![Simple](https://www.plantuml.com/plantuml/proxy?fmt=svg&cache=no&src=https://raw.githubusercontent.com/dotnet-demos/Org.Security.Cryptography.X509Extensions/master/diagrams/simple.puml) + +## Cons +- If the certificate is rotated out of sync between the encrypting and decrypting applications, the decryption will fail. +- If the content is transmitted over wire using encrypted auth information, attackers can intercept and replay using different HTTP or wire payload. \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml b/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml index 87cf7d6..9a080a4 100644 --- a/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml +++ b/src/Org.Security.Cryptography.X509Extensions/articles/toc.yml @@ -1,4 +1,10 @@ - name: Introduction href: intro.md +- name: Simple encryption + href: simple-encryption.md +- name: Encryption with thumbprint of certificate + href: with-thumbprint.md +- name: Encryption with timestamp of encryption + href: with-timestamp.md - name: Federal Information Processing Standard href: FIPS.md \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/with-thumbprint.md b/src/Org.Security.Cryptography.X509Extensions/articles/with-thumbprint.md new file mode 100644 index 0000000..24f018a --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/articles/with-thumbprint.md @@ -0,0 +1,14 @@ +# Encryption with thumbprint of certificate + +## Use cases +- Different application decrypting the content. +- Not transmitted over wire. +- The certificate is rotated out of sync between encrypting and decrypting applications. + +## Payload +The encryption with thumbprint includes the below contents in the payload. + +![Thumbprint](https://www.plantuml.com/plantuml/proxy?fmt=svg&cache=no&src=https://raw.githubusercontent.com/dotnet-demos/Org.Security.Cryptography.X509Extensions/master/diagrams/thumbprint.puml) + +## Cons +- If the content is transmitted over wire using encrypted auth information, attackers can intercept and replay using different HTTP or wire payload. \ No newline at end of file diff --git a/src/Org.Security.Cryptography.X509Extensions/articles/with-timestamp.md b/src/Org.Security.Cryptography.X509Extensions/articles/with-timestamp.md new file mode 100644 index 0000000..44f652c --- /dev/null +++ b/src/Org.Security.Cryptography.X509Extensions/articles/with-timestamp.md @@ -0,0 +1,14 @@ +# Encryption with timestamp of encryption + +## Use cases +- Different application decrypting the content. +- Transmitted over wire as authentication. +- The certificate is rotated out of sync between encrypting and decrypting applications. + +## Payload +The encryption with timestamp includes the below contents in the payload. + +![timestamp](https://www.plantuml.com/plantuml/proxy?fmt=svg&cache=no&src=https://raw.githubusercontent.com/dotnet-demos/Org.Security.Cryptography.X509Extensions/master/diagrams/timestamp.puml) + +## Cons +- The time to decrypt may be little more. \ No newline at end of file From 71ec08f66d004517390ea74ecd089f914092d347 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Sat, 21 Aug 2021 21:28:56 -0400 Subject: [PATCH 33/52] Update dotnet.yml --- .github/workflows/dotnet.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 4dd60ce..95db13c 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -15,18 +15,18 @@ jobs: dotnet-version: 5.0.x - name: Restore dependencies run: dotnet restore - - name: Build Library + - name: Build Library 🔧 run: dotnet build src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj --no-restore - - name: Build Tests + - name: Build Tests 🧪 run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - name: Test With Code Coverage run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=80 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal - - name: UploadToCodeCov + - name: UploadToCodeCov ⇪ uses: codecov/codecov-action@v2 with: files: src/UnitTests/coverage.Net5.0.opencover.xml verbose: true - - name: Deploy + - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@releases/v3 with: GITHUB_TOKEN: $ From e4fa88e50e42a133d6f9642737bae507301d9dd8 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Sat, 21 Aug 2021 23:02:27 -0400 Subject: [PATCH 34/52] Unit testing - Added tests for coverage --- .../X509SignatureExtensions.cs | 2 - .../X509Certificate2_CreateSignature.cs | 44 +++++++++++++ .../X509Certificate2_VerifySignature.cs | 66 +++++++++++++++++++ .../{ => SignAndVerify}/X509SignatureTests.cs | 3 +- 4 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs create mode 100644 src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs rename src/UnitTests/{ => SignAndVerify}/X509SignatureTests.cs (93%) diff --git a/src/Org.Security.Cryptography.X509Extensions/X509SignatureExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509SignatureExtensions.cs index 9e24893..a71a1a8 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509SignatureExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509SignatureExtensions.cs @@ -45,8 +45,6 @@ public static bool VerifySignature(this X509Certificate2 x509Cert, byte[] hash, static string InferHashAlgorithm(byte[] hash) { - if (null == hash) throw new ArgumentNullException(nameof(hash)); - // MD5 128 bit / 16 bytes // SHA1 160 bit / 20 bytes // SHA224 224 bit / 28 bytes diff --git a/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs b/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs new file mode 100644 index 0000000..ccba14c --- /dev/null +++ b/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs @@ -0,0 +1,44 @@ + +using System; +using Org.Security.Cryptography; +using System.Security.Cryptography; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Security.Cryptography.X509Certificates; + +namespace UnitTests.SignAndVerify +{ + [TestClass] + public class X509Certificate2_CreateSignature + { + #region Validation tests + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenCertificateIsNull_ThrowArgumentNullException() + { + //arrange + const string TestData = "Hello world"; + var payload = Encoding.UTF8.GetBytes(TestData); + X509Certificate2 certificate2 = null; + //Act + using (var hashAlgorithm = HashAlgorithm.Create("MD5")) + { + var hash = hashAlgorithm.ComputeHash(payload); + certificate2.CreateSignature(hash); + } + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenPayloadIsNull_ThrowArgumentNullException() + { + //arrange + //Act + using (var hashAlgorithm = HashAlgorithm.Create("MD5")) + { + MyConfig.SigningCertificate.CreateSignature(null); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs b/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs new file mode 100644 index 0000000..0956729 --- /dev/null +++ b/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs @@ -0,0 +1,66 @@ + +using System; +using Org.Security.Cryptography; +using System.Security.Cryptography; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Security.Cryptography.X509Certificates; + +namespace UnitTests.SignAndVerify +{ + [TestClass] + public class X509Certificate2_VerifySignature + { + #region Validation tests + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenCertificateIsNull_ThrowArgumentNullException() + { + //Arrange + const string TestData = "Hello world"; + var payload = Encoding.UTF8.GetBytes(TestData); + using (var hashAlgorithm = HashAlgorithm.Create("MD5")) + { + var hash = hashAlgorithm.ComputeHash(payload); + var signature = MyConfig.SigningCertificate.CreateSignature(hash); + X509Certificate2 certificate2 = null; + // Act + var good = certificate2.VerifySignature(hash, signature); + Assert.IsTrue(good); + } + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenSignatureIsNull_ThrowArgumentNullException() + { + //Arrange + const string TestData = "Hello world"; + var payload = Encoding.UTF8.GetBytes(TestData); + using (var hashAlgorithm = HashAlgorithm.Create("MD5")) + { + var hash = hashAlgorithm.ComputeHash(payload); + // Act + var good = MyConfig.VerifyCertificate.VerifySignature(hash, null); + Assert.IsTrue(good); + } + } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void WhenHashIsNull_ThrowArgumentNullException() + { + //Arrange + const string TestData = "Hello world"; + var payload = Encoding.UTF8.GetBytes(TestData); + using (var hashAlgorithm = HashAlgorithm.Create("MD5")) + { + var hash = hashAlgorithm.ComputeHash(payload); + var signature = MyConfig.SigningCertificate.CreateSignature(hash); + // Act + var good = MyConfig.VerifyCertificate.VerifySignature(null, signature); + Assert.IsTrue(good); + } + } + #endregion + } +} \ No newline at end of file diff --git a/src/UnitTests/X509SignatureTests.cs b/src/UnitTests/SignAndVerify/X509SignatureTests.cs similarity index 93% rename from src/UnitTests/X509SignatureTests.cs rename to src/UnitTests/SignAndVerify/X509SignatureTests.cs index 414c207..c3f6bce 100644 --- a/src/UnitTests/X509SignatureTests.cs +++ b/src/UnitTests/SignAndVerify/X509SignatureTests.cs @@ -5,8 +5,9 @@ using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Security.Cryptography.X509Certificates; -namespace UnitTests.Signature +namespace UnitTests.SignAndVerify { [TestClass] public class X509SignatureTests From cd4170e2c4e51eb6c634890f35b56ba4bdac58de Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Mon, 23 Aug 2021 09:56:37 -0400 Subject: [PATCH 35/52] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b3787c..45965e6 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ | Test | [![codecov](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions/branch/master/graph/badge.svg?token=AS2FV3ACUI)](https://codecov.io/gh/dotnet-demos/Org.Security.Cryptography.X509Extensions) | # Org.Security.Cryptography.X509Extensions -`X509Certificate2` Extensions for Encrypting and Signing using X509 certs. +`X509Certificate2` Extensions and classes for Encrypting and Signing using X509 certs. # Getting started @@ -34,6 +34,10 @@ ``` For other APIs, please refer the unit tests or the [API documentation](https://dotnet-demos.github.io/Org.Security.Cryptography.X509Extensions/api/index.html) +# Documentation + +[Documentation site](https://dotnet-demos.github.io/Org.Security.Cryptography.X509Extensions/) has [articles](https://dotnet-demos.github.io/Org.Security.Cryptography.X509Extensions/articles/intro.html) as well as [API documentation](https://dotnet-demos.github.io/Org.Security.Cryptography.X509Extensions/api/index.html). + # Running tests Use `dotnet test` command or use the "Test Explorer" windows of Visual Studio. @@ -50,4 +54,4 @@ It is excluding the shared test library. ## Visual Studio -Use the "Run Coverlet Report" extension as mentioned [here](https://www.code4it.dev/blog/code-coverage-vs-2019-coverlet) +Use the "Run Coverlet Report" extension as mentioned [here](https://www.code4it.dev/blog/code-coverage-vs-2019-coverlet). From 0f3b80347f04fb21e14812d0e93147687b3ea59e Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Wed, 25 Aug 2021 13:55:19 -0400 Subject: [PATCH 36/52] Endurance Tests - Made the certificate loading from file than from cert store --- .github/workflows/dotnet.yml | 9 ++++--- .../X509AsymmetricAlgorithmExtensions.cs | 2 -- src/UnitTests/App.config | 2 ++ ...sedDecryptor_DecryptBase64EncodedString.cs | 4 +-- ...e64EncodedStringWithTimestampValidation.cs | 4 +-- ...CertificateBasedDecryptor_DecryptStream.cs | 20 +++++++------- ...or_DecryptStreamWithTimestampValidation.cs | 27 +++++++++++++++++++ ...CertificateBasedEncryptor_EncryptStream.cs | 5 ++-- ...ateBasedEncryptor_EncryptStringToBase64.cs | 4 +-- ...ptor_EncryptStringToBase64WithTimestamp.cs | 4 +-- src/X509.EnduranceTest.NetCore/App.config | 2 ++ .../App.config | 15 ++++++----- .../X509.EnduranceTest.NetFramework.csproj | 3 ++- .../CertificateLoader.cs | 4 ++- .../MyConfig.cs | 10 +++---- src/X509.EnduranceTest.Shared/TestMain.cs | 4 ++- 16 files changed, 77 insertions(+), 42 deletions(-) rename src/{UnitTests => X509.EnduranceTest.Shared}/MyConfig.cs (55%) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 95db13c..32f1288 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -8,18 +8,19 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Clone 📥 + uses: actions/checkout@v2 - name: Setup .NET 5 uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.x - - name: Restore dependencies + - name: Restore dependencies 📦 run: dotnet restore - name: Build Library 🔧 run: dotnet build src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj --no-restore - - name: Build Tests 🧪 + - name: Build Tests 🔧 run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - - name: Test With Code Coverage + - name: Test With Code Coverage 🧪 run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=80 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal - name: UploadToCodeCov ⇪ uses: codecov/codecov-action@v2 diff --git a/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs index 3490e3c..ec9debb 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs @@ -39,9 +39,7 @@ internal static AsymmetricAlgorithm GetPublicKeyAsymmetricAlgorithm(this X509Cer /// internal static AsymmetricAlgorithm GetPrivateKeyAsymmetricAlgorithm(this X509Certificate2 x509Cert) { - if (null == x509Cert) throw new ArgumentNullException(nameof(x509Cert)); if (null == x509Cert.Thumbprint) throw new ArgumentNullException("X509Certificate2.Thumbprint was NULL."); - try { try diff --git a/src/UnitTests/App.config b/src/UnitTests/App.config index d5c2631..038bdcd 100644 --- a/src/UnitTests/App.config +++ b/src/UnitTests/App.config @@ -3,6 +3,8 @@ + + diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs index d749c14..2c09131 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedString.cs @@ -12,8 +12,8 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; //Act var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, TEST); var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedString( diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs index b2c7ba1..a9201be 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptBase64EncodedStringWithTimestampValidation.cs @@ -12,8 +12,8 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() { //Arrange const string input = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; //Act var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64WithTimestamp(x509EncryptionCert, input); var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedStringWithTimestampValidation( diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs index 27ff489..5489530 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStream.cs @@ -19,8 +19,8 @@ public void WhenTheInputStreamParameterIsNull_ThrowArgumentNullException() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; byte[] input = Encoding.UTF8.GetBytes(TEST); //Act new X509CertificateBasedDecryptor().DecryptStream(null, new MemoryStream(), @@ -33,8 +33,8 @@ public void WhenTheOutputStreamParameterIsNull_ThrowArgumentNullException() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; byte[] input = Encoding.UTF8.GetBytes(TEST); //Act new X509CertificateBasedDecryptor().DecryptStream( new MemoryStream(), null, @@ -47,8 +47,8 @@ public void WhenTheDataEncryptionAlgorithmNameParameterIsNull_ThrowArgumentNullE { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; byte[] input = Encoding.UTF8.GetBytes(TEST); //Act new X509CertificateBasedDecryptor().DecryptStream( @@ -64,8 +64,8 @@ public void WhenTheCertificateSelectorParamIsNull_ThrowArgumentNullException() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; byte[] input = Encoding.UTF8.GetBytes(TEST); //Act byte[] encryptedArray = EncryptionDecryptionUtils.EncryptBytesUsingX509CertificateBasedEncryptor(x509EncryptionCert, input); @@ -81,8 +81,8 @@ public void WhenTheDecryptionCertificateIsNull_ThrowArgumentNullException() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; byte[] input = Encoding.UTF8.GetBytes(TEST); //Act byte[] encryptedArray = EncryptionDecryptionUtils.EncryptBytesUsingX509CertificateBasedEncryptor(x509EncryptionCert, input); diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs index 7189b92..197e896 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs @@ -96,6 +96,33 @@ public void WhenDecryptionHappensAfter10SecsOfDefaultExpiry_ThrowException() //Assert Assert.IsTrue(input.SequenceEqual(decryptedOutput)); } + [TestMethod] + //[ExpectedException(typeof(TimeoutException))] + public void WhenDecryptionPayloadDontHaveTimestamp_ThrowException() + { + //Arrange + const string inputString = "Hello World!"; + byte[] input = Encoding.UTF8.GetBytes(inputString); + bool exceptionThrown = false; + //Act + + byte[] encryptedArray = EncryptionDecryptionUtils.EncryptBytesUsingX509CertificateBasedEncryptor(MyConfig.EncryptionCertificate, input); + try + { + byte[] decryptedOutput = EncryptionDecryptionUtils.DecryptBytesWithTimestampValidationUsingX509CertificateBasedDecryptor( + encryptedArray, + thumbprint => MyConfig.DecryptionCertificate); + }catch(ArgumentOutOfRangeException aoorEx) + { + exceptionThrown = true; + } + catch(InvalidOperationException ioEx) + { + exceptionThrown = true; + } + //Assert + Assert.IsTrue(exceptionThrown,$"Expected either {typeof(ArgumentOutOfRangeException)} or {typeof(InvalidOperationException)}"); + } #endregion } } \ No newline at end of file diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs index bfc0b00..b54ec0f 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs @@ -52,9 +52,8 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); - byte[] input = Encoding.UTF8.GetBytes(TEST); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; byte[] input = Encoding.UTF8.GetBytes(TEST); //Act byte[] encryptedArray = EncryptionDecryptionUtils.EncryptBytesUsingX509CertificateBasedEncryptor(x509EncryptionCert, input); byte[] decryptedOutput = EncryptionDecryptionUtils.DecryptBytesUsingX509CertificateBasedDecryptor( diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs index 84706bb..d285c56 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64.cs @@ -12,8 +12,8 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() { //Arrange const string TEST = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; //Act var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64(x509EncryptionCert, TEST); var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedString( diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs index accc8ca..23f05c9 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStringToBase64WithTimestamp.cs @@ -12,8 +12,8 @@ public void WhenItIsCalledWithProperParameters_ShouldEncrypt() { //Arrange const string input = "Hello World!"; - var x509EncryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - var x509DecryptionCert = CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + var x509EncryptionCert = MyConfig.EncryptionCertificate; + var x509DecryptionCert = MyConfig.DecryptionCertificate; //Act var encryptedBase64 = new X509CertificateBasedEncryptor().EncryptStringToBase64WithTimestamp(x509EncryptionCert, input); var decryptedOutput = new X509CertificateBasedDecryptor().DecryptBase64EncodedStringWithTimestampValidation( diff --git a/src/X509.EnduranceTest.NetCore/App.config b/src/X509.EnduranceTest.NetCore/App.config index fbb712a..22c62b6 100644 --- a/src/X509.EnduranceTest.NetCore/App.config +++ b/src/X509.EnduranceTest.NetCore/App.config @@ -5,6 +5,8 @@ + + diff --git a/src/X509.EnduranceTest.NetFramework/App.config b/src/X509.EnduranceTest.NetFramework/App.config index fbb712a..7697ee2 100644 --- a/src/X509.EnduranceTest.NetFramework/App.config +++ b/src/X509.EnduranceTest.NetFramework/App.config @@ -1,19 +1,20 @@ - + - - - + + + + + - + - \ No newline at end of file +--> diff --git a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj index be7bd8c..f18b55b 100644 --- a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj +++ b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj @@ -8,10 +8,11 @@ Exe X509.EnduranceTest.NetFramework X509.EnduranceTest.NetFramework - v4.7.2 + v4.8 512 true true + AnyCPU diff --git a/src/X509.EnduranceTest.Shared/CertificateLoader.cs b/src/X509.EnduranceTest.Shared/CertificateLoader.cs index 4b80939..bed205a 100644 --- a/src/X509.EnduranceTest.Shared/CertificateLoader.cs +++ b/src/X509.EnduranceTest.Shared/CertificateLoader.cs @@ -1,4 +1,5 @@ -using System.Security.Cryptography.X509Certificates; +using System; +using System.Security.Cryptography.X509Certificates; namespace X509.EnduranceTest.Shared { @@ -11,6 +12,7 @@ public static X509Certificate2 LoadFromFile(string filePath) public static X509Certificate2 LoadFromFile(string filePath,string password) { var cert = new X509Certificate2(filePath, password, X509KeyStorageFlags.PersistKeySet); + if (null == cert) throw new NullReferenceException($"Certificate not found at {filePath}"); return cert; } } diff --git a/src/UnitTests/MyConfig.cs b/src/X509.EnduranceTest.Shared/MyConfig.cs similarity index 55% rename from src/UnitTests/MyConfig.cs rename to src/X509.EnduranceTest.Shared/MyConfig.cs index 80cec2b..eea4a8f 100644 --- a/src/UnitTests/MyConfig.cs +++ b/src/X509.EnduranceTest.Shared/MyConfig.cs @@ -6,7 +6,7 @@ namespace UnitTests { - internal static class MyConfig + public static class MyConfig { internal static string TestCertThumbPrint => ConfigurationManager.AppSettings["X509.ThumbPrint"] ?? @@ -19,10 +19,10 @@ internal static class MyConfig /// /// TODO: Below certificates expire on 2023-08-17. Need to recreate certificate then to continue using this test project /// - internal static X509Certificate2 EncryptionCertificate=> CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - internal static X509Certificate2 DecryptionCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); - internal static X509Certificate2 VerifyCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); - internal static X509Certificate2 SigningCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); + public static X509Certificate2 EncryptionCertificate=> CertificateLoader.LoadFromFile(ConfigurationManager.AppSettings["EncryptionCertificatePath"]); + public static X509Certificate2 DecryptionCertificate => CertificateLoader.LoadFromFile(ConfigurationManager.AppSettings["DecryptionCertificatePath"], MyConfig.TestCertficatePassword); + public static X509Certificate2 VerifyCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); + public static X509Certificate2 SigningCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); } } diff --git a/src/X509.EnduranceTest.Shared/TestMain.cs b/src/X509.EnduranceTest.Shared/TestMain.cs index ffa53b8..7213eb6 100644 --- a/src/X509.EnduranceTest.Shared/TestMain.cs +++ b/src/X509.EnduranceTest.Shared/TestMain.cs @@ -8,6 +8,7 @@ using System.Security.Cryptography.X509Certificates; using Org.Security.Cryptography; +using UnitTests; namespace X509.EnduranceTest.Shared { @@ -33,6 +34,7 @@ public static void Run() } catch (Exception err) { + var topError = err; while (null != err) @@ -60,7 +62,7 @@ static void PrintOptionsAndRunTest() var input = (Console.ReadLine() ?? string.Empty).Trim().ToUpper(); - var cert = X509CertificateCache.GetCertificate(X509Thumbprint); + var cert = MyConfig.DecryptionCertificate; switch (input) { From 0ef4ef95457dedc2aae877a838ed0c5acfc600a9 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 09:18:31 -0400 Subject: [PATCH 37/52] Test fixes and refactoring in endurance tests --- src/UnitTests/UnitTests.csproj | 11 +- .../ConsoleWriter.cs | 77 ++++++++++++ .../EncryptionDecryptionUtils.cs | 17 ++- src/X509.EnduranceTest.Shared/TestMain.cs | 119 ++---------------- 4 files changed, 105 insertions(+), 119 deletions(-) create mode 100644 src/X509.EnduranceTest.Shared/ConsoleWriter.cs rename src/{UnitTests => X509.EnduranceTest.Shared}/EncryptionDecryptionUtils.cs (78%) diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 7ba7ee4..2ff007e 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -24,6 +24,9 @@ + + PreserveNewest + Always @@ -46,6 +49,8 @@ - --> - - + --> + + + + \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/ConsoleWriter.cs b/src/X509.EnduranceTest.Shared/ConsoleWriter.cs new file mode 100644 index 0000000..9f5c4df --- /dev/null +++ b/src/X509.EnduranceTest.Shared/ConsoleWriter.cs @@ -0,0 +1,77 @@ + +using System; +using System.Security.Cryptography.X509Certificates; + +namespace X509.EnduranceTest.Shared +{ + class ConsoleWriter + { + internal static void PrintCSP(X509Certificate2 cert) + { + Console.WriteLine($"Cert: {cert.Subject} / {cert.Thumbprint}"); + Console.WriteLine(); + + try + { + Console.WriteLine("cert.PublicKey.Key"); + var alg = cert.PublicKey.Key; + Console.WriteLine(alg.GetType().FullName); + Console.WriteLine(); + } + catch (Exception err) + { + PrintErrorSummary(err); + } + + try + { + // Fails in .NET Framework if -KeySpec Signature not specified. + // Works in .NET Core + Console.WriteLine("cert.PrivateKey"); + var alg = cert.PrivateKey; + Console.WriteLine(alg.GetType().FullName); + Console.WriteLine(); + } + catch (Exception err) + { + PrintErrorSummary(err); + } + + try + { + Console.WriteLine("cert.GetRSAPublicKey()"); + var alg = cert.GetRSAPublicKey(); + Console.WriteLine(alg.GetType().FullName); + Console.WriteLine(); + } + catch (Exception err) + { + PrintErrorSummary(err); + } + + try + { + Console.WriteLine("cert.GetRSAPrivateKey()"); + var alg = cert.GetRSAPrivateKey(); + Console.WriteLine(alg.GetType().FullName); + Console.WriteLine(); + } + catch (Exception err) + { + PrintErrorSummary(err); + } + + void PrintErrorSummary(Exception ex) + { + Console.WriteLine("ERROR:"); + while (null != ex) + { + Console.WriteLine($"[{ex.GetType().FullName}]"); + Console.WriteLine(ex.Message); + ex = ex.InnerException; + } + } + } + + } +} diff --git a/src/UnitTests/EncryptionDecryptionUtils.cs b/src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs similarity index 78% rename from src/UnitTests/EncryptionDecryptionUtils.cs rename to src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs index 81de941..9438441 100644 --- a/src/UnitTests/EncryptionDecryptionUtils.cs +++ b/src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs @@ -6,9 +6,9 @@ namespace UnitTests { - class EncryptionDecryptionUtils + public class EncryptionDecryptionUtils { - internal static byte[] EncryptBytesWithTimestampUsingX509CertificateBasedEncryptor(X509Certificate2 x509Cert, byte[] inputData) + public static byte[] EncryptBytesWithTimestampUsingX509CertificateBasedEncryptor(X509Certificate2 x509Cert, byte[] inputData) { var encryptor = new X509CertificateBasedEncryptor(); using (var input = new MemoryStream(inputData)) @@ -19,7 +19,7 @@ internal static byte[] EncryptBytesWithTimestampUsingX509CertificateBasedEncrypt return output.ToArray(); } } - internal static byte[] DecryptBytesWithTimestampValidationUsingX509CertificateBasedDecryptor( + public static byte[] DecryptBytesWithTimestampValidationUsingX509CertificateBasedDecryptor( byte[] inputData, Func certificateSelector, TimeSpan? lifeSpan = null) @@ -40,7 +40,7 @@ internal static byte[] DecryptBytesWithTimestampValidationUsingX509CertificateBa return output.ToArray(); } } - internal static byte[] EncryptBytesUsingX509CertificateBasedEncryptor(X509Certificate2 x509Cert, byte[] inputData) + public static byte[] EncryptBytesUsingX509CertificateBasedEncryptor(X509Certificate2 x509Cert, byte[] inputData) { var encryptor = new X509CertificateBasedEncryptor(); using (var input = new MemoryStream(inputData)) @@ -51,7 +51,7 @@ internal static byte[] EncryptBytesUsingX509CertificateBasedEncryptor(X509Certif return output.ToArray(); } } - internal static byte[] DecryptBytesUsingX509CertificateBasedDecryptor(byte[] inputData, Func certificateSelector) + public static byte[] DecryptBytesUsingX509CertificateBasedDecryptor(byte[] inputData, Func certificateSelector) { var decryptor = new X509CertificateBasedDecryptor(); using (var input = new MemoryStream(inputData)) @@ -62,7 +62,7 @@ internal static byte[] DecryptBytesUsingX509CertificateBasedDecryptor(byte[] inp return output.ToArray(); } } - internal static byte[] EncryptBytesUsingExtensionMethod(X509Certificate2 x509Cert, byte[] inputData) + public static byte[] EncryptBytesUsingExtensionMethod(X509Certificate2 x509Cert, byte[] inputData) { using (var input = new MemoryStream(inputData)) using (var output = new MemoryStream(inputData.Length)) @@ -72,7 +72,7 @@ internal static byte[] EncryptBytesUsingExtensionMethod(X509Certificate2 x509Cer return output.ToArray(); } } - internal static byte[] DecryptBytesUsingExtensionMethod(X509Certificate2 x509Cert, byte[] inputData) + public static byte[] DecryptBytesUsingExtensionMethod(X509Certificate2 x509Cert, byte[] inputData) { using (var input = new MemoryStream(inputData)) using (var output = new MemoryStream(inputData.Length)) @@ -83,5 +83,4 @@ internal static byte[] DecryptBytesUsingExtensionMethod(X509Certificate2 x509Cer } } } -} - +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/TestMain.cs b/src/X509.EnduranceTest.Shared/TestMain.cs index 7213eb6..45159c5 100644 --- a/src/X509.EnduranceTest.Shared/TestMain.cs +++ b/src/X509.EnduranceTest.Shared/TestMain.cs @@ -14,13 +14,10 @@ namespace X509.EnduranceTest.Shared { public static class TestMain { - //............................................................................... #region AppSettings - //............................................................................... - - static string X509Thumbprint => AppSetting("X509.Thumbprint"); - static int SampleDataSizeKB => Convert.ToInt32(AppSetting("SampleDataSizeKB")); - static int LoopCount => Convert.ToInt32(AppSetting("LoopCount")); + + static int SampleDataSizeKB => Convert.ToInt32(AppSetting("SampleDataSizeKB")); + static int LoopCount => Convert.ToInt32(AppSetting("LoopCount")); static string AppSetting(string name) => ConfigurationManager.AppSettings[name] ?? throw new Exception($"AppSetting 'name' not defined."); @@ -34,16 +31,13 @@ public static void Run() } catch (Exception err) { - var topError = err; - while (null != err) { Console.WriteLine($"[{err.GetType().FullName}]"); Console.WriteLine(err.Message); err = err.InnerException; } - Console.WriteLine(topError.StackTrace); } } @@ -79,7 +73,7 @@ static void PrintOptionsAndRunTest() break; case "P": - PrintCSP(cert); + ConsoleWriter.PrintCSP(cert); break; case "Q": @@ -100,8 +94,8 @@ public static void ValidateEncryptionAndDecryptionOnce(X509Certificate2 cert) Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); // Encrypt/Decrypt ONCE... - var encryptedBytes = EncryptBytes(sampleData, cert); - var decryptedBytes = DecryptBytes(encryptedBytes, cert); + var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); + var decryptedBytes = EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); Console.WriteLine($"SampleData: {sampleData.Length:#,0} bytes"); Console.WriteLine($"Encrypted: {encryptedBytes.Length:#,0} bytes"); Console.WriteLine($"Decrypted: {decryptedBytes.Length:#,0} bytes"); @@ -111,72 +105,6 @@ public static void ValidateEncryptionAndDecryptionOnce(X509Certificate2 cert) if (!good) throw new Exception("Decrypted result doesn't match original data."); } - static void PrintCSP(X509Certificate2 cert) - { - Console.WriteLine($"Cert: {cert.Subject} / {cert.Thumbprint}"); - Console.WriteLine(); - - try - { - Console.WriteLine("cert.PublicKey.Key"); - var alg = cert.PublicKey.Key; - Console.WriteLine(alg.GetType().FullName); - Console.WriteLine(); - } - catch (Exception err) - { - PrintErrorSummary(err); - } - - try - { - // Fails in .NET Framework if -KeySpec Signature not specified. - // Works in .NET Core - Console.WriteLine("cert.PrivateKey"); - var alg = cert.PrivateKey; - Console.WriteLine(alg.GetType().FullName); - Console.WriteLine(); - } - catch (Exception err) - { - PrintErrorSummary(err); - } - - try - { - Console.WriteLine("cert.GetRSAPublicKey()"); - var alg = cert.GetRSAPublicKey(); - Console.WriteLine(alg.GetType().FullName); - Console.WriteLine(); - } - catch (Exception err) - { - PrintErrorSummary(err); - } - - try - { - Console.WriteLine("cert.GetRSAPrivateKey()"); - var alg = cert.GetRSAPrivateKey(); - Console.WriteLine(alg.GetType().FullName); - Console.WriteLine(); - } - catch (Exception err) - { - PrintErrorSummary(err); - } - - void PrintErrorSummary(Exception ex) - { - Console.WriteLine("ERROR:"); - while(null != ex) - { - Console.WriteLine($"[{ex.GetType().FullName}]"); - Console.WriteLine(ex.Message); - ex = ex.InnerException; - } - } - } static void BeginEncryptionLoop(X509Certificate2 cert, int maxIterations) { @@ -195,8 +123,8 @@ static void BeginLoop(X509Certificate2 cert, int maxIterations, bool encrypt = f Console.WriteLine($"MaxIterations: {maxIterations:#,0}"); Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); - var encryptedBytes = EncryptBytes(sampleData, cert); - var decryptedBytes = DecryptBytes(encryptedBytes, cert); + var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); + var decryptedBytes = EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); var counter = 0; var elapsed = Stopwatch.StartNew(); @@ -207,8 +135,8 @@ static void BeginLoop(X509Certificate2 cert, int maxIterations, bool encrypt = f while (counter++ <= maxIterations) { - if (encrypt) EncryptBytes(decryptedBytes, cert); - if (decrypt) DecryptBytes(encryptedBytes, cert); + if (encrypt) EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, decryptedBytes); + if (decrypt) EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); if (nextStatusUpdate < DateTime.Now) { @@ -217,34 +145,11 @@ static void BeginLoop(X509Certificate2 cert, int maxIterations, bool encrypt = f nextStatusUpdate = DateTime.Now.Add(statusUpdateInterval); } } - rate = counter / elapsed.Elapsed.TotalSeconds; Console.WriteLine("Finished."); Console.WriteLine($"{elapsed.Elapsed:hh\\:mm\\:ss} @ {rate:#,0} per-sec. Iterations: {counter:#,0} (Use Ctrl-C to quit...)"); } - - static byte[] EncryptBytes(byte[] inputData, X509Certificate2 cert) - { - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream()) - { - cert.EncryptStream(input, output); - output.Flush(); - return output.ToArray(); - } - } - - static byte[] DecryptBytes(byte[] inputData, X509Certificate2 cert) - { - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) - { - cert.DecryptStream(input, output); - output.Flush(); - return output.ToArray(); - } - } - - + } + public class EnduranceTestResult { } } From 4865c24f0ad81da3fa9216eee2b2c7c1b5e22343 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 09:26:35 -0400 Subject: [PATCH 38/52] Build fixes - app.settings case issue on copy --- src/UnitTests/UnitTests.csproj | 2 +- .../X509.EnduranceTest.NetFramework.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index 2ff007e..debb9c6 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -24,7 +24,7 @@ - + PreserveNewest diff --git a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj index f18b55b..71a6944 100644 --- a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj +++ b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj @@ -56,7 +56,7 @@ - + From bb0dd37d1925d78cfc69fb78a7e344b7fc2eec43 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 09:29:34 -0400 Subject: [PATCH 39/52] Build issue - case of App.settings --- src/UnitTests/UnitTests.csproj | 2 +- .../X509.EnduranceTest.NetFramework.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/UnitTests/UnitTests.csproj b/src/UnitTests/UnitTests.csproj index debb9c6..e227585 100644 --- a/src/UnitTests/UnitTests.csproj +++ b/src/UnitTests/UnitTests.csproj @@ -51,6 +51,6 @@ --> - + \ No newline at end of file diff --git a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj index 71a6944..f18b55b 100644 --- a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj +++ b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj @@ -56,7 +56,7 @@ - + From db916d3ecdd06949ede6c2763ec88b5f06f28d7f Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 10:49:55 -0400 Subject: [PATCH 40/52] Endurance Tests - Refactored the Endurance Test runner for future endurance tests --- .github/workflows/dotnet.yml | 4 +- src/X509.EnduranceTest.NetCore/Program.cs | 7 +- .../Program.cs | 3 +- .../X509.EnduranceTest.NetFramework.csproj | 3 + .../packages.config | 1 + .../EnduranceTestRunner.cs | 41 +++++++ .../HackForCSharp9.cs | 20 ++++ src/X509.EnduranceTest.Shared/TestMain.cs | 109 ++++-------------- .../X509.EnduranceTest.Shared.csproj | 5 +- 9 files changed, 100 insertions(+), 93 deletions(-) create mode 100644 src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs create mode 100644 src/X509.EnduranceTest.Shared/HackForCSharp9.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 32f1288..e8f6ed9 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -22,12 +22,12 @@ jobs: run: dotnet build src/UnitTests/UnitTests.csproj --no-restore - name: Test With Code Coverage 🧪 run: dotnet test src/UnitTests/UnitTests.csproj --no-build --framework Net5.0 /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:Threshold=80 /p:ThresholdType=line /p:Exclude="[*]X509.EnduranceTest.Shared*" --verbosity normal - - name: UploadToCodeCov ⇪ + - name: Upload Coverage To CodeCov.io ⇪ uses: codecov/codecov-action@v2 with: files: src/UnitTests/coverage.Net5.0.opencover.xml verbose: true - - name: Deploy 🚀 + - name: Deploy Docs to GitHug Pages 🚀 uses: JamesIves/github-pages-deploy-action@releases/v3 with: GITHUB_TOKEN: $ diff --git a/src/X509.EnduranceTest.NetCore/Program.cs b/src/X509.EnduranceTest.NetCore/Program.cs index 6ade0c4..8490fba 100644 --- a/src/X509.EnduranceTest.NetCore/Program.cs +++ b/src/X509.EnduranceTest.NetCore/Program.cs @@ -1,16 +1,17 @@  using System; +using System.Threading.Tasks; namespace X509.EnduranceTest { class Program { - static void Main(string[] args) + async static Task Main(string[] args) { - X509.EnduranceTest.Shared.TestMain.Run(); + await X509.EnduranceTest.Shared.TestMain.Run(); Console.WriteLine("Press ENTER to quit..."); Console.ReadLine(); } } -} +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.NetFramework/Program.cs b/src/X509.EnduranceTest.NetFramework/Program.cs index 6ade0c4..b2dab2d 100644 --- a/src/X509.EnduranceTest.NetFramework/Program.cs +++ b/src/X509.EnduranceTest.NetFramework/Program.cs @@ -1,11 +1,12 @@  using System; +using System.Threading.Tasks; namespace X509.EnduranceTest { class Program { - static void Main(string[] args) + static async Task Main(string[] args) { X509.EnduranceTest.Shared.TestMain.Run(); diff --git a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj index f18b55b..b369458 100644 --- a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj +++ b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj @@ -34,6 +34,9 @@ 4 + + ..\..\packages\EasyConsoleStd.2.0.0\lib\netstandard2.0\EasyConsole.dll + diff --git a/src/X509.EnduranceTest.NetFramework/packages.config b/src/X509.EnduranceTest.NetFramework/packages.config index 8ee555d..12ba138 100644 --- a/src/X509.EnduranceTest.NetFramework/packages.config +++ b/src/X509.EnduranceTest.NetFramework/packages.config @@ -1,5 +1,6 @@  + diff --git a/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs b/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs new file mode 100644 index 0000000..f88fff2 --- /dev/null +++ b/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs @@ -0,0 +1,41 @@ + +using System; +using System.Diagnostics; + +namespace X509.EnduranceTest.Shared +{ + internal class EnduranceTestRunner + { + internal static EnduranceTestResult BeginLoop( int maxIterations, Action actionToTest) + { + Console.WriteLine($"MaxIterations: {maxIterations:#,0}"); + + var counter = 0; + var elapsed = Stopwatch.StartNew(); + var statusUpdateInterval = TimeSpan.FromSeconds(2); + var nextStatusUpdate = DateTime.Now.Add(statusUpdateInterval); + + var rate = 0.0; + + while (counter++ <= maxIterations) + { + actionToTest(); + if (nextStatusUpdate < DateTime.Now) + { + rate = counter / elapsed.Elapsed.TotalSeconds; + Console.WriteLine($"{elapsed.Elapsed:hh\\:mm\\:ss} @ {rate:#,0} per-sec. Iterations: {counter:#,0} (Use Ctrl-C to quit...)"); + nextStatusUpdate = DateTime.Now.Add(statusUpdateInterval); + } + } + rate = counter / elapsed.Elapsed.TotalSeconds; + Console.WriteLine("Finished."); + Console.WriteLine($"{elapsed.Elapsed:hh\\:mm\\:ss} @ {rate:#,0} per-sec. Iterations: {counter:#,0} (Use Ctrl-C to quit...)"); + return new EnduranceTestResult(elapsed.Elapsed,rate,counter); + } + } + public record EnduranceTestResult (TimeSpan Elapsed,double iteractionsPerSecond, int iterationsCompleted); +} + +namespace System.Runtime.CompilerServices +{ +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/HackForCSharp9.cs b/src/X509.EnduranceTest.Shared/HackForCSharp9.cs new file mode 100644 index 0000000..18e3496 --- /dev/null +++ b/src/X509.EnduranceTest.Shared/HackForCSharp9.cs @@ -0,0 +1,20 @@ +/// +/// Hack to get C# 9.0 compiled for .Net Framework projects +/// https://blog.ndepend.com/using-c9-record-and-init-property-in-your-net-framework-4-x-net-standard-and-net-core-projects/ +/// +/// +/// In .Net 5 the below class is already present. So better do conditional compilation for below code. +/// +namespace System.Runtime.CompilerServices +{ + using System.ComponentModel; + + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/TestMain.cs b/src/X509.EnduranceTest.Shared/TestMain.cs index 45159c5..76ca03b 100644 --- a/src/X509.EnduranceTest.Shared/TestMain.cs +++ b/src/X509.EnduranceTest.Shared/TestMain.cs @@ -1,12 +1,13 @@  using System; using System.Configuration; -using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; - +using System.Threading; +using System.Threading.Tasks; +using EasyConsole; using Org.Security.Cryptography; using UnitTests; @@ -15,7 +16,7 @@ namespace X509.EnduranceTest.Shared public static class TestMain { #region AppSettings - + static int SampleDataSizeKB => Convert.ToInt32(AppSetting("SampleDataSizeKB")); static int LoopCount => Convert.ToInt32(AppSetting("LoopCount")); @@ -23,11 +24,11 @@ public static class TestMain #endregion - public static void Run() + async public static Task Run() { try { - PrintOptionsAndRunTest(); + await PrintOptionsAndRunTestAsync(); } catch (Exception err) { @@ -41,53 +42,16 @@ public static void Run() Console.WriteLine(topError.StackTrace); } } - - static void PrintOptionsAndRunTest() + async static Task PrintOptionsAndRunTestAsync() { - while (true) - { - Console.WriteLine("-------------------------------------------"); - Console.WriteLine("[P] Print AsymmetricAlgorithm provider."); - Console.WriteLine("[V] Validate Encryption/Decryption, ONCE."); - Console.WriteLine("[E] Start ENcryption loop."); - Console.WriteLine("[D] Start DEcryption loop."); - Console.WriteLine("[Q] or Ctrl-C to quit"); - Console.WriteLine("-------------------------------------------"); - - var input = (Console.ReadLine() ?? string.Empty).Trim().ToUpper(); - - var cert = MyConfig.DecryptionCertificate; - - switch (input) - { - case "V": - ValidateEncryptionAndDecryptionOnce(cert); - break; - - case "E": - BeginEncryptionLoop(cert, LoopCount); - break; - - case "D": - BeginDecryptionLoop(cert, LoopCount); - break; + var menu = new Menu() + .AddSync("Print AsymmetricAlgorithm provider.", () => ConsoleWriter.PrintCSP(MyConfig.DecryptionCertificate)) + .AddSync("Validate Encryption/Decryption, ONCE.", () => ValidateEncryptionAndDecryptionOnce(MyConfig.DecryptionCertificate)) + .AddSync("Start ENcryption loop", () => BeginEncryptionLoop(MyConfig.DecryptionCertificate, LoopCount)) + .AddSync("Start DEcryption loop", () => BeginDecryptionLoop(MyConfig.DecryptionCertificate, LoopCount)); - case "P": - ConsoleWriter.PrintCSP(cert); - break; - - case "Q": - return; - - default: - ValidateEncryptionAndDecryptionOnce(cert); - break; - } - - Console.WriteLine(); - } + await menu.Display(CancellationToken.None); } - public static void ValidateEncryptionAndDecryptionOnce(X509Certificate2 cert) { var sampleData = TestDataGenerator.GenerateJunk(SampleDataSizeKB); @@ -104,52 +68,25 @@ public static void ValidateEncryptionAndDecryptionOnce(X509Certificate2 cert) var good = sampleData.SequenceEqual(decryptedBytes); if (!good) throw new Exception("Decrypted result doesn't match original data."); } - - static void BeginEncryptionLoop(X509Certificate2 cert, int maxIterations) { - BeginLoop(cert, maxIterations, encrypt: true); - } + var sampleData = TestDataGenerator.GenerateJunk(SampleDataSizeKB); + Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); + var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); + var decryptedBytes = EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); - static void BeginDecryptionLoop(X509Certificate2 cert, int maxIterations) - { - BeginLoop(cert, maxIterations, decrypt: true); + var result= EnduranceTestRunner.BeginLoop( maxIterations, () => EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, decryptedBytes)); } - static void BeginLoop(X509Certificate2 cert, int maxIterations, bool encrypt = false, bool decrypt = false) + static void BeginDecryptionLoop(X509Certificate2 cert, int maxIterations) { var sampleData = TestDataGenerator.GenerateJunk(SampleDataSizeKB); - - Console.WriteLine($"MaxIterations: {maxIterations:#,0}"); Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); - var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); - var decryptedBytes = EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); - - var counter = 0; - var elapsed = Stopwatch.StartNew(); - var statusUpdateInterval = TimeSpan.FromSeconds(2); - var nextStatusUpdate = DateTime.Now.Add(statusUpdateInterval); - - var rate = 0.0; - - while (counter++ <= maxIterations) - { - if (encrypt) EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, decryptedBytes); - if (decrypt) EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); - - if (nextStatusUpdate < DateTime.Now) - { - rate = counter / elapsed.Elapsed.TotalSeconds; - Console.WriteLine($"{elapsed.Elapsed:hh\\:mm\\:ss} @ {rate:#,0} per-sec. Iterations: {counter:#,0} (Use Ctrl-C to quit...)"); - nextStatusUpdate = DateTime.Now.Add(statusUpdateInterval); - } - } - rate = counter / elapsed.Elapsed.TotalSeconds; - Console.WriteLine("Finished."); - Console.WriteLine($"{elapsed.Elapsed:hh\\:mm\\:ss} @ {rate:#,0} per-sec. Iterations: {counter:#,0} (Use Ctrl-C to quit...)"); + EnduranceTestRunner.BeginLoop( maxIterations, () => EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes)); } + + } - public class EnduranceTestResult { - } + } diff --git a/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj b/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj index 2b4d646..85a663a 100644 --- a/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj +++ b/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj @@ -3,8 +3,11 @@ netstandard2.0 - + + 9.0 + + From 155e57466d7cc8acd5f7ffd4add4fa019143ff8d Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 13:27:04 -0400 Subject: [PATCH 41/52] Added more Endurance tests and cleared warnings --- .../X509CertificateBasedDecryptor.cs | 10 ++- .../X509Certificate2_CreateSignature.cs | 14 ++-- .../X509Certificate2_VerifySignature.cs | 42 ++++------ .../SignAndVerify/X509SignatureTests.cs | 22 +++-- ...or_DecryptStreamWithTimestampValidation.cs | 4 +- ...CertificateBasedEncryptor_EncryptStream.cs | 4 - .../X509.EnduranceTest.NetCore.csproj | 2 - .../Program.cs | 5 +- .../X509.EnduranceTest.NetFramework.csproj | 4 + .../packages.config | 1 + .../ConsoleWriter.cs | 16 +++- .../EncryptionDecryptionUtils.cs | 82 ++++++++----------- .../EnduranceTestRunner.cs | 9 +- src/X509.EnduranceTest.Shared/MyConfig.cs | 1 - .../MyConfigForEnduranceTests.cs | 14 ++++ .../TestDataGenerator.cs | 20 ++--- src/X509.EnduranceTest.Shared/TestMain.cs | 67 +++++---------- .../X509.EnduranceTest.Shared.csproj | 1 + ...509Certificate2ExtensionsEnduranceTests.cs | 28 +++++++ ...CertificateBasedDecryptorEnduranceTests.cs | 22 +++++ ...CertificateBasedEncryptorEnduranceTests.cs | 19 +++++ .../X509CertificateCache.cs | 3 +- 22 files changed, 217 insertions(+), 173 deletions(-) create mode 100644 src/X509.EnduranceTest.Shared/MyConfigForEnduranceTests.cs create mode 100644 src/X509.EnduranceTest.Shared/X509Certificate2ExtensionsEnduranceTests.cs create mode 100644 src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs create mode 100644 src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs diff --git a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs index 364b3a6..9449209 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509CertificateBasedDecryptor.cs @@ -66,12 +66,20 @@ public void DecryptStreamWithTimestampValidation( public string DecryptBase64EncodedStringWithTimestampValidation( string valueToDecode, Func certificateSelector) + { + return this.DecryptBase64EncodedStringWithTimestampValidation(valueToDecode, certificateSelector, Defaults.EncyptedPayloadTimeSpan); + } + public string DecryptBase64EncodedStringWithTimestampValidation( + string valueToDecode, + Func certificateSelector, + TimeSpan lifeSpanOfInput, + string dataEncryptionAlgorithmName = Defaults.DEF_DataEncryptionAlgorithmName) { var inputData = Convert.FromBase64String(valueToDecode); using (var input = new MemoryStream(inputData)) using (var output = new MemoryStream(inputData.Length)) { - this.DecryptStreamWithTimestampValidation(input, output, certificateSelector); + this.DecryptStreamWithTimestampValidation(input, output, certificateSelector,lifeSpanOfInput,dataEncryptionAlgorithmName); output.Flush(); var outputArray = output.ToArray(); return Encoding.UTF8.GetString(outputArray); diff --git a/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs b/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs index ccba14c..54de869 100644 --- a/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs +++ b/src/UnitTests/SignAndVerify/X509Certificate2_CreateSignature.cs @@ -22,11 +22,9 @@ public void WhenCertificateIsNull_ThrowArgumentNullException() var payload = Encoding.UTF8.GetBytes(TestData); X509Certificate2 certificate2 = null; //Act - using (var hashAlgorithm = HashAlgorithm.Create("MD5")) - { - var hash = hashAlgorithm.ComputeHash(payload); - certificate2.CreateSignature(hash); - } + using var hashAlgorithm = HashAlgorithm.Create("MD5"); + var hash = hashAlgorithm.ComputeHash(payload); + certificate2.CreateSignature(hash); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] @@ -34,10 +32,8 @@ public void WhenPayloadIsNull_ThrowArgumentNullException() { //arrange //Act - using (var hashAlgorithm = HashAlgorithm.Create("MD5")) - { - MyConfig.SigningCertificate.CreateSignature(null); - } + using var hashAlgorithm = HashAlgorithm.Create("MD5"); + MyConfig.SigningCertificate.CreateSignature(null); } #endregion } diff --git a/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs b/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs index 0956729..4adca0b 100644 --- a/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs +++ b/src/UnitTests/SignAndVerify/X509Certificate2_VerifySignature.cs @@ -20,15 +20,13 @@ public void WhenCertificateIsNull_ThrowArgumentNullException() //Arrange const string TestData = "Hello world"; var payload = Encoding.UTF8.GetBytes(TestData); - using (var hashAlgorithm = HashAlgorithm.Create("MD5")) - { - var hash = hashAlgorithm.ComputeHash(payload); - var signature = MyConfig.SigningCertificate.CreateSignature(hash); - X509Certificate2 certificate2 = null; - // Act - var good = certificate2.VerifySignature(hash, signature); - Assert.IsTrue(good); - } + using var hashAlgorithm = HashAlgorithm.Create("MD5"); + var hash = hashAlgorithm.ComputeHash(payload); + var signature = MyConfig.SigningCertificate.CreateSignature(hash); + X509Certificate2 certificate2 = null; + // Act + var good = certificate2.VerifySignature(hash, signature); + Assert.IsTrue(good); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] @@ -37,13 +35,11 @@ public void WhenSignatureIsNull_ThrowArgumentNullException() //Arrange const string TestData = "Hello world"; var payload = Encoding.UTF8.GetBytes(TestData); - using (var hashAlgorithm = HashAlgorithm.Create("MD5")) - { - var hash = hashAlgorithm.ComputeHash(payload); - // Act - var good = MyConfig.VerifyCertificate.VerifySignature(hash, null); - Assert.IsTrue(good); - } + using var hashAlgorithm = HashAlgorithm.Create("MD5"); + var hash = hashAlgorithm.ComputeHash(payload); + // Act + var good = MyConfig.VerifyCertificate.VerifySignature(hash, null); + Assert.IsTrue(good); } [TestMethod] [ExpectedException(typeof(ArgumentNullException))] @@ -52,14 +48,12 @@ public void WhenHashIsNull_ThrowArgumentNullException() //Arrange const string TestData = "Hello world"; var payload = Encoding.UTF8.GetBytes(TestData); - using (var hashAlgorithm = HashAlgorithm.Create("MD5")) - { - var hash = hashAlgorithm.ComputeHash(payload); - var signature = MyConfig.SigningCertificate.CreateSignature(hash); - // Act - var good = MyConfig.VerifyCertificate.VerifySignature(null, signature); - Assert.IsTrue(good); - } + using var hashAlgorithm = HashAlgorithm.Create("MD5"); + var hash = hashAlgorithm.ComputeHash(payload); + var signature = MyConfig.SigningCertificate.CreateSignature(hash); + // Act + var good = MyConfig.VerifyCertificate.VerifySignature(null, signature); + Assert.IsTrue(good); } #endregion } diff --git a/src/UnitTests/SignAndVerify/X509SignatureTests.cs b/src/UnitTests/SignAndVerify/X509SignatureTests.cs index c3f6bce..6a46e62 100644 --- a/src/UnitTests/SignAndVerify/X509SignatureTests.cs +++ b/src/UnitTests/SignAndVerify/X509SignatureTests.cs @@ -23,20 +23,18 @@ public void TestSignature() foreach (var name in HashAlgorithmNames) { - using (var hashAlgorithm = HashAlgorithm.Create(name)) - { - // Digest - var hash = hashAlgorithm.ComputeHash(payload); - Console.WriteLine($"Hash: {name} {hash.Length * 8} bits / {hash.Length} BYTES"); + using var hashAlgorithm = HashAlgorithm.Create(name); + // Digest + var hash = hashAlgorithm.ComputeHash(payload); + Console.WriteLine($"Hash: {name} {hash.Length * 8} bits / {hash.Length} BYTES"); - // Sign - var signature = MyConfig.SigningCertificate.CreateSignature(hash); - Console.WriteLine($"Signature: {signature.Length * 8} bits / {signature.Length} BYTES"); + // Sign + var signature = MyConfig.SigningCertificate.CreateSignature(hash); + Console.WriteLine($"Signature: {signature.Length * 8} bits / {signature.Length} BYTES"); - // Verify - var good = MyConfig.VerifyCertificate.VerifySignature(hash, signature); - Assert.IsTrue(good); - } + // Verify + var good = MyConfig.VerifyCertificate.VerifySignature(hash, signature); + Assert.IsTrue(good); } } } diff --git a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs index 197e896..c0ca9cd 100644 --- a/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs +++ b/src/UnitTests/X509CertificateBasedDecryptor_DecryptStreamWithTimestampValidation.cs @@ -112,11 +112,11 @@ public void WhenDecryptionPayloadDontHaveTimestamp_ThrowException() byte[] decryptedOutput = EncryptionDecryptionUtils.DecryptBytesWithTimestampValidationUsingX509CertificateBasedDecryptor( encryptedArray, thumbprint => MyConfig.DecryptionCertificate); - }catch(ArgumentOutOfRangeException aoorEx) + }catch (ArgumentOutOfRangeException) { exceptionThrown = true; } - catch(InvalidOperationException ioEx) + catch(InvalidOperationException) { exceptionThrown = true; } diff --git a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs index b54ec0f..a14ab52 100644 --- a/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs +++ b/src/UnitTests/X509CertificateBasedEncryptor_EncryptStream.cs @@ -27,8 +27,6 @@ public void WhenEncryptionCertificateParameterIsNull_ThrowArgumentNullException( public void WhenTheInputStreamParameterIsNull_ThrowArgumentNullException() { //Arrange - const string TEST = "Hello World!"; - byte[] input = Encoding.UTF8.GetBytes(TEST); //Act new X509CertificateBasedEncryptor().EncryptStream(MyConfig.EncryptionCertificate, null, new MemoryStream()); //Assert @@ -38,8 +36,6 @@ public void WhenTheInputStreamParameterIsNull_ThrowArgumentNullException() public void WhenTheOutputStreamParameterIsNull_ThrowArgumentNullException() { //Arrange - const string TEST = "Hello World!"; - byte[] input = Encoding.UTF8.GetBytes(TEST); //Act new X509CertificateBasedEncryptor().EncryptStream(MyConfig.EncryptionCertificate, new MemoryStream(),null); //Assert diff --git a/src/X509.EnduranceTest.NetCore/X509.EnduranceTest.NetCore.csproj b/src/X509.EnduranceTest.NetCore/X509.EnduranceTest.NetCore.csproj index 97a509d..b124545 100644 --- a/src/X509.EnduranceTest.NetCore/X509.EnduranceTest.NetCore.csproj +++ b/src/X509.EnduranceTest.NetCore/X509.EnduranceTest.NetCore.csproj @@ -13,6 +13,4 @@ - - diff --git a/src/X509.EnduranceTest.NetFramework/Program.cs b/src/X509.EnduranceTest.NetFramework/Program.cs index b2dab2d..2135eee 100644 --- a/src/X509.EnduranceTest.NetFramework/Program.cs +++ b/src/X509.EnduranceTest.NetFramework/Program.cs @@ -8,10 +8,7 @@ class Program { static async Task Main(string[] args) { - X509.EnduranceTest.Shared.TestMain.Run(); - - Console.WriteLine("Press ENTER to quit..."); - Console.ReadLine(); + await X509.EnduranceTest.Shared.TestMain.Run(); } } } diff --git a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj index b369458..49fa660 100644 --- a/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj +++ b/src/X509.EnduranceTest.NetFramework/X509.EnduranceTest.NetFramework.csproj @@ -34,9 +34,13 @@ 4 + + ..\..\packages\Bogus.33.0.2\lib\net40\Bogus.dll + ..\..\packages\EasyConsoleStd.2.0.0\lib\netstandard2.0\EasyConsole.dll + diff --git a/src/X509.EnduranceTest.NetFramework/packages.config b/src/X509.EnduranceTest.NetFramework/packages.config index 12ba138..2b2cc35 100644 --- a/src/X509.EnduranceTest.NetFramework/packages.config +++ b/src/X509.EnduranceTest.NetFramework/packages.config @@ -1,5 +1,6 @@  + diff --git a/src/X509.EnduranceTest.Shared/ConsoleWriter.cs b/src/X509.EnduranceTest.Shared/ConsoleWriter.cs index 9f5c4df..26cb424 100644 --- a/src/X509.EnduranceTest.Shared/ConsoleWriter.cs +++ b/src/X509.EnduranceTest.Shared/ConsoleWriter.cs @@ -61,7 +61,7 @@ internal static void PrintCSP(X509Certificate2 cert) PrintErrorSummary(err); } - void PrintErrorSummary(Exception ex) + static void PrintErrorSummary(Exception ex) { Console.WriteLine("ERROR:"); while (null != ex) @@ -72,6 +72,16 @@ void PrintErrorSummary(Exception ex) } } } - + internal static void WriteRecursively(Exception err) + { + var topError = err; + while (null != err) + { + Console.WriteLine($"[{err.GetType().FullName}]"); + Console.WriteLine(err.Message); + err = err.InnerException; + } + Console.WriteLine(topError.StackTrace); + } } -} +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs b/src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs index 9438441..2b1660a 100644 --- a/src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs +++ b/src/X509.EnduranceTest.Shared/EncryptionDecryptionUtils.cs @@ -11,13 +11,11 @@ public class EncryptionDecryptionUtils public static byte[] EncryptBytesWithTimestampUsingX509CertificateBasedEncryptor(X509Certificate2 x509Cert, byte[] inputData) { var encryptor = new X509CertificateBasedEncryptor(); - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) - { - encryptor.EncryptStreamWithTimestamp(x509Cert, input, output); - output.Flush(); - return output.ToArray(); - } + using var input = new MemoryStream(inputData); + using var output = new MemoryStream(inputData.Length); + encryptor.EncryptStreamWithTimestamp(x509Cert, input, output); + output.Flush(); + return output.ToArray(); } public static byte[] DecryptBytesWithTimestampValidationUsingX509CertificateBasedDecryptor( byte[] inputData, @@ -25,62 +23,52 @@ public static byte[] DecryptBytesWithTimestampValidationUsingX509CertificateBas TimeSpan? lifeSpan = null) { var decryptor = new X509CertificateBasedDecryptor(); - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) + using var input = new MemoryStream(inputData); + using var output = new MemoryStream(inputData.Length); + if (lifeSpan.HasValue) + { + decryptor.DecryptStreamWithTimestampValidation(input, output, certificateSelector, lifeSpan.Value); + } + else { - if (lifeSpan.HasValue) - { - decryptor.DecryptStreamWithTimestampValidation(input, output, certificateSelector, lifeSpan.Value); - } - else - { - decryptor.DecryptStreamWithTimestampValidation(input, output, certificateSelector); - } - output.Flush(); - return output.ToArray(); + decryptor.DecryptStreamWithTimestampValidation(input, output, certificateSelector); } + output.Flush(); + return output.ToArray(); } public static byte[] EncryptBytesUsingX509CertificateBasedEncryptor(X509Certificate2 x509Cert, byte[] inputData) { var encryptor = new X509CertificateBasedEncryptor(); - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) - { - encryptor.EncryptStream(x509Cert, input, output); - output.Flush(); - return output.ToArray(); - } + using var input = new MemoryStream(inputData); + using var output = new MemoryStream(inputData.Length); + encryptor.EncryptStream(x509Cert, input, output); + output.Flush(); + return output.ToArray(); } public static byte[] DecryptBytesUsingX509CertificateBasedDecryptor(byte[] inputData, Func certificateSelector) { var decryptor = new X509CertificateBasedDecryptor(); - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) - { - decryptor.DecryptStream(input, output, certificateSelector); - output.Flush(); - return output.ToArray(); - } + using var input = new MemoryStream(inputData); + using var output = new MemoryStream(inputData.Length); + decryptor.DecryptStream(input, output, certificateSelector); + output.Flush(); + return output.ToArray(); } public static byte[] EncryptBytesUsingExtensionMethod(X509Certificate2 x509Cert, byte[] inputData) { - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) - { - x509Cert.EncryptStream(input, output); - output.Flush(); - return output.ToArray(); - } + using var input = new MemoryStream(inputData); + using var output = new MemoryStream(inputData.Length); + x509Cert.EncryptStream(input, output); + output.Flush(); + return output.ToArray(); } public static byte[] DecryptBytesUsingExtensionMethod(X509Certificate2 x509Cert, byte[] inputData) { - using (var input = new MemoryStream(inputData)) - using (var output = new MemoryStream(inputData.Length)) - { - x509Cert.DecryptStream(input, output); - output.Flush(); - return output.ToArray(); - } + using var input = new MemoryStream(inputData); + using var output = new MemoryStream(inputData.Length); + x509Cert.DecryptStream(input, output); + output.Flush(); + return output.ToArray(); } } } \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs b/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs index f88fff2..24092a3 100644 --- a/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs +++ b/src/X509.EnduranceTest.Shared/EnduranceTestRunner.cs @@ -4,9 +4,11 @@ namespace X509.EnduranceTest.Shared { + public record EnduranceTestResult(TimeSpan Elapsed, double IteractionsPerSecond, int IterationsCompleted); + internal class EnduranceTestRunner { - internal static EnduranceTestResult BeginLoop( int maxIterations, Action actionToTest) + internal static EnduranceTestResult Run( int maxIterations, Action actionToTest) { Console.WriteLine($"MaxIterations: {maxIterations:#,0}"); @@ -33,9 +35,4 @@ internal static EnduranceTestResult BeginLoop( int maxIterations, Action actionT return new EnduranceTestResult(elapsed.Elapsed,rate,counter); } } - public record EnduranceTestResult (TimeSpan Elapsed,double iteractionsPerSecond, int iterationsCompleted); -} - -namespace System.Runtime.CompilerServices -{ } \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/MyConfig.cs b/src/X509.EnduranceTest.Shared/MyConfig.cs index eea4a8f..e30500d 100644 --- a/src/X509.EnduranceTest.Shared/MyConfig.cs +++ b/src/X509.EnduranceTest.Shared/MyConfig.cs @@ -23,6 +23,5 @@ public static class MyConfig public static X509Certificate2 DecryptionCertificate => CertificateLoader.LoadFromFile(ConfigurationManager.AppSettings["DecryptionCertificatePath"], MyConfig.TestCertficatePassword); public static X509Certificate2 VerifyCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.cer"); public static X509Certificate2 SigningCertificate => CertificateLoader.LoadFromFile("TestCertificates/hello.world.2048.net.pfx", MyConfig.TestCertficatePassword); - } } diff --git a/src/X509.EnduranceTest.Shared/MyConfigForEnduranceTests.cs b/src/X509.EnduranceTest.Shared/MyConfigForEnduranceTests.cs new file mode 100644 index 0000000..5f3b576 --- /dev/null +++ b/src/X509.EnduranceTest.Shared/MyConfigForEnduranceTests.cs @@ -0,0 +1,14 @@ + +using System; +using System.Configuration; + +namespace UnitTests +{ + internal static class MyConfigForEnduranceTests + { + internal static int SampleDataSizeKB => Convert.ToInt32(AppSetting("SampleDataSizeKB")); + + static string AppSetting(string name) => ConfigurationManager.AppSettings[name] ?? throw new Exception($"AppSetting 'name' not defined."); + + } +} diff --git a/src/X509.EnduranceTest.Shared/TestDataGenerator.cs b/src/X509.EnduranceTest.Shared/TestDataGenerator.cs index 9431926..d7687e8 100644 --- a/src/X509.EnduranceTest.Shared/TestDataGenerator.cs +++ b/src/X509.EnduranceTest.Shared/TestDataGenerator.cs @@ -10,19 +10,17 @@ public static byte[] GenerateJunk(int kiloBytes) { int maxBytes = kiloBytes * 1024; - using (var buffer = new MemoryStream(maxBytes)) - { - var bytesWritten = 0; + using var buffer = new MemoryStream(maxBytes); + var bytesWritten = 0; - while (bytesWritten < maxBytes) - { - var more = Guid.NewGuid().ToByteArray(); - buffer.Write(more, 0, more.Length); - bytesWritten += more.Length; - } - buffer.Flush(); - return buffer.ToArray(); + while (bytesWritten < maxBytes) + { + var more = Guid.NewGuid().ToByteArray(); + buffer.Write(more, 0, more.Length); + bytesWritten += more.Length; } + buffer.Flush(); + return buffer.ToArray(); } } } diff --git a/src/X509.EnduranceTest.Shared/TestMain.cs b/src/X509.EnduranceTest.Shared/TestMain.cs index 76ca03b..e4f6124 100644 --- a/src/X509.EnduranceTest.Shared/TestMain.cs +++ b/src/X509.EnduranceTest.Shared/TestMain.cs @@ -15,15 +15,6 @@ namespace X509.EnduranceTest.Shared { public static class TestMain { - #region AppSettings - - static int SampleDataSizeKB => Convert.ToInt32(AppSetting("SampleDataSizeKB")); - static int LoopCount => Convert.ToInt32(AppSetting("LoopCount")); - - static string AppSetting(string name) => ConfigurationManager.AppSettings[name] ?? throw new Exception($"AppSetting 'name' not defined."); - - #endregion - async public static Task Run() { try @@ -32,29 +23,35 @@ async public static Task Run() } catch (Exception err) { - var topError = err; - while (null != err) - { - Console.WriteLine($"[{err.GetType().FullName}]"); - Console.WriteLine(err.Message); - err = err.InnerException; - } - Console.WriteLine(topError.StackTrace); + ConsoleWriter.WriteRecursively(err); } } async static Task PrintOptionsAndRunTestAsync() { - var menu = new Menu() - .AddSync("Print AsymmetricAlgorithm provider.", () => ConsoleWriter.PrintCSP(MyConfig.DecryptionCertificate)) - .AddSync("Validate Encryption/Decryption, ONCE.", () => ValidateEncryptionAndDecryptionOnce(MyConfig.DecryptionCertificate)) - .AddSync("Start ENcryption loop", () => BeginEncryptionLoop(MyConfig.DecryptionCertificate, LoopCount)) - .AddSync("Start DEcryption loop", () => BeginDecryptionLoop(MyConfig.DecryptionCertificate, LoopCount)); + bool canContinue= true; + while (canContinue) + { + var menu = new Menu() + .AddSync("Print AsymmetricAlgorithm provider.", () => ConsoleWriter.PrintCSP(MyConfig.DecryptionCertificate)) + .AddSync("Validate Encryption/Decryption, ONCE.", () => ValidateEncryptionAndDecryptionOnce(MyConfig.DecryptionCertificate)) + .AddSync("X509Certificate2.EncryptStream - 8KB random data 100,000 times", () => X509Certificate2ExtensionsEnduranceTests.Encryption(MyConfig.DecryptionCertificate,8, 100000)) + .AddSync("X509Certificate2.DecryptStream - 8KB random data 100,000 times", () => X509Certificate2ExtensionsEnduranceTests.Decryption(MyConfig.DecryptionCertificate,8, 100000)) - await menu.Display(CancellationToken.None); + .AddSync("X509CertificateBasedEncryptor.EncryptStringToBase64WithTimestamp - Random 256 bytes, 100,000", () => X509CertificateBasedEncryptorEnduranceTests.EncryptStringToBase64WithTimestamp(.25, 100000)) + .AddSync("X509CertificateBasedDecryptor.DecryptBase64EncodedStringWithTimestampValidation - Random 256 bytes, 100,000", () => X509CertificateBasedDecryptorEnduranceTests.DecryptStringToBase64WithTimestamp(.25, 100000)) + .AddSync("X509CertificateBasedEncryptor.EncryptStringToBase64WithTimestamp - Random 1 KB, 100,000", () => X509CertificateBasedEncryptorEnduranceTests.EncryptStringToBase64WithTimestamp(1, 100000)) + .AddSync("X509CertificateBasedDecryptor.DecryptBase64EncodedStringWithTimestampValidation - Random 1 KB, 100,000", () => X509CertificateBasedDecryptorEnduranceTests.DecryptStringToBase64WithTimestamp(1, 100000)) + .AddSync("X509CertificateBasedEncryptor.EncryptStringToBase64WithTimestamp - Random 8KB, 100,000", () => X509CertificateBasedEncryptorEnduranceTests.EncryptStringToBase64WithTimestamp(8, 100000)) + .AddSync("X509CertificateBasedDecryptor.DecryptBase64EncodedStringWithTimestampValidation - Random 8KB, 100,000", () => X509CertificateBasedDecryptorEnduranceTests.DecryptStringToBase64WithTimestamp(8,100000)) + .AddSync("Exit",()=> canContinue = false); + await menu.Display(CancellationToken.None); + } + Console.WriteLine("Press ENTER to quit..."); + Console.ReadLine(); } public static void ValidateEncryptionAndDecryptionOnce(X509Certificate2 cert) { - var sampleData = TestDataGenerator.GenerateJunk(SampleDataSizeKB); + var sampleData = TestDataGenerator.GenerateJunk(MyConfigForEnduranceTests.SampleDataSizeKB); Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); // Encrypt/Decrypt ONCE... @@ -68,25 +65,5 @@ public static void ValidateEncryptionAndDecryptionOnce(X509Certificate2 cert) var good = sampleData.SequenceEqual(decryptedBytes); if (!good) throw new Exception("Decrypted result doesn't match original data."); } - static void BeginEncryptionLoop(X509Certificate2 cert, int maxIterations) - { - var sampleData = TestDataGenerator.GenerateJunk(SampleDataSizeKB); - Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); - var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); - var decryptedBytes = EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); - - var result= EnduranceTestRunner.BeginLoop( maxIterations, () => EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, decryptedBytes)); - } - - static void BeginDecryptionLoop(X509Certificate2 cert, int maxIterations) - { - var sampleData = TestDataGenerator.GenerateJunk(SampleDataSizeKB); - Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); - var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); - EnduranceTestRunner.BeginLoop( maxIterations, () => EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes)); - } - - } - -} +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj b/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj index 85a663a..e8c2f66 100644 --- a/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj +++ b/src/X509.EnduranceTest.Shared/X509.EnduranceTest.Shared.csproj @@ -7,6 +7,7 @@ 9.0 + diff --git a/src/X509.EnduranceTest.Shared/X509Certificate2ExtensionsEnduranceTests.cs b/src/X509.EnduranceTest.Shared/X509Certificate2ExtensionsEnduranceTests.cs new file mode 100644 index 0000000..f7a742b --- /dev/null +++ b/src/X509.EnduranceTest.Shared/X509Certificate2ExtensionsEnduranceTests.cs @@ -0,0 +1,28 @@ + +using System; +using System.Security.Cryptography.X509Certificates; +using UnitTests; + +namespace X509.EnduranceTest.Shared +{ + internal class X509Certificate2ExtensionsEnduranceTests + { + internal static void Encryption(X509Certificate2 cert, int dataSizeInKB, int maxIterations) + { + var sampleData = TestDataGenerator.GenerateJunk(dataSizeInKB); + Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); + var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); + var decryptedBytes = EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes); + + var result = EnduranceTestRunner.Run(maxIterations, () => EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, decryptedBytes)); + } + + internal static void Decryption(X509Certificate2 cert, int dataSizeInKB, int maxIterations) + { + var sampleData = TestDataGenerator.GenerateJunk(dataSizeInKB); + Console.WriteLine($"Generated {sampleData.Length / 1024} KB random binary data."); + var encryptedBytes = EncryptionDecryptionUtils.EncryptBytesUsingExtensionMethod(cert, sampleData); + EnduranceTestRunner.Run(maxIterations, () => EncryptionDecryptionUtils.DecryptBytesUsingExtensionMethod(cert, encryptedBytes)); + } + } +} diff --git a/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs b/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs new file mode 100644 index 0000000..36913d5 --- /dev/null +++ b/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs @@ -0,0 +1,22 @@ +using Bogus.DataSets; +using Org.Security.Cryptography; +using System; +using UnitTests; + +namespace X509.EnduranceTest.Shared +{ + internal class X509CertificateBasedDecryptorEnduranceTests + { + internal static void DecryptStringToBase64WithTimestamp(double dataSizeInKB,int loopCount) + { + var sampleData = new Lorem().Random.String2((int)(dataSizeInKB * 1024)); + Console.WriteLine($"Generated {sampleData.Length:0,#} bytes random binary data."); + var encryptor = new X509CertificateBasedEncryptor(); + var encryptedValue = encryptor.EncryptStringToBase64WithTimestamp(MyConfig.EncryptionCertificate, sampleData); + var decryptor = new X509CertificateBasedDecryptor(); + + var result = EnduranceTestRunner.Run(loopCount, + () => decryptor.DecryptBase64EncodedStringWithTimestampValidation(encryptedValue,(thumprint)=>MyConfig.DecryptionCertificate,TimeSpan.FromMinutes(5))); + } + } +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs b/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs new file mode 100644 index 0000000..b44625d --- /dev/null +++ b/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs @@ -0,0 +1,19 @@ +using Bogus.DataSets; +using Org.Security.Cryptography; +using System; +using UnitTests; + +namespace X509.EnduranceTest.Shared +{ + internal class X509CertificateBasedEncryptorEnduranceTests + { + internal static void EncryptStringToBase64WithTimestamp(double dataSizeInKB, int loopCount) + { + var sampleData = new Lorem().Random.String2((int)(dataSizeInKB * 1024)); + Console.WriteLine($"Generated {sampleData.Length:#,0} bytes random binary data."); + var encryptor = new X509CertificateBasedEncryptor(); + var result = EnduranceTestRunner.Run(loopCount, + () => encryptor.EncryptStringToBase64WithTimestamp(MyConfig.EncryptionCertificate, sampleData)); + } + } +} \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/X509CertificateCache.cs b/src/X509.EnduranceTest.Shared/X509CertificateCache.cs index e214ee1..d0bb07a 100644 --- a/src/X509.EnduranceTest.Shared/X509CertificateCache.cs +++ b/src/X509.EnduranceTest.Shared/X509CertificateCache.cs @@ -86,9 +86,8 @@ public static X509Certificate2 TryGetCertificate(string x509Thumbprint, StoreNam // Lookup the cache. var found = CertificateCache.TryGetValue(cacheKey, out var certFromCache); if (found && null != certFromCache) return certFromCache; - // Not in cache. Look in the store. - using (X509Store store = new X509Store(storeName, storeLocation)) + using (X509Store store = new(storeName, storeLocation)) { // Open an existing store. store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); From 8d6f0088aaf38b3ac9d9b36d07f64048b74c0bb5 Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 14:30:33 -0400 Subject: [PATCH 42/52] EnduranceTest - Cached the certificates to avoid retrieval everytime --- .../X509CertificateBasedDecryptorEnduranceTests.cs | 9 ++++++--- .../X509CertificateBasedEncryptorEnduranceTests.cs | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs b/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs index 36913d5..7cc1283 100644 --- a/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs +++ b/src/X509.EnduranceTest.Shared/X509CertificateBasedDecryptorEnduranceTests.cs @@ -10,13 +10,16 @@ internal class X509CertificateBasedDecryptorEnduranceTests internal static void DecryptStringToBase64WithTimestamp(double dataSizeInKB,int loopCount) { var sampleData = new Lorem().Random.String2((int)(dataSizeInKB * 1024)); - Console.WriteLine($"Generated {sampleData.Length:0,#} bytes random binary data."); + Console.WriteLine($"Generated {sampleData.Length:0,#} bytes random text data."); var encryptor = new X509CertificateBasedEncryptor(); - var encryptedValue = encryptor.EncryptStringToBase64WithTimestamp(MyConfig.EncryptionCertificate, sampleData); + var encryptonCertificate = MyConfig.EncryptionCertificate; + var decryptonCertificate = MyConfig.DecryptionCertificate; + + var encryptedValue = encryptor.EncryptStringToBase64WithTimestamp(encryptonCertificate, sampleData); var decryptor = new X509CertificateBasedDecryptor(); var result = EnduranceTestRunner.Run(loopCount, - () => decryptor.DecryptBase64EncodedStringWithTimestampValidation(encryptedValue,(thumprint)=>MyConfig.DecryptionCertificate,TimeSpan.FromMinutes(5))); + () => decryptor.DecryptBase64EncodedStringWithTimestampValidation(encryptedValue,(thumprint)=> decryptonCertificate, TimeSpan.FromMinutes(5))); } } } \ No newline at end of file diff --git a/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs b/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs index b44625d..3d922fb 100644 --- a/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs +++ b/src/X509.EnduranceTest.Shared/X509CertificateBasedEncryptorEnduranceTests.cs @@ -10,10 +10,11 @@ internal class X509CertificateBasedEncryptorEnduranceTests internal static void EncryptStringToBase64WithTimestamp(double dataSizeInKB, int loopCount) { var sampleData = new Lorem().Random.String2((int)(dataSizeInKB * 1024)); - Console.WriteLine($"Generated {sampleData.Length:#,0} bytes random binary data."); + Console.WriteLine($"Generated {sampleData.Length:#,0} bytes random text data."); var encryptor = new X509CertificateBasedEncryptor(); + var encryptionCertificate = MyConfig.EncryptionCertificate; var result = EnduranceTestRunner.Run(loopCount, - () => encryptor.EncryptStringToBase64WithTimestamp(MyConfig.EncryptionCertificate, sampleData)); + () => encryptor.EncryptStringToBase64WithTimestamp(encryptionCertificate, sampleData)); } } } \ No newline at end of file From 35f9e11352eaa50a5afa82a66514ab19b41b385d Mon Sep 17 00:00:00 2001 From: Joy George Kunjikkuru Date: Thu, 26 Aug 2021 20:15:34 -0400 Subject: [PATCH 43/52] Performance - Throughput increased with caching. --- ...ecurity.Cryptography.X509Extensions.csproj | 1 + .../X509AsymmetricAlgorithmExtensions.cs | 88 +++++++++++++------ .../App.config | 29 ++++-- .../X509.EnduranceTest.NetFramework.csproj | 31 +++++++ .../packages.config | 10 +++ src/X509.EnduranceTest.Shared/TestMain.cs | 5 +- 6 files changed, 127 insertions(+), 37 deletions(-) diff --git a/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj b/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj index 38f3176..aa863a3 100644 --- a/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj +++ b/src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj @@ -25,6 +25,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs b/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs index ec9debb..1daa553 100644 --- a/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs +++ b/src/Org.Security.Cryptography.X509Extensions/X509AsymmetricAlgorithmExtensions.cs @@ -1,4 +1,5 @@  +using Microsoft.Extensions.Caching.Memory; using System; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; @@ -7,6 +8,7 @@ namespace Org.Security.Cryptography { internal static class X509AsymmetricAlgorithmExtensions { + /// /// Returns an AsymmetricAlgorithm, representing the PublicKey /// @@ -14,50 +16,80 @@ internal static AsymmetricAlgorithm GetPublicKeyAsymmetricAlgorithm(this X509Cer { // Not sure what scenario the thumnprint will be null. If the cert is loaded it would have thumbprint if (null == x509Cert.Thumbprint) throw new ArgumentNullException("X509Certificate2.Thumbprint was NULL."); - try + return CacheManager.GetOrAdd($"{nameof(GetPublicKeyAsymmetricAlgorithm)}{x509Cert.Thumbprint}", (key) => { try { - // [FASTER] - return x509Cert.PublicKey?.Key ?? throw new Exception($"X509Certificate2.PublicKey?.Key was NULL."); + try + { + // [FASTER] + return x509Cert.PublicKey?.Key ?? throw new Exception($"X509Certificate2.PublicKey?.Key was NULL."); + } + catch (CryptographicException) + { + // [SLOWER] - Seems rare scenario + return x509Cert.GetRSAPublicKey() ?? throw new Exception($"X509Certificate2.GetRSAPublicKey() returned NULL"); + } } - catch (CryptographicException) + catch (Exception err) { - // [SLOWER] - Seems rare scenario - return x509Cert.GetRSAPublicKey() ?? throw new Exception($"X509Certificate2.GetRSAPublicKey() returned NULL"); + var msg = $"Error accessing PublicKey of the X509 Certificate. Cert: {x509Cert.Thumbprint}"; + throw new Exception(msg, err); } - } - catch (Exception err) - { - var msg = $"Error accessing PublicKey of the X509 Certificate. Cert: {x509Cert.Thumbprint}"; - throw new Exception(msg, err); - } + }); } /// /// Returns an AsymmetricAlgorithm, representing the PrivateKey /// + /// + /// Why the below complications? The PrivateKey caching and faster. Other one is not. + /// https://github.com/dotnet/runtime/issues/17269#issuecomment-218932128 + /// internal static AsymmetricAlgorithm GetPrivateKeyAsymmetricAlgorithm(this X509Certificate2 x509Cert) { if (null == x509Cert.Thumbprint) throw new ArgumentNullException("X509Certificate2.Thumbprint was NULL."); - try - { - try - { - // [FASTER] - return x509Cert.PrivateKey ?? throw new Exception($"X509Certificate2.PrivateKey was NULL."); - } - catch (CryptographicException) - { - // [SLOWER] - return x509Cert.GetRSAPrivateKey() ?? throw new Exception($"X509Certificate2.GetRSAPrivateKey() returned NULL."); - } - } - catch (Exception err) + + return CacheManager.GetOrAdd($"{nameof(GetPrivateKeyAsymmetricAlgorithm)}{x509Cert.Thumbprint}", (key) => + { + try + { + try + { + // [FASTER works in .Net Core 3.1] + return x509Cert.PrivateKey ?? throw new Exception($"X509Certificate2.PrivateKey was NULL."); + } + catch (CryptographicException) + { + // [SLOWER works in .Net Framework 4.8] + // Someone did analysis https://docs.microsoft.com/en-us/archive/blogs/alejacma/invalid-provider-type-specified-error-when-accessing-x509certificate2-privatekey-on-cng-certificates + return x509Cert.GetRSAPrivateKey() ?? throw new Exception($"X509Certificate2.GetRSAPrivateKey() returned NULL."); + } + } + catch (Exception err) + { + var msg = $"Error accessing PrivateKey of the X509 Certificate. Cert: {x509Cert.Thumbprint}"; + throw new Exception(msg, err); + } + + }); + } + } + static class CacheManager + { + static MemoryCache algorithmCache = new MemoryCache(new MemoryCacheOptions()); + + internal static TOut GetOrAdd(object key, Func valueFunction) + { + TOut outValue; + algorithmCache.TryGetValue(key, out outValue); + if (null == outValue) { - var msg = $"Error accessing PrivateKey of the X509 Certificate. Cert: {x509Cert.Thumbprint}"; - throw new Exception(msg, err); + outValue = valueFunction(key); + algorithmCache.Set(key, outValue); + } + return outValue; } } } diff --git a/src/X509.EnduranceTest.NetFramework/App.config b/src/X509.EnduranceTest.NetFramework/App.config index 7697ee2..e97533c 100644 --- a/src/X509.EnduranceTest.NetFramework/App.config +++ b/src/X509.EnduranceTest.NetFramework/App.config @@ -1,18 +1,33 @@ - + - - - - - + + + + + - + + + + + + + + + + + + + + + +