Skip to content

Commit 1a50deb

Browse files
committed
Added some ReadMe files to explain how to create ECC 256 keys and Cert files
Added ReadMe to explain how to upload the Nuget package. Added and moved all testcases to use PEM files to load the certificate, no no longer rely on having the cert installed in the windows certificate manager for development, use PEM file imbedded as resources in the Test project. Added a Covid result of Not Detected to test project Added support methods to load X509Certificate2 from PEM files Added more validation to the JWKS de-serializer
1 parent 0e74b44 commit 1a50deb

18 files changed

Lines changed: 299 additions & 45 deletions

ReadMeOpenSSLCreateECCkeys.txt

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
Taken from : https://www.scottbrady91.com/OpenSSL/Creating-Elliptical-Curve-Keys-using-OpenSSL
2+
3+
Recently, I have been using OpenSSL to generate private keys and X509 certificates
4+
for Elliptical Curve Cryptography (ECC) and then using them in ASP.NET Core for token signing.
5+
6+
In this article, I�m going to show you how to use OpenSSL to generate private and public
7+
keys on the curve of your choice. Check out my other article for how to do the same for RSA
8+
keys.
9+
10+
OpenSSL ECDSA Cheat Sheet
11+
-------------------------------------------------------------------
12+
# find your curve
13+
openssl ecparam -list_curves
14+
15+
# generate a private key for a curve
16+
openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
17+
18+
# generate corresponding public key
19+
openssl ec -in private-key.pem -pubout -out public-key.pem
20+
21+
# optional: create a self-signed certificate
22+
openssl req -new -x509 -key private-key.pem -out cert.pem -days 360
23+
24+
# optional: convert pem to pfx
25+
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx
26+
Generating an Elliptical Curve Private Key Using OpenSSL
27+
To start, you will need to choose the curve you will be working with. You can use the
28+
following command to see a list of
29+
supported curve names and descriptions.
30+
31+
openssl ecparam -list_curves
32+
In this example, I am using prime256v1 (secp256r1), which is suitable for JWT signing;
33+
this is the curve used for JOSE�s ES256.
34+
-------------------------------------------------------------------
35+
36+
You can now generate a private key:
37+
38+
openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
39+
This should give you a PEM file containing your EC private key, which looks something
40+
like the following:
41+
42+
-----BEGIN EC PRIVATE KEY-----
43+
MHcCAQEEIKEubpBiHkZQYlORbCy8gGTz8tzrWsjBJA6GfFCrQ98coAoGCCqGSM49
44+
AwEHoUQDQgAEOr6rMmRRNKuZuwws/hWwFTM6ECEEaJGGARCJUO4UfoURl8b4JThG
45+
t8VDFKeR2i+ZxE+xh/wTBaJ/zvtSqZiNnQ==
46+
-----END EC PRIVATE KEY-----
47+
48+
Creating an EC Public Key from a Private Key Using OpenSSL
49+
Now that you have your private key, you can use it to generate another PEM, containing
50+
only your public key.
51+
52+
openssl ec -in private-key.pem -pubout -out public-key.pem
53+
This should give you another PEM file, containing the public key:
54+
55+
-----BEGIN PUBLIC KEY-----
56+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEOr6rMmRRNKuZuwws/hWwFTM6ECEE
57+
aJGGARCJUO4UfoURl8b4JThGt8VDFKeR2i+ZxE+xh/wTBaJ/zvtSqZiNnQ==
58+
-----END PUBLIC KEY-----
59+
60+
Creating an EC Self-Signed Certificate Using OpenSSL
61+
Now that you have a private key, you could use it to generate a self-signed certificate.
62+
This is not required, but it allows you to use the key
63+
for server/client authentication, or gain X509 specific functionality in technologies
64+
such as JWT and SAML.
65+
66+
openssl req -new -x509 -key private-key.pem -out cert.pem -days 360
67+
This will again generate another PEM file, this time containing the certificate created
68+
by your private key:
69+
70+
-----BEGIN CERTIFICATE-----
71+
MIIB4DCCAYWgAwIBAgIUH53ssiPt4JEGx+VJyntCpHL+TdAwCgYIKoZIzj0EAwIw
72+
RTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGElu
73+
dGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0yMDA3MTgxMTE4NDNaFw0yMTA3MTMx
74+
MTE4NDNaMEUxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYD
75+
VQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwWTATBgcqhkjOPQIBBggqhkjO
76+
PQMBBwNCAAQ6vqsyZFE0q5m7DCz+FbAVMzoQIQRokYYBEIlQ7hR+hRGXxvglOEa3
77+
xUMUp5HaL5nET7GH/BMFon/O+1KpmI2do1MwUTAdBgNVHQ4EFgQU9yjFBqAZOMv+
78+
cD6a3KHTWuYrcFEwHwYDVR0jBBgwFoAU9yjFBqAZOMv+cD6a3KHTWuYrcFEwDwYD
79+
VR0TAQH/BAUwAwEB/zAKBggqhkjOPQQDAgNJADBGAiEAwCpA5Nx083qqUqU6LUd0
80+
vzZLK4etuInxNvXohXH5LiACIQDSI63J4DiN3dq2sPPLw5iQi9MMefcV1iAySbKT
81+
B9BaAw==
82+
-----END CERTIFICATE-----
83+
84+
You could leave things there, but if you are working on Windows, you may prefer a PFX
85+
file that contains both the certificate and the private key for you to export and use.
86+
87+
You can do this using OpenSSL�s pkcs12 command:
88+
89+
openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx
90+
OpenSSL will ask you to create a password for the PFX file. Feel free to leave this blank.
91+
92+
This should leave you with a certificate that Windows can both install and export the
93+
EC private key from.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
How to pubish to Nuget:
1+
How to publish to Nuget:
22

