diff --git a/tools/xml_dsig_tool/README.md b/tools/xml_dsig_tool/README.md
new file mode 100644
index 00000000..21a1de6d
--- /dev/null
+++ b/tools/xml_dsig_tool/README.md
@@ -0,0 +1,30 @@
+The xml_dsig_tool is a Windows command line application that provides the ability to perform basic cryptographic functions per the W3C XML Signature Syntax and Processing Version 1.1. The functions include:
+
+sign : append an enveloped signature to an unsigned XML document
+validate : validate a signed base rim's signature (NOTE: cryptographic validation only, this tool does not validate the RIM structure)
+
+
+# Build and package
+ - Install Visual Studio
+ - The recommended project name is "xml_dsig_tool" so that the resulting executable file will be appropriately named xml_dsig_tool.exe.
+ - Install NuGet packages:
+ - System.CommandLine.2.0.0-beta4 (check "Include Prerelease" next to search bar)
+ - System.Security.Cryptography.X509Certificates
+ - System.Security.Cryptography.Xml
+ - Publish executable
+ - https://docs.microsoft.com/en-us/dotnet/core/tutorials/publishing-with-visual-studio?pivots=dotnet-6-0
+ - Install support files to .exe directory
+ - privateRimKey.pem
+ - RimSignCert.pem
+ - unsigned.xml
+
+
+# Running xml_dsig_tool
+Navigate to the .exe directory and run the following commands
+
+help
+
+sign --file unsigned.xml --private-key privateKey.pem
+
+validate --file signed_unsigned.xml --certificate RimSignCert.pem
+
diff --git a/tools/xml_dsig_tool/RimSignCert.pem b/tools/xml_dsig_tool/RimSignCert.pem
new file mode 100644
index 00000000..40aa4386
--- /dev/null
+++ b/tools/xml_dsig_tool/RimSignCert.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID2jCCAsKgAwIBAgIJAP0uwoNdwZDFMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UECgwHRXhhbXBsZTERMA8GA1UECwwI
+UENDbGllbnQxEjAQBgNVBAMMCUV4YW1wbGVDQTAeFw0yMDA3MjEyMTQ1MDBaFw0z
+MDA1MzAyMTQ1MDBaMFwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJWQTEQMA4GA1UE
+CgwHRXhhbXBsZTERMA8GA1UECwwIUENDbGllbnQxGzAZBgNVBAMMEmV4YW1wbGUu
+UklNLnNpZ25lcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKd1lWGk
+SRuxAAY2wHag2GVxUk1dZx2PTpfQOflvLeccAVwa8mQhlsRERq+QK8ilj8Xfqs44
+/nBaccZDOjdfIxIUCMfwhGXjxCaqZbgTucNsExDnu4arTGraoAwzHg0cVLiKT/Cx
+j9NL4dcMgxRXsPdHfXb0923C7xYd2t2qfW05umgaj7qeQl6c68CFNsGX4JA8rWFQ
+ZvvGx5DGlK4KTcjPuQQINs5fxasNKqLY2hq+z82x/rqwr2hmyizD6FpFSyIABPEM
+PfB036GEhRwu1WEMkq8yIp2jgRUoFYke9pB3ph9pVow0Hh4mNFSKD4pP41VSKY1n
+us83mdkuukPy5o0CAwEAAaOBpzCBpDAdBgNVHQ4EFgQUL96459AwoiCdqgGGGpZP
+7ezyvMEwHwYDVR0jBBgwFoAURqG47dumcV/Q0ud6ijxdbprDljgwCQYDVR0TBAIw
+ADALBgNVHQ8EBAMCBsAwEwYDVR0lBAwwCgYIKwYBBQUHAwMwNQYIKwYBBQUHAQEE
+KTAnMCUGCCsGAQUFBzAChhlodHRwczovL2V4YW1wbGUuY29tL2NlcnRzMA0GCSqG
+SIb3DQEBCwUAA4IBAQDpKx5oQlkS11cg7Qp58BmCvjCzFpof+qYePooJsD3i5SwK
+fRTa2CkDMww9qrwBK7G60y7jhe5InKTdqIlVqaji5ZImR0QMKTtk7zt9AJ9EaEzK
+xfDiE/qX34KxNe4ZmbvLH8N+BSujQXMMi56zGjW469Y/rbDMG8uU1dq3zqhO5b+d
+Ur1ecdkYLgzxu6O+oWy5JpVibmcjvNezJsUtjc+km2FYm24vU3/fCNzZ2z0EHQES
+cIEQ5OqfpdFrV3De238RhMH6J4xePSidnFpfBc6FrdyDI1A8eRFz36I4xfVL3ZnJ
+P/+j+NE4q6yz5VGvm0npLO394ZihtsI1sRAR8ORJ
+-----END CERTIFICATE-----
diff --git a/tools/xml_dsig_tool/privateRimKey.pem b/tools/xml_dsig_tool/privateRimKey.pem
new file mode 100644
index 00000000..afe282c4
--- /dev/null
+++ b/tools/xml_dsig_tool/privateRimKey.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCndZVhpEkbsQAG
+NsB2oNhlcVJNXWcdj06X0Dn5by3nHAFcGvJkIZbEREavkCvIpY/F36rOOP5wWnHG
+Qzo3XyMSFAjH8IRl48QmqmW4E7nDbBMQ57uGq0xq2qAMMx4NHFS4ik/wsY/TS+HX
+DIMUV7D3R3129Pdtwu8WHdrdqn1tObpoGo+6nkJenOvAhTbBl+CQPK1hUGb7xseQ
+xpSuCk3Iz7kECDbOX8WrDSqi2Noavs/Nsf66sK9oZsosw+haRUsiAATxDD3wdN+h
+hIUcLtVhDJKvMiKdo4EVKBWJHvaQd6YfaVaMNB4eJjRUig+KT+NVUimNZ7rPN5nZ
+LrpD8uaNAgMBAAECggEAcnG8npd9U0x7HMQMcsZoPaPdwHvF/gCzkLNA+8RM1bZh
+A4ZzA5WlCQs0V8Wq9pyXjn7Wp8txsG1PdlT5k2AUgsVoXuR0R4IKyvYHQG9StEjH
+GvWURmwJdLlnSg8hSYqEJ/52taNUDO6+MI8fgiaQDd8w0ryF4OCpLy9GJdnfkGYZ
+Ayemb3USFUdj/S67NVqxnvAfFMM5FqkKGhkoy7wBRgO6eOeJvoTq8LMiPiponwwF
+DW409ZStbrk1f1Oszst/UvFUWA9BdDfeoPmFR61y3eB5zlMQG8Mhr2v5hvkj9TPX
+FU4Fm4EzZ1h/60cdWoP6XYCP7F2NqZ8N8u4UBQNAIQKBgQDcGIw5GJEvRF+FFTTR
+hYatMRn80DGTVjdT32MgajdKx05OWxBmQsFob34fiSnr0wAXPJeDXG4ruMBE2bSk
+EC8rCO08G8ihQoH8x0cvuERe1fpVWk3RWNucVGIiJSEXAIwWrlYZLTfYd5GqBkPE
+OQxxo4MtOyqeHmVH1mOywk9ABQKBgQDCxt95luzqQZV9Xl78QQvOIbjOdHLjY23Z
+yp8sGt9birL/WZ33TCRgmH1e61BdrSqO7Om/ail2Y59XM5UU6kLbDj0IgmOPTsrJ
+JmIVf8r3bKltVUaLePgr4yex7dmtHRH8OkLXKnE0RCO0kCi9kJMB12yE3pWxk+Pu
+zztQd3a66QKBgBNJd2g9deONe01fOVyu9clRhzR3ThDaOkj4R2h8xlGgO4V0R3Ce
+ovIy6vt6epj2yYg/wAs720+rhfXCmijSXj/ILXnZ+W/gMyHimKNe42boG2LFYhJZ
+Vg1R+7OAS3EHlD8ckeDs7Hrkp3gdymx0j1mZ+ZHKIIbwpPFxoRT2IBm9AoGBAI0Z
+bIK0puP8psKvPrgWluq42xwUl7XKLaX8dtqIjQ3PqGP7E8g2TJP9Y7UDWrDB5Xas
+gZi821R8Ts3o/DKukcgGxIgJjP4f4h9dwug4L1yWRxaBFB2tgHqqj/MBjxMtX/4M
+Zqdgg6mNQyBm3lyVAynuWRrX9DE0JYa2cQ2VvVkhAoGBAMBv/oT813w00759PmkO
+Uxv3LXTJuYBbq0Rmga25jN3ow8LrGQdSVg7F/af3I5KUF7mLiegDy1pkRfauyXH7
++WhEqnf86vDrzPpytDMxinWOQZusCqeWHb+nuVTuL3Fv+GxEdwVGYI/7lFJ7B//h
+P5rU93ZoYY7sWcGVqaaEkMRU
+-----END PRIVATE KEY-----
diff --git a/tools/xml_dsig_tool/unsigned.xml b/tools/xml_dsig_tool/unsigned.xml
new file mode 100644
index 00000000..1fa11f85
--- /dev/null
+++ b/tools/xml_dsig_tool/unsigned.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/xml_dsig_tool/xmlDsig.cs b/tools/xml_dsig_tool/xmlDsig.cs
new file mode 100644
index 00000000..8add8088
--- /dev/null
+++ b/tools/xml_dsig_tool/xmlDsig.cs
@@ -0,0 +1,249 @@
+using System.Security.Cryptography;
+using System.Security.Cryptography.X509Certificates;
+using System.Security.Cryptography.Xml;
+using System.Xml;
+using System.CommandLine;
+using System.Text;
+
+/**
+ * This command line program has three commands:
+ * 1. sign - append a signature calculated from a user-provided private key
+ * 2. validate - validate a signature with a user-provided certificate
+ * 3. debug - print out important components of a signed XML document
+ *
+ * The validate functioin strictly checks the cryptographic integrity of the signature,
+ * it does not verify the integrity of the certificate chain.
+ */
+class XmlDsigTool
+{
+
+ static async Task Main(String[] args)
+ {
+ var fileOption = new Option(
+ name: "--file",
+ description: "The filename input for the command.",
+ parseArgument: result =>
+ {
+ string? filePath = result.Tokens.Single().Value;
+ if (!File.Exists(filePath))
+ {
+ result.ErrorMessage = "File " + filePath + " does not exist.";
+ return null;
+ }
+ else
+ {
+ return filePath;
+ }
+ });
+ var privateKeyOption = new Option(
+ name: "--private-key",
+ description: "The private key with which to sign."
+ );
+ var certificateOption = new Option(
+ name: "--certificate",
+ description: "The certificate with which to validate the signature."
+ );
+
+ var rootCommand = new RootCommand("A tool for signing, validating, and debugging base RIMs.");
+ var signCommand = new Command("sign", "Sign the given file with the given key.")
+ {
+ fileOption,
+ privateKeyOption
+ };
+ var validateCommand = new Command("validate", "Validate the signature in the given base RIM.")
+ {
+ fileOption,
+ certificateOption
+ };
+ var debugCommand = new Command("debug", "Print out the significant portions of a base RIM and the expected signature value.")
+ {
+ fileOption,
+ privateKeyOption
+ };
+
+ signCommand.SetHandler(async (file, privateKey) =>
+ {
+ await SignXml(file, privateKey);
+ }, fileOption, privateKeyOption);
+ validateCommand.SetHandler(async (file, certificate) =>
+ {
+ await ValidateXml(file, certificate);
+ }, fileOption, certificateOption);
+ debugCommand.SetHandler(async (file, privateKey) =>
+ {
+ await DebugRim(file, privateKey);
+ }, fileOption, privateKeyOption);
+
+ rootCommand.AddCommand(signCommand);
+ rootCommand.AddCommand(validateCommand);
+ rootCommand.AddCommand(debugCommand);
+
+ return rootCommand.InvokeAsync(args).Result;
+ }
+ internal static async Task SignXml(string xmlFilename, string keyFilename)
+ {
+ if (String.IsNullOrWhiteSpace(xmlFilename))
+ throw new ArgumentException(nameof(xmlFilename));
+ if (String.IsNullOrWhiteSpace(keyFilename))
+ throw new ArgumentException(nameof(keyFilename));
+
+ Console.Write("Signing xml...");
+
+ // Load an XML file into a SignedXML object.
+ XmlDocument unsignedDoc = new XmlDocument();
+ unsignedDoc.Load(xmlFilename);
+ SignedXml signedXml = new SignedXml(unsignedDoc);
+
+ //Load private key from file
+ string privateKeyText = File.ReadAllText(keyFilename);
+ var privateKey = RSA.Create();
+ privateKey.ImportFromPem(privateKeyText);
+
+ // Add the key to the SignedXml document.
+ signedXml.SigningKey = privateKey;
+
+ // Create a reference to be signed.
+ Reference reference = new Reference();
+ reference.Uri = "";
+
+ // Add an enveloped transformation to the reference.
+ XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
+ reference.AddTransform(env);
+
+ // Add the reference to the SignedXml object.
+ signedXml.AddReference(reference);
+
+ // Add keyinfo block
+ KeyInfo keyInfo = new KeyInfo();
+ keyInfo.AddClause(new RSAKeyValue((RSA)privateKey));
+ signedXml.KeyInfo = keyInfo;
+
+ // Compute the signature.
+ signedXml.ComputeSignature();
+
+ // Get the XML representation of the signature and save
+ // it to an XmlElement object.
+ XmlElement xmlDigitalSignature = signedXml.GetXml();
+
+ // Append the element to the XML document.
+ unsignedDoc.DocumentElement.AppendChild(unsignedDoc.ImportNode(xmlDigitalSignature, true));
+ string signedFilename = "signed_" + xmlFilename;
+ unsignedDoc.Save(signedFilename);
+ Console.WriteLine("Xml signed and written to " + signedFilename);
+ }
+
+ // Verify the signature of an XML file against an asymmetric
+ // algorithm and return the result.
+ internal static async Task ValidateXml(string signedFilename, string certFilename)
+ {
+ // Check arguments.
+ if (String.IsNullOrWhiteSpace(signedFilename))
+ throw new ArgumentException(nameof(signedFilename));
+ if (certFilename == null)
+ throw new ArgumentException(nameof(certFilename));
+
+ Console.Write("Verifying signature...");
+ // Create a new SignedXml object and pass it
+ // the XML document class.
+ XmlDocument signedDoc = new XmlDocument();
+ signedDoc.Load(signedFilename);
+ SignedXml signedXml = new SignedXml(signedDoc);
+
+ //Load public cert from file
+ X509Certificate2 signingCert = new X509Certificate2(certFilename);
+ RSA publicKey = signingCert.GetRSAPublicKey();
+
+ // Find the "Signature" node and create a new
+ // XmlNodeList object.
+ XmlNodeList nodeList = signedDoc.GetElementsByTagName("Signature");
+
+ // Throw an exception if no signature was found.
+ if (nodeList.Count <= 0)
+ {
+ throw new CryptographicException("Verification failed: No Signature was found in the document.");
+ }
+
+ // This example only supports one signature for
+ // the entire XML document. Throw an exception
+ // if more than one signature was found.
+ if (nodeList.Count >= 2)
+ {
+ throw new CryptographicException("Verification failed: More than one signature was found for the document.");
+ }
+
+ // Load the first node.
+ signedXml.LoadXml((XmlElement)nodeList[0]);
+ Boolean isValid = false;
+ try
+ {
+ isValid = signedXml.CheckSignature(publicKey);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ }
+
+ // Check the signature and return the result.
+ if (isValid)
+ {
+ Console.WriteLine("Signature is valid!");
+ }
+ else
+ {
+ Console.WriteLine("Signature is not valid.");
+ }
+ }
+
+ internal static async Task DebugRim(string filename, string keyFilename)
+ {
+ if (String.IsNullOrWhiteSpace(filename))
+ {
+ throw new ArgumentException(nameof(filename));
+ } else if (String.IsNullOrWhiteSpace(keyFilename))
+ {
+ throw new ArgumentException(nameof(keyFilename));
+ }
+
+ XmlDocument xmlToBeSigned = new XmlDocument();
+ XmlDocument xmlDoc = new XmlDocument();
+ xmlDoc.Load(filename);
+ XmlNodeList nodes = xmlDoc.GetElementsByTagName("SoftwareIdentity");
+ XmlNodeList signatureNodes = xmlDoc.GetElementsByTagName("Signature");
+
+ //Assumes there is only one signature; may change in the future for multiple signatures
+ if (signatureNodes.Count > 0)
+ {
+ nodes[0].RemoveChild(signatureNodes[0]);
+ }
+ xmlToBeSigned.AppendChild(xmlToBeSigned.ImportNode(nodes[0], true));
+ string outFileName = "ToBeSigned_" + filename;
+ xmlToBeSigned.Save(outFileName);
+ Console.WriteLine("Xml data to be signed parsed to " + outFileName);
+
+ //Load private key from file
+ string privateKeyText = File.ReadAllText(keyFilename);
+ var privateKey = RSA.Create();
+ privateKey.ImportFromPem(privateKeyText);
+
+ // Add the key to the SignedXml document.
+ SignedXml signedXml = new SignedXml(xmlToBeSigned);
+ signedXml.SigningKey = privateKey;
+
+ // Create a reference to be signed.
+ Reference reference = new Reference();
+ reference.Uri = "";
+
+ // Add an enveloped transformation to the reference.
+ XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
+ reference.AddTransform(env);
+
+ // Add the reference to the SignedXml object.
+ signedXml.AddReference(reference);
+
+ signedXml.ComputeSignature();
+ Signature signature = signedXml.Signature;
+ Console.WriteLine("For the data to be signed the expected signature value is "
+ + Encoding.Default.GetString(signature.SignatureValue));
+ }
+
+}