Skip to content

Commit b15ef5d

Browse files
committed
Added fall-back for .NET Framework. Endurance Test.
1 parent 5443d8a commit b15ef5d

20 files changed

+688
-291
lines changed

src/Org.Security.Cryptography.X509Extensions/GenerateSelfSignedCerts.ps1

Lines changed: 0 additions & 60 deletions
This file was deleted.

src/Org.Security.Cryptography.X509Extensions/Org.Security.Cryptography.X509Extensions.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@
55
<RootNamespace>Org.Security.Cryptography</RootNamespace>
66
<Version>20.3.0</Version>
77
<Authors>Venkatesh Ramakrishnan</Authors>
8-
<Company>OSS</Company>
8+
<Company>Open Source</Company>
99
<Product>X509Extensions</Product>
10-
<Description>Helpers for signing/encryption/decryption using X509 certs.</Description>
10+
<Description>Helpers for encryption/decryption/signing using X509 certs.</Description>
1111
<PackageProjectUrl>https://github.com/vplusplus/Org.Security.Cryptography.X509Extensions</PackageProjectUrl>
1212
<PackageTags>.NET Standard X509 Encrypt Decrypt DigitalSignature</PackageTags>
1313
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1414
<PackageLicenseExpression>MIT</PackageLicenseExpression>
15-
<PackageReleaseNotes>DISCLAIMER: Information security is a complex and sensitive area, and requires deep expertise. Author of this package is NOT an expert of this domain. Consumers are expected to review the source code first before using this package.</PackageReleaseNotes>
15+
<PackageReleaseNotes>Information security is a complex and sensitive area, and requires deep expertise. Review the source code first before using this package.</PackageReleaseNotes>
1616
</PropertyGroup>
1717

