@@ -3,29 +3,25 @@ Example Azure KeyVault HSM Protected Key XML Signing
33 */
44
55using System ;
6- using System . Net . Http ;
76using System . Security . Cryptography . X509Certificates ;
87using System . Threading . Tasks ;
98using System . Xml ;
109using System . Security . Cryptography ;
1110using System . Security . Cryptography . Xml ;
12- using Microsoft . Azure . KeyVault ;
13- using Microsoft . Azure . KeyVault . Models ;
14- using Microsoft . Extensions . Configuration ;
15- using Microsoft . Extensions . Configuration . AzureKeyVault ;
16- using Microsoft . IdentityModel . Clients . ActiveDirectory ;
11+ using Azure . Security . KeyVault . Keys ;
12+ using Azure . Security . KeyVault . Certificates ;
13+ using Azure . Identity ;
1714using System . Text ;
1815
19-
2016namespace cli_exakvdocsign
2117{
22- class Program
18+ class Program
2319 {
2420 // AAD Application ID - Create a new application registration and grant it appropriate rights to the KeyVault.
2521 private static string AADClientID ;
2622 // AAD Application Secret/Key - Create a "password key" that lives for the desired validity period before expiring.
2723 private static string AADClientSecret ;
28- // KeyVault Address - Full address of deployed KeyVault.
24+ // KeyVault Address - Full address of deployed KeyVault.
2925 internal static string KeyVaultAddress ;
3026 //certificate name - Provided in creation of cert, also visible in key identifier. (*.vault.azure.net/keys/<certnamehere/*)
3127 internal static string CertName ;
@@ -34,41 +30,46 @@ class Program
3430 //file name and path of where to save the signed xml document.
3531 private static string XMLFileOutputName ;
3632
37- //instantiated KeyVault client used through the example.
38- internal static KeyVaultClient keyVault ;
39- //retrieved keybundle (private key). Note, the actual private key is never returned by Azure KeyVault HSM.
40- internal static KeyBundle secretKeyBundle ;
41- //retrieved public certificate bundle. This will also hold a reference the the appropriate private key information.
42- internal static CertificateBundle publicCertBundle ;
43-
33+ //instantiated KeyVault clients used through the example.
34+ internal static KeyClient keyClient ;
35+ internal static CertificateClient certificateClient ;
36+ //retrieved key
37+ internal static KeyVaultKey secretKey ;
38+ //retrieved public certificate
39+ internal static KeyVaultCertificateWithPolicy publicCert ;
40+
4441 //holds file name of signed xml to check. When populated, no other actions are ran.
4542 private static string ValidateFile = null ;
4643
47-
48-
49- static void Main ( string [ ] args )
44+ // Add these static properties to the Program class:
45+ internal static string TenantID ;
46+
47+ static async Task Main ( string [ ] args )
5048 {
5149 try {
5250 //run through the arguments and break them into variables.
5351 ProcessArgs ( args ) ;
5452
55- //if user is requesting validaiton of a file, only run verifydoc and exit.
53+ //if user is requesting validation of a file, only run verifydoc and exit.
5654 if ( ValidateFile != null )
5755 {
5856 VerifyDoc ( ) ;
5957 }
6058 else {
61-
62- //create KeyVault Client and authenticate with AAD. (GetAccessToken method.)
63- keyVault = new KeyVaultClient (
64- new KeyVaultClient . AuthenticationCallback ( GetAccessToken ) ,
65- new HttpClient ( ) ) ;
66-
67- //get cert bundle from supplied certificate name.
68- publicCertBundle = keyVault . GetCertificateAsync ( KeyVaultAddress , CertName ) . Result ;
69- //get secret (private key) bundle from information in public cert bundle pointing to current key and version associated to cert.
70- secretKeyBundle = keyVault . GetKeyAsync ( publicCertBundle . KeyIdentifier . Vault , publicCertBundle . KeyIdentifier . Name ) . Result ;
71-
59+
60+ //create KeyVault Clients with Azure Identity
61+ var credential = new ClientSecretCredential (
62+ tenantId : Environment . GetEnvironmentVariable ( "AZURE_TENANT_ID" ) , // You'll need to add this
63+ clientId : AADClientID ,
64+ clientSecret : AADClientSecret ) ;
65+
66+ keyClient = new KeyClient ( new Uri ( KeyVaultAddress ) , credential ) ;
67+ certificateClient = new CertificateClient ( new Uri ( KeyVaultAddress ) , credential ) ;
68+
69+ //get cert from supplied certificate name.
70+ publicCert = await certificateClient . GetCertificateAsync ( CertName ) ;
71+ //get key from certificate
72+ secretKey = await keyClient . GetKeyAsync ( CertName ) ;
7273
7374 //sign the doc yo!
7475 SignDoc ( ) ;
@@ -79,16 +80,6 @@ static void Main(string[] args)
7980 }
8081 }
8182
82- public static async Task < string > GetAccessToken ( string authority , string resource , string scope )
83- {
84- ClientCredential clientCredential = new ClientCredential ( AADClientID , AADClientSecret ) ;
85-
86- var context = new AuthenticationContext ( authority , TokenCache . DefaultShared ) ;
87- var result = await context . AcquireTokenAsync ( resource , clientCredential ) ;
88-
89- return result . AccessToken ;
90- }
91-
9283 private static void ProcessArgs ( string [ ] args ) {
9384 if ( args . Length > 0 ) {
9485 for ( int i = 0 ; i < args . Length ; i ++ ) {
@@ -114,6 +105,10 @@ private static void ProcessArgs(string[] args){
114105 else if ( args [ i ] . StartsWith ( "-xmlfiletosave" ) ) {
115106 XMLFileOutputName = args [ i + 1 ] ;
116107 }
108+ else if ( args [ i ] . StartsWith ( "-tenantid" ) )
109+ {
110+ TenantID = args [ i ] . Substring ( 10 ) ;
111+ }
117112 }
118113 }
119114 else {
@@ -147,8 +142,8 @@ public static void SignDoc(){
147142 // Load an XML file into the XmlDocument object.
148143 xmlDoc . PreserveWhitespace = true ;
149144 xmlDoc . Load ( XMLFileName ) ;
150-
151- // Sign the XML document.
145+
146+ // Sign the XML document.
152147 SignXml ( xmlDoc ) ;
153148
154149 }
@@ -164,19 +159,15 @@ public static void VerifyDoc(){
164159 // Load an XML file into the XmlDocument object.
165160 xmlDoc . PreserveWhitespace = true ;
166161 xmlDoc . Load ( ValidateFile ) ;
167-
168- // Sign the XML document.
162+
163+ // Sign the XML document.
169164 Console . WriteLine ( "The file validation results in: " + Verify ( xmlDoc ) ) ;
170165 }
171166
172- //sourced https://docs.microsoft.com/en-us/dotnet/standard/security/how-to-sign-xml-documents-with-digital-signatures
173- // Sign an XML file.
174- // This document cannot be verified unless the verifying
175- // code has the key with which it was signed.
176167 public static void SignXml ( XmlDocument xmlDoc )
177168 {
178- RSA key = secretKeyBundle . Key . ToRSA ( ) ;
179-
169+ RSA key = secretKey . Key . ToRSA ( ) ;
170+
180171 // Check arguments.
181172 if ( xmlDoc == null )
182173 throw new ArgumentException ( "xmlDoc" ) ;
@@ -192,14 +183,12 @@ public static void SignXml(XmlDocument xmlDoc)
192183 // add key info
193184 KeyInfo importKeyInfo = new KeyInfo ( ) ;
194185 KeyInfoX509Data importKeyInfoData = new KeyInfoX509Data ( ) ;
195- X509Certificate tempCert = new X509Certificate ( publicCertBundle . Cer ) ;
186+ X509Certificate tempCert = new X509Certificate ( publicCert . Cer ) ;
196187 importKeyInfoData . AddCertificate ( tempCert ) ;
197188 importKeyInfoData . AddIssuerSerial ( tempCert . Issuer , tempCert . GetSerialNumberString ( ) ) ;
198189
199190 importKeyInfo . AddClause ( importKeyInfoData ) ;
200191 signedXml . KeyInfo = importKeyInfo ;
201-
202-
203192
204193 // Create a reference to be signed.
205194 Reference reference = new Reference ( ) ;
@@ -225,8 +214,7 @@ public static void SignXml(XmlDocument xmlDoc)
225214 xmlDoc . Save ( XMLFileOutputName ) ;
226215
227216 var test = Verify ( xmlDoc ) ;
228- Console . WriteLine ( "XML Signed and validated as: " + test . ToString ( ) ) ;
229-
217+ Console . WriteLine ( "XML Signed and validated as: " + test . ToString ( ) ) ;
230218 }
231219
232220 public static bool Verify ( XmlDocument document )
@@ -251,9 +239,6 @@ public static bool Verify(XmlDocument document)
251239 }
252240 }
253241 return signed . CheckSignature ( new X509Certificate2 ( cer ) , true ) ;
254-
255242 }
256-
257-
258243 }
259244}
0 commit comments