33
1. Rev the version in the project package dialog and save
44
2. Right click the project and select Pack

SmartHealthCard.Test/Model/FhirDataSupport.cs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ namespace SmartHealthCard.Test.Model
1010
{
1111
static class FhirDataSupport
1212
{
13-
public static Bundle GetCovid19FhirBundleExample1()
13+
public static Bundle GetCovid19DetectedFhirBundleExample()
1414
{
15-
Patient PatientResource = GetPatientResource("TestFamilyName", "TestGivenName", new DateTime(1973, 09, 30), "61481059995");
16-
15+
Patient PatientResource = GetPatientResource("Coyote", "Wile E", new DateTime(1973, 09, 30), "61481059995");
16+
1717
Coding Code = new Coding(system: "http://loinc.org", code: "94558-4"); //SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay
1818
Coding Value = new Coding(system: "http://snomed.info/sct", code: "260373001"); //Detected
1919
Observation CovidResultObservationResource = GetObservationResource(
@@ -36,6 +36,32 @@ public static Bundle GetCovid19FhirBundleExample1()
3636
return Bundle;
3737
}
3838

39+
public static Bundle GetCovid19NotDetectedFhirBundleExample()
40+
{
41+
Patient PatientResource = GetPatientResource("Coyote", "Wile E", new DateTime(1973, 09, 30), "61481059995");
42+
43+
Coding Code = new Coding(system: "http://loinc.org", code: "94558-4"); //SARS-CoV-2 (COVID-19) Ag [Presence] in Respiratory specimen by Rapid immunoassay
44+
Coding Value = new Coding(system: "http://snomed.info/sct", code: "260415000"); //Not Detected
45+
Observation CovidResultObservationResource = GetObservationResource(
46+
ObsCode: Code,
47+
ObsValue: Value,
48+
EffectiveDate: new DateTime(2021, 05, 24),
49+
PerformerOrganisationName: "ACME Healthcare",
50+
IdentityAssuranceLevelCode: "IAL1.4");
51+
52+
List<Resource> BundleResourceList = new List<Resource>()
53+
{
54+
PatientResource,
55+
CovidResultObservationResource
56+
};
57+
58+
Bundle Bundle = new Bundle();
59+
Bundle.Type = Bundle.BundleType.Collection;
60+
Bundle.Entry = GetBundleResourceEntryList(BundleResourceList);
61+
62+
return Bundle;
63+
}
64+
3965
private static Patient GetPatientResource(string FamilyName, string GivenName, DateTime DateOfBirth, string PhoneNumber)
4066
{
4167
Patient Patient = new Patient();
@@ -77,7 +103,7 @@ private static Observation GetObservationResource(Coding ObsCode, Coding ObsValu
77103
{
78104
Coding = new List<Coding>()
79105
{
80-
new Coding(system: "http://loinc.org", code: "94558-4")
106+
ObsCode
81107
}
82108
};
83109

@@ -86,7 +112,7 @@ private static Observation GetObservationResource(Coding ObsCode, Coding ObsValu
86112
{
87113
Coding = new List<Coding>()
88114
{
89-
new Coding(system: "http://snomed.info/sct", code: "260373001")
115+
ObsValue
90116
}
91117
};
92118
Observation.Performer = new List<ResourceReference>()

SmartHealthCard.Test/ResourceData.Designer.cs

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SmartHealthCard.Test/ResourceData.resx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,16 @@
118118
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
119119
</resheader>
120120
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
121+
<data name="InvalidJwks" type="System.Resources.ResXFileRef, System.Windows.Forms">
122+
<value>Resources\InvalidJwks.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
123+
</data>
121124
<data name="SmartHealthCardCovidExample" type="System.Resources.ResXFileRef, System.Windows.Forms">
122125
<value>Resources\SmartHealthCardCovidExample.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
123126
</data>
127+
<data name="TestECC256Cert" type="System.Resources.ResXFileRef, System.Windows.Forms">
128+
<value>Resources\TestECC256Cert.pem;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
129+
</data>
130+
<data name="TestECC256Private_key" type="System.Resources.ResXFileRef, System.Windows.Forms">
131+
<value>Resources\TestECC256Private-key.pem;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
132+
</data>
124133
</root>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"keys": [
3+
{
4+
"kty": "EC",
5+
"kid": "__q7Q4cf6G69ExQSrfW5A_2c3mJrtrRPBhtoRTyZlQM",
6+
"use": "sig",
7+
"alg": "ES256",
8+
"crv": "P-256",
9+
"y": "OPR0AnWPAmhFC6y1RAXFvsAGS0ptfUwuoTKkWXpP4bE"
10+
}
11+
]
12+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIICnTCCAkOgAwIBAgIUVNNmHJZG+2m2bE2NjuYXs5UholswCgYIKoZIzj0EAwIw
3+
gaMxCzAJBgNVBAYTAkFVMQwwCgYDVQQIDANRTEQxETAPBgNVBAcMCEJyaXNiYW5l
4+
MRcwFQYDVQQKDA5Tb25pY0hlYWxoY2FyZTEQMA4GA1UECwwHU29uaWNJVDEUMBIG
5+
A1UEAwwLQW5ndXNNSWxsYXIxMjAwBgkqhkiG9w0BCQEWI2FuZ3VzLm1pbGxhckBz
6+
b25pY2hlYWx0aGNhcmUuY29tLmF1MB4XDTIxMDUxMDA0NTg0MFoXDTIyMDUwNTA0
7+
NTg0MFowgaMxCzAJBgNVBAYTAkFVMQwwCgYDVQQIDANRTEQxETAPBgNVBAcMCEJy
8+
aXNiYW5lMRcwFQYDVQQKDA5Tb25pY0hlYWxoY2FyZTEQMA4GA1UECwwHU29uaWNJ
9+
VDEUMBIGA1UEAwwLQW5ndXNNSWxsYXIxMjAwBgkqhkiG9w0BCQEWI2FuZ3VzLm1p
10+
bGxhckBzb25pY2hlYWx0aGNhcmUuY29tLmF1MFkwEwYHKoZIzj0CAQYIKoZIzj0D
11+
AQcDQgAEj2oyU1JyNT1x66i+PFsdsU1qL+y/Nxq7RjwKkd5kNyc49HQCdY8CaEUL
12+
rLVEBcW+wAZLSm19TC6hMqRZek/hsaNTMFEwHQYDVR0OBBYEFFGC+JzgmozVIEv8
13+
59nMkGR/LIFYMB8GA1UdIwQYMBaAFFGC+JzgmozVIEv859nMkGR/LIFYMA8GA1Ud
14+
EwEB/wQFMAMBAf8wCgYIKoZIzj0EAwIDSAAwRQIhAKwGX0gIrQ1MmKKhf4cKUAI7
15+
PpSgKU3S+wiaNWuQ5aeRAiAF2lYQ+O2TEMVjT5+rm8gQEcmO2FEAvLs3N4hs1Guf
16+
JQ==
17+
-----END CERTIFICATE-----
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-----BEGIN EC PRIVATE KEY-----
2+
MHcCAQEEIHrua8isWUVjVBzQBt+s2QUzyGnh2uO20Siq+ha0pTZaoAoGCCqGSM49
3+
AwEHoUQDQgAEj2oyU1JyNT1x66i+PFsdsU1qL+y/Nxq7RjwKkd5kNyc49HQCdY8C
4+
aEULrLVEBcW+wAZLSm19TC6hMqRZek/hsQ==
5+
-----END EC PRIVATE KEY-----

SmartHealthCard.Test/SmartHealthCardDecoderTest.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ public class SmartHealthCardDecoderTest
1818
public async void Decode_Token_Verify_with_JWKS()
1919
{
2020
//### Prepare ######################################################
21-
//Get the ECC certificate from the Windows Certificate Store by Thumb-print
22-
X509Certificate2 Certificate = CertificateSupport.GetCertificate(Thumbprint: CertificateSupport.TestingThumbprint);
21+
22+
//Get the ECC certificate from the Cert and Private key PEM files
23+
X509Certificate2 Certificate = CertificateSupport.GetCertificateFromPemFiles();
24+
2325
List<X509Certificate2> CertificateList = new List<X509Certificate2>() { Certificate };
2426

2527
//The base of the URL where a validator will retrieve the public keys from (e.g : [Issuer]/.well-known/jwks.json)
@@ -46,8 +48,8 @@ public async void Decode_Token_Verify_with_JWKS()
4648
public async void Decode_Token_Verify_with_Certificate()
4749
{
4850
//### Prepare ######################################################
49-
//Get the ECC certificate from the Windows Certificate Store by Thumb-print
50-
X509Certificate2 Certificate = CertificateSupport.GetCertificate(Thumbprint: "72c78a3460fb27b9ef2ccfae2538675b75363fee");
51+
//Get the ECC certificate from the Cert and Private key PEM files
52+
X509Certificate2 Certificate = CertificateSupport.GetCertificateFromPemFiles();
5153

5254
//The base of the URL where a validator will retrieve the public keys from (e.g : [Issuer]/.well-known/jwks.json)
5355
Uri Issuer = new Uri("https://sonichealthcare.com/something");

SmartHealthCard.Test/SmartHealthCardEncoderTest.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
using SmartHealthCard.Test.Serializers;
55
using SmartHealthCard.Test.Support;
66
using SmartHealthCard.Token;
7+
using SmartHealthCard.Token.Encoders;
78
using SmartHealthCard.Token.Exceptions;
89
using SmartHealthCard.Token.Model.Jwks;
910
using SmartHealthCard.Token.Model.Shc;
1011
using SmartHealthCard.Token.Providers;
1112
using System;
1213
using System.Collections.Generic;
14+
using System.IO;
1315
using System.Security.Cryptography.X509Certificates;
1416
using Xunit;
1517

@@ -22,14 +24,14 @@ public async void Create_Token_Decode_Token()
2224
{
2325
//### Prepare ######################################################
2426

25-
//Get the ECC certificate from the Windows Certificate Store by Thumb-print
26-
X509Certificate2 Certificate = CertificateSupport.GetCertificate(Thumbprint: CertificateSupport.TestingThumbprint);
27-
27+
//Get the ECC certificate from the Cert and Private key PEM files
28+
X509Certificate2 Certificate = CertificateSupport.GetCertificateFromPemFiles();
29+
2830
//The Version of FHIR in use
2931
string FhirVersion = "4.0.1";
3032

3133
//Get FHIR bundle
32-
Bundle FhirBundleResource = FhirDataSupport.GetCovid19FhirBundleExample1();
34+
Bundle FhirBundleResource = FhirDataSupport.GetCovid19DetectedFhirBundleExample();
3335
string FhirBundleJson = FhirSerializer.SerializeToJson(FhirBundleResource);
3436

3537
//The base of the URL where a validator will retrieve the public keys from (e.g : [Issuer]/.well-known/jwks.json)

0 commit comments

Comments
 (0)