1818
</Project>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
2+
# ----------------------------------------------------------------------------------------
3+
# Standard disclaimer, so that I am not judged...
4+
# Use of self-signed=certs is NOT recommonded for real world application workloads.
5+
# ----------------------------------------------------------------------------------------
6+
7+
$dnsName = "hello.world.net"
8+
$outputPath = "D:\Junk"
9+
10+
# Output file names.
11+
$pfxFileName = "$outputPath\$dnsName.pfx"
12+
$cerFileName = "$outputPath\$dnsName.cer"
13+
$thumbprintFileName = "$outputPath\$dnsName.thumb.txt"
14+
15+
# Cert-validity, from yesterday, for two years...
16+
$dtStart = (Get-Date).ToUniversalTime().AddDays(-1).Date
17+
$dtEnd = $dtStart.AddYears(2)
18+
19+
# Collect password for the PFX file. Username is ignored.
20+
$pfxCredentials = Get-Credential -Message "Enter a password for the PFX" -UserName "$dnsName"
21+
22+
# NOTE: KeySpec was required to use Cert.PrivateKey in .Net Framework 4.7.1
23+
# NOTE: Alternative is to use cert.GetRSAPrivateKey()
24+
Write-Host "Creating (and registering) self-signed certificate. DNS Name: $dnsName"
25+
$cert = New-SelfSignedCertificate `
26+
-DnsName $dnsName `
27+
-CertStoreLocation Cert:\CurrentUser\My `
28+
-KeyLength 1024 `
29+
-NotBefore $dtStart `
30+
-NotAfter $dtEnd
31+
32+
# -KeySpec KeyExchange | Signature | None
33+
# -KeyExportPolicy Exportable | NonExportable
34+
# -KeyAlgorithm RSA
35+
# -KeyLength 1024 2048
36+
37+
# Retrieve and show cert info
38+
$thumbprint = $cert.Thumbprint
39+
$c = Get-Item Cert:\CurrentUser\My\$thumbprint
40+
Write-Host $c.ToString($true)
41+
42+
# Export to files...
43+
Write-Host "Exporting PFX, CER and thumbprint"
44+
Export-PfxCertificate -Cert $cert -FilePath $pfxFileName -Password $pfxCredentials.Password | Out-Null
45+
Export-Certificate -Cert $cert -FilePath $cerFileName | Out-Null
46+
Set-Content -Path $thumbprintFileName -Value $cert.Thumbprint | Out-Null
47+
48+
# Print the file names and the thumbprint
49+
Write-Host "--------------------------------------------------------------------"
50+
Write-Host "Generated..."
51+
Write-Host "--------------------------------------------------------------------"
52+
$pfxFileName
53+
$cerFileName
54+
$thumbprintFileName
55+
56+
# Print the certificate Thumbprint
57+
Write-Host "Thumbprint:"
58+
$cert.Thumbprint
59+
60+
# REMOVE CERT from local store.
61+
# Write-Host "--------------------------------------------------------------------"
62+
# Write-Host "IMP: Removing the self-signed-certificate from Cert:\CurrentUser\My"
63+
# Write-Host "IMP: Import the .pfx again in the target machine"
64+
# Write-Host "--------------------------------------------------------------------"
65+
# Get-ChildItem -Path cert:\CurrentUser\My\$thumbprint | Remove-Item
66+
# Write-Host "Removed the certificate."
67+
68+
# Use following command to import the PFX using powershell.
69+
# Import-PfxCertificate -Exportable -CertStoreLocation Cert:\LocalMachine\My -FilePath $pfxFileName -Password $securePassword
70+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
2+
3+
# List of certs
4+
$certs = Get-ChildItem -Path cert:\CurrentUser\My
5+
6+
# Print Thumbprint
7+
$certs
8+
9+
# Loop and print specific property
10+
Foreach ($c IN $certs)
11+
{
12+
Write-Host $c.SubjectName.Name
13+
Write-Host $c.Issuer
14+
Write-Host $c.Thumbprint
15+
Write-Host "HasPrivateKey?" $c.HasPrivateKey
16+
Write-Host "SignatureAlgorithm" $c.SignatureAlgorithm.FriendlyName
17+
}
18+
19+
# Find cert by Thumbprint, print details
20+
$thumb = "TheThumbprint"
21+
$c = Get-ChildItem -Path cert:\CurrentUser\My\$$thumb
22+
Write-Host $c.ToString($true)
23+
24+

src/Org.Security.Cryptography.X509Extensions/X509StreamEncryptionExtensions.cs

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11

22
using System;
3-
using System.Diagnostics;
43
using System.IO;
54
using System.Security.Cryptography;
65
using System.Security.Cryptography.X509Certificates;
@@ -71,17 +70,16 @@ public static void Encrypt(this Stream inputStream, Stream outputStream, X509Cer
7170
if (null == cert) throw new ArgumentNullException(nameof(cert));
7271
if (null == algName) throw new ArgumentNullException(nameof(algName));
7372

74-
// Ensure PublicKey exists
75-
if (null == cert.PublicKey) throw new ArgumentException($"X509Certificate2.PublicKey was NULL: {cert.Thumbprint}", nameof(cert));
76-
if (null == cert.PublicKey.Key) throw new ArgumentException($"X509Certificate2.PublicKey.Key was NULL: {cert.Thumbprint}", nameof(cert));
73+
// DO NOT Dispose this; Doing so will render the X509Certificate in cache use-less.
74+
var keyEncryption = cert.GetRsaPublicKeyAsymmetricAlgorithm();
7775

7876
using (var dataEncryption = SymmetricAlgorithm.Create(algName))
7977
{
8078
if (null == dataEncryption) throw new Exception($"SymmetricAlgorithm.Create() returned null. Check algName: '{algName}'");
8179

8280
dataEncryption.KeySize = keySize;
8381
dataEncryption.BlockSize = blockSize;
84-
Encrypt(inputStream, outputStream, cert.PublicKey.Key, dataEncryption);
82+
Encrypt(inputStream, outputStream, keyEncryption, dataEncryption);
8583
}
8684
}
8785

@@ -97,14 +95,14 @@ public static void Decrypt(this Stream inputStream, Stream outputStream, X509Cer
9795
if (null == cert) throw new ArgumentNullException(nameof(cert));
9896
if (null == algName) throw new ArgumentNullException(nameof(algName));
9997

100-
// Ensure PrivateKey exists.
101-
if (null == cert.PrivateKey) throw new ArgumentException($"X509Certificate2.PrivateKey was NULL: {cert.Thumbprint}", nameof(cert));
98+
// DO NOT Dispose this; Doing so will render the X509Certificate in cache use-less.
99+
var keyEncryption = cert.GetRsaPrivateKeyAsymmetricAlgorithm();
102100

103101
using (var dataEncryption = SymmetricAlgorithm.Create(algName))
104102
{
105103
if (null == dataEncryption) throw new Exception($"SymmetricAlgorithm.Create() returned null. Check algName: '{algName}'");
106104

107-
Decrypt(inputStream, outputStream, cert.PrivateKey, dataEncryption);
105+
Decrypt(inputStream, outputStream, keyEncryption, dataEncryption);
108106
}
109107
}
110108

@@ -165,6 +163,9 @@ static void Decrypt(Stream inputStream, Stream outputStream, AsymmetricAlgorithm
165163
}
166164
}
167165

166+
//...............................................................................
167+
#region Utils: WriteLengthAndBytes(), ReadLengthAndBytes()
168+
//...............................................................................
168169
static void WriteLengthAndBytes(this Stream outputStream, byte[] bytes)
169170
{
170171
if (null == outputStream) throw new ArgumentNullException(nameof(outputStream));
@@ -196,5 +197,63 @@ static byte[] ReadLengthAndBytes(this Stream inputStream, int maxBytes)
196197

197198
return bytes;
198199
}
200+
201+
#endregion
202+
203+
//...............................................................................
204+
#region Obtain private/public key AsymmetricAlgorithm
205+
//...............................................................................
206+
static AsymmetricAlgorithm GetRsaPublicKeyAsymmetricAlgorithm(this X509Certificate2 cert)
207+
{
208+
if (null == cert) throw new ArgumentNullException(nameof(cert));
209+
if (null == cert.Thumbprint) throw new ArgumentNullException("X509Certificate2.Thumbprint was NULL.");
210+
211+
try
212+
{
213+
try
214+
{
215+
// [FASTER]
216+
return cert.PublicKey?.Key ?? throw new Exception($"X509Certificate2.PublicKey?.Key was NULL.");
217+
}
218+
catch (CryptographicException)
219+
{
220+
// [SLOWER]
221+
return cert.GetRSAPublicKey() ?? throw new Exception($"X509Certificate2.GetRSAPublicKey() returned NULL");
222+
}
223+
}
224+
catch (Exception err)
225+
{
226+
var msg = $"Error accessing PublicKey of the X509 Certificate. Cert: {cert.Thumbprint}";
227+
throw new Exception(msg, err);
228+
}
229+
}
230+
231+
static AsymmetricAlgorithm GetRsaPrivateKeyAsymmetricAlgorithm(this X509Certificate2 cert)
232+
{
233+
if (null == cert) throw new ArgumentNullException(nameof(cert));
234+
if (null == cert.Thumbprint) throw new ArgumentNullException("X509Certificate2.Thumbprint was NULL.");
235+
236+
try
237+
{
238+
try
239+
{
240+
// [FASTER]
241+
return cert.PrivateKey ?? throw new Exception($"X509Certificate2.PrivateKey was NULL.");
242+
}
243+
catch (CryptographicException)
244+
{
245+
// [SLOWER]
246+
return cert.GetRSAPrivateKey() ?? throw new Exception($"X509Certificate2.GetRSAPrivateKey() returned NULL.");
247+
}
248+
}
249+
catch (Exception err)
250+
{
251+
var msg = $"Error accessing PrivateKey of the X509 Certificate. Cert: {cert.Thumbprint}";
252+
throw new Exception(msg, err);
253+
}
254+
}
255+
256+
#endregion
257+
199258
}
200259
}

src/UnitTests/App.config

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<configuration>
3+
4+
<appSettings>
5+
<add key="X509.ThumbPrint" value="F73299C92B327DB836170E9CF1C6AA948BFF5124" />
6+
</appSettings>
7+
8+
</configuration>
Lines changed: 4 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,19 @@
1-
using Microsoft.VisualStudio.TestTools.UnitTesting;
2-
using Org.Security.Cryptography;
1+

32
using System;
4-
using System.Collections.Generic;
3+
using Org.Security.Cryptography;
54
using System.Security.Cryptography;
65
using System.Security.Cryptography.X509Certificates;
7-
86
using System.Text;
97

8+
using Microsoft.VisualStudio.TestTools.UnitTesting;
9+
1010
namespace UnitTests.POCs
1111
{
1212
// https://docs.microsoft.com/en-us/dotnet/standard/security/cryptographic-signatures
1313

1414
[TestClass]
1515
public class SignatureSamples
1616
{
17-
// Sender
18-
// Message Or payload - The substance
19-
// Message Digest - A compact representation of the message
20-
// Encrypt the message digest with private key to create signature.
21-
//
22-
// Receiver:
23-
// Decrypt the signature using sender's public key
24-
// Hash the message or payload to recreate the message digest
25-
// Compare the hashses
26-
27-
// To verify that data was signed by a particular party, you must have the following information:
28-
// a) The public key of the party that signed the data.
29-
// b) The digital signature.
30-
// c) The data that was signed.
31-
// d) The hash algorithm used by the signer.
32-
33-
public void HelloSignature()
34-
{
35-
string something = "Hello World";
36-
37-
//The hash value to sign.
38-
byte[] hashValue = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(something));
39-
40-
//Generate a public/private key pair.
41-
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
42-
43-
//Create an RSAPKCS1SignatureFormatter object and pass it the
44-
//RSACryptoServiceProvider to transfer the private key.
45-
RSAPKCS1SignatureFormatter rsaFormatter = new RSAPKCS1SignatureFormatter(rsa);
46-
47-
//Set the hash algorithm to SHA1.
48-
rsaFormatter.SetHashAlgorithm("SHA1");
49-
50-
//Create a signature for hashValue and assign it to
51-
//signedHashValue.
52-
byte[] signedHashValue = rsaFormatter.CreateSignature(hashValue);
53-
54-
}
55-
5617
[TestMethod]
5718
public void TestSignature()
5819
{
@@ -70,10 +31,8 @@ public void TestSignature()
7031
Assert.IsTrue(good);
7132

7233
}
73-
7434
}
7535

76-
7736
static class X509RsaSha1Signature
7837
{
7938
const string SHA1 = "SHA1";
@@ -112,8 +71,5 @@ public static bool Verify(byte[] payload, byte[] signature, string thumbprint)
11271
return signatureDeformatter.VerifySignature(digest, signature);
11372
}
11473
}
115-
116-
117-
11874
}
11975
}

0 commit comments

Comments
 (0)