Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
name: Build and Release

on:
push:
branches: [ master, main ]
pull_request:
branches: [ master, main ]

jobs:
build:
runs-on: windows-latest

steps:
- uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore dependencies
run: dotnet restore

- name: Build and publish for multiple platforms
run: |
dotnet publish -c Release --self-contained -r osx-x64
dotnet publish -c Release --self-contained -r centos-x64
dotnet publish -c Release --self-contained -r rhel-x64
dotnet publish -c Release --self-contained -r linux-x64
dotnet publish -c Release --self-contained -r win-x64

- name: Create artifacts
uses: actions/upload-artifact@v4
with:
name: centos-x64-binaries
path: cli-exakvdocsign/bin/Release/net8.0/centos-x64/publish/

- name: Upload Linux x64 artifacts
uses: actions/upload-artifact@v4
with:
name: linux-x64-binaries
path: cli-exakvdocsign/bin/Release/net8.0/linux-x64/publish/

- name: Upload RHEL x64 artifacts
uses: actions/upload-artifact@v4
with:
name: rhel-x64-binaries
path: cli-exakvdocsign/bin/Release/net8.0/rhel-x64/publish/

- name: Upload macOS x64 artifacts
uses: actions/upload-artifact@v4
with:
name: osx-x64-binaries
path: cli-exakvdocsign/bin/Release/net8.0/osx-x64/publish/

- name: Upload Windows x64 artifacts
uses: actions/upload-artifact@v4
with:
name: win-x64-binaries
path: cli-exakvdocsign/bin/Release/net8.0/win-x64/publish/

release:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/main'

steps:
- uses: actions/checkout@v4

- name: Download all artifacts
uses: actions/download-artifact@v4

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: v${{ github.run_number }}
release_name: Release v${{ github.run_number }}
body: ${{ github.event.head_commit.message }}
draft: true
prerelease: false

- name: Upload centos-x64 Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./centos-x64-binaries
asset_name: centos-x64-binaries.zip
asset_content_type: application/zip

- name: Upload linux-x64 Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./linux-x64-binaries
asset_name: linux-x64-binaries.zip
asset_content_type: application/zip

- name: Upload rhel-x64 Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./rhel-x64-binaries
asset_name: rhel-x64-binaries.zip
asset_content_type: application/zip

- name: Upload osx-x64 Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./osx-x64-binaries
asset_name: osx-x64-binaries.zip
asset_content_type: application/zip

- name: Upload win-x64 Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./win-x64-binaries
asset_name: win-x64-binaries.zip
asset_content_type: application/zip
8 changes: 4 additions & 4 deletions cli-exakvdocsign/CustomR256Signature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,16 @@ public class RSAPKCS1SHA256SignatureDescription : SignatureDescription
/// Construct an RSAPKCS1SHA256SignatureDescription object. The default settings for this object
/// are:
/// <list type="bullet">
/// <item>Digest algorithm - <see cref="SHA256Managed" /></item>
/// <item>Key algorithm - <see cref="RSACryptoServiceProvider" /></item>
/// <item>Digest algorithm - <see cref="SHA256" /></item>
/// <item>Key algorithm - <see cref="RSA" /></item>
/// <item>Formatter algorithm - <see cref="RSAPKCS1SignatureFormatter" /></item>
/// <item>Deformatter algorithm - <see cref="RSAPKCS1SignatureDeformatter" /></item>
/// </list>
/// </summary>
public RSAPKCS1SHA256SignatureDescription()
{
KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName;
DigestAlgorithm = typeof(SHA256Managed).FullName; // Note - SHA256CryptoServiceProvider is not registered with CryptoConfig
KeyAlgorithm = typeof(RSA).FullName; // Updated from RSACryptoServiceProvider
DigestAlgorithm = typeof(SHA256).FullName; // Updated from SHA256Managed
FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName;
DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName;
}
Expand Down
15 changes: 11 additions & 4 deletions cli-exakvdocsign/CustomSigner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@
using System.Security.Cryptography.Xml;
using System.Text;
using System.Reflection;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;

using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Keys.Cryptography;

namespace cli_exakvdocsign
{
public class CustomSigner: CustomSignedXml.ISignerProvider
{
public byte[] Sign(byte[] data)
{
return cli_exakvdocsign.Program.keyVault.SignAsync(cli_exakvdocsign.Program.KeyVaultAddress, cli_exakvdocsign.Program.publicCertBundle.KeyIdentifier.Name, cli_exakvdocsign.Program.publicCertBundle.KeyIdentifier.Version, Microsoft.Azure.KeyVault.WebKey.JsonWebKeySignatureAlgorithm.RS256, data).Result.Result;
// Create cryptography client for the key
var cryptographyClient = new CryptographyClient(cli_exakvdocsign.Program.secretKey.Id, new Azure.Identity.ClientSecretCredential(
tenantId: Environment.GetEnvironmentVariable("AZURE_TENANT_ID"),
clientId: Environment.GetEnvironmentVariable("AZURE_CLIENT_ID"), // You'll need to expose this from Program
clientSecret: Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET"))); // You'll need to expose this from Program

// Sign the data using RS256 algorithm
var signResult = cryptographyClient.SignAsync(SignatureAlgorithm.RS256, data).Result;
return signResult.Signature;
}
}
}
103 changes: 44 additions & 59 deletions cli-exakvdocsign/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,25 @@ Example Azure KeyVault HSM Protected Key XML Signing
*/

using System;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Xml;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.KeyVault.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureKeyVault;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Azure.Security.KeyVault.Keys;
using Azure.Security.KeyVault.Certificates;
using Azure.Identity;
using System.Text;


namespace cli_exakvdocsign
{
class Program
class Program
{
// AAD Application ID - Create a new application registration and grant it appropriate rights to the KeyVault.
private static string AADClientID;
// AAD Application Secret/Key - Create a "password key" that lives for the desired validity period before expiring.
private static string AADClientSecret;
// KeyVault Address - Full address of deployed KeyVault.
// KeyVault Address - Full address of deployed KeyVault.
internal static string KeyVaultAddress;
//certificate name - Provided in creation of cert, also visible in key identifier. (*.vault.azure.net/keys/<certnamehere/*)
internal static string CertName;
Expand All @@ -34,41 +30,46 @@ class Program
//file name and path of where to save the signed xml document.
private static string XMLFileOutputName;

//instantiated KeyVault client used through the example.
internal static KeyVaultClient keyVault;
//retrieved keybundle (private key). Note, the actual private key is never returned by Azure KeyVault HSM.
internal static KeyBundle secretKeyBundle;
//retrieved public certificate bundle. This will also hold a reference the the appropriate private key information.
internal static CertificateBundle publicCertBundle;

//instantiated KeyVault clients used through the example.
internal static KeyClient keyClient;
internal static CertificateClient certificateClient;
//retrieved key
internal static KeyVaultKey secretKey;
//retrieved public certificate
internal static KeyVaultCertificateWithPolicy publicCert;

//holds file name of signed xml to check. When populated, no other actions are ran.
private static string ValidateFile = null;



static void Main(string[] args)
// Add these static properties to the Program class:
internal static string TenantID;

static async Task Main(string[] args)
{
try{
//run through the arguments and break them into variables.
ProcessArgs(args);

//if user is requesting validaiton of a file, only run verifydoc and exit.
//if user is requesting validation of a file, only run verifydoc and exit.
if(ValidateFile != null)
{
VerifyDoc();
}
else{

//create KeyVault Client and authenticate with AAD. (GetAccessToken method.)
keyVault = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(GetAccessToken),
new HttpClient());

//get cert bundle from supplied certificate name.
publicCertBundle = keyVault.GetCertificateAsync(KeyVaultAddress, CertName).Result;
//get secret (private key) bundle from information in public cert bundle pointing to current key and version associated to cert.
secretKeyBundle = keyVault.GetKeyAsync(publicCertBundle.KeyIdentifier.Vault, publicCertBundle.KeyIdentifier.Name).Result;


//create KeyVault Clients with Azure Identity
var credential = new ClientSecretCredential(
tenantId: Environment.GetEnvironmentVariable("AZURE_TENANT_ID"), // You'll need to add this
clientId: AADClientID,
clientSecret: AADClientSecret);

keyClient = new KeyClient(new Uri(KeyVaultAddress), credential);
certificateClient = new CertificateClient(new Uri(KeyVaultAddress), credential);

//get cert from supplied certificate name.
publicCert = await certificateClient.GetCertificateAsync(CertName);
//get key from certificate
secretKey = await keyClient.GetKeyAsync(CertName);

//sign the doc yo!
SignDoc();
Expand All @@ -79,16 +80,6 @@ static void Main(string[] args)
}
}

public static async Task<string> GetAccessToken(string authority, string resource, string scope)
{
ClientCredential clientCredential = new ClientCredential(AADClientID, AADClientSecret);

var context = new AuthenticationContext(authority, TokenCache.DefaultShared);
var result = await context.AcquireTokenAsync(resource, clientCredential);

return result.AccessToken;
}

private static void ProcessArgs(string[] args){
if(args.Length > 0){
for(int i =0; i < args.Length; i++){
Expand All @@ -114,6 +105,10 @@ private static void ProcessArgs(string[] args){
else if(args[i].StartsWith("-xmlfiletosave")){
XMLFileOutputName = args[i+1];
}
else if(args[i].StartsWith("-tenantid"))
{
TenantID = args[i].Substring(10);
}
}
}
else{
Expand Down Expand Up @@ -147,8 +142,8 @@ public static void SignDoc(){
// Load an XML file into the XmlDocument object.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(XMLFileName);
// Sign the XML document.

// Sign the XML document.
SignXml(xmlDoc);

}
Expand All @@ -164,19 +159,15 @@ public static void VerifyDoc(){
// Load an XML file into the XmlDocument object.
xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(ValidateFile);
// Sign the XML document.

// Sign the XML document.
Console.WriteLine("The file validation results in: " + Verify(xmlDoc));
}

//sourced https://docs.microsoft.com/en-us/dotnet/standard/security/how-to-sign-xml-documents-with-digital-signatures
// Sign an XML file.
// This document cannot be verified unless the verifying
// code has the key with which it was signed.
public static void SignXml(XmlDocument xmlDoc)
{
RSA key = secretKeyBundle.Key.ToRSA();
RSA key = secretKey.Key.ToRSA();

// Check arguments.
if (xmlDoc == null)
throw new ArgumentException("xmlDoc");
Expand All @@ -192,14 +183,12 @@ public static void SignXml(XmlDocument xmlDoc)
// add key info
KeyInfo importKeyInfo = new KeyInfo();
KeyInfoX509Data importKeyInfoData = new KeyInfoX509Data();
X509Certificate tempCert = new X509Certificate(publicCertBundle.Cer);
X509Certificate tempCert = new X509Certificate(publicCert.Cer);
importKeyInfoData.AddCertificate(tempCert);
importKeyInfoData.AddIssuerSerial(tempCert.Issuer, tempCert.GetSerialNumberString());

importKeyInfo.AddClause(importKeyInfoData);
signedXml.KeyInfo = importKeyInfo;



// Create a reference to be signed.
Reference reference = new Reference();
Expand All @@ -225,8 +214,7 @@ public static void SignXml(XmlDocument xmlDoc)
xmlDoc.Save(XMLFileOutputName);

var test = Verify(xmlDoc);
Console.WriteLine("XML Signed and validated as: " + test.ToString());

Console.WriteLine("XML Signed and validated as: " + test.ToString());
}

public static bool Verify(XmlDocument document)
Expand All @@ -251,9 +239,6 @@ public static bool Verify(XmlDocument document)
}
}
return signed.CheckSignature(new X509Certificate2(cer), true);

}


}
}
Loading
Loading