Read commandline arguments for sign and validate functions. Arguments for either include an xml file and a pem file.

This commit is contained in:
chubtub 2022-07-28 07:46:54 -04:00
parent 1836620f98
commit de2db0491d

View File

@ -1,87 +1,103 @@
using System;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml; using System.Security.Cryptography.Xml;
using System.Xml; using System.Xml;
using System.CommandLine;
/** /**
* This program reads in the following files: * This command line program has three commands:
* 1. Public certificate, pem format * 1. sign - append a signature calculated from a user-provided private key
* 2. Corresponding private key, pem format * 2. validate - validate a signature with a user-provided certificate
* 3. Unsigned xml document * 3. debug - print out important components of a signed XML document
* 4. Signed xml document *
* The two functions are SignXml() and VerifyXml() and are called in succession. * The validate functioin strictly checks the cryptographic integrity of the signature,
*
* XmlDocument.PreserveWhitespace(false) allows the subsequent signed xml document
* to pass validation.
*
* VerifyXml() strictly checks the cryptographic integrity of the Signature block,
* it does not verify the integrity of the certificate chain. * it does not verify the integrity of the certificate chain.
*/ */
public class VerifyXML class Rimtool
{ {
public static void Main(String[] args) static async Task<int> Main(String[] args)
{ {
try var fileOption = new Option<string>(
{ name: "--file",
const string signingAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"; description: "The filename input for the command.",
const string signingCertName = "RimSignCert.pem"; parseArgument: result =>
const string privateKeyFilename = "privateRimKey.pem";
const string unsignedFilename = "unsigned.xml";
const string signedFilename = "signed.xml";
const string signedRIM = "signedRIM.swidtag";
//Load public cert from file
X509Certificate2 signingCert = new X509Certificate2(signingCertName);
RSA publicKey = signingCert.GetRSAPublicKey();
//Load private key from file
string privateKeyText = System.IO.File.ReadAllText(privateKeyFilename);
//System.Console.WriteLine("Using private key: " + privateKeyText);
var privateKey = RSA.Create();
privateKey.ImportFromPem(privateKeyText);
// Load an XML file into the XmlDocument object.
XmlDocument unsignedDoc = new XmlDocument();
unsignedDoc.Load(unsignedFilename);
SignXml(unsignedDoc, privateKey);
unsignedDoc.Save(signedFilename);
// Verify the signature of the signed XML.
XmlDocument signedDoc = new XmlDocument();
signedDoc.Load(signedFilename);
bool result = VerifyXml(signedDoc, publicKey);
// Display the results of the signature verification to
// the console.
if (result)
{ {
Console.WriteLine("The XML signature is valid!"); string? filePath = result.Tokens.Single().Value;
} if (!File.Exists(filePath))
else {
{ result.ErrorMessage = "File " + filePath + " does not exist.";
Console.WriteLine("The XML signature is not valid."); return null;
} } else
} {
catch (Exception e) return filePath;
}
});
var privateKeyOption = new Option<string>(
name: "--private-key",
description: "The private key with which to sign."
);
var certificateOption = new Option<string>(
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.")
{ {
Console.WriteLine(e.Message); 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.")
{
fileOption
};
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) =>
{
await DebugRim(file);
}, fileOption);
rootCommand.AddCommand(signCommand);
rootCommand.AddCommand(validateCommand);
rootCommand.AddCommand(debugCommand);
return rootCommand.InvokeAsync(args).Result;
} }
private static void SignXml(XmlDocument xmlDoc, RSA rsaKey) internal static async Task SignXml(string xmlFilename, string keyFilename)
{ {
if (xmlDoc == null) if (String.IsNullOrWhiteSpace(xmlFilename))
throw new ArgumentException(nameof(xmlDoc)); throw new ArgumentException(nameof(xmlFilename));
if (rsaKey == null) if (String.IsNullOrWhiteSpace(keyFilename))
throw new ArgumentException(nameof(rsaKey)); throw new ArgumentException(nameof(keyFilename));
Console.Write("Signing xml..."); Console.Write("Signing xml...");
// Create a SignedXml object. // Load an XML file into a SignedXML object.
SignedXml signedXml = new SignedXml(xmlDoc); XmlDocument unsignedDoc = new XmlDocument();
unsignedDoc.Load(xmlFilename);
SignedXml signedXml = new SignedXml(unsignedDoc);
//Load private key from file
string privateKeyText = System.IO.File.ReadAllText(keyFilename);
var privateKey = RSA.Create();
privateKey.ImportFromPem(privateKeyText);
// Add the key to the SignedXml document. // Add the key to the SignedXml document.
signedXml.SigningKey = rsaKey; signedXml.SigningKey = privateKey;
// Create a reference to be signed. // Create a reference to be signed.
Reference reference = new Reference(); Reference reference = new Reference();
@ -96,7 +112,7 @@ public class VerifyXML
// Add keyinfo block // Add keyinfo block
KeyInfo keyInfo = new KeyInfo(); KeyInfo keyInfo = new KeyInfo();
keyInfo.AddClause(new RSAKeyValue((RSA)rsaKey)); keyInfo.AddClause(new RSAKeyValue((RSA)privateKey));
signedXml.KeyInfo = keyInfo; signedXml.KeyInfo = keyInfo;
// Compute the signature. // Compute the signature.
@ -107,28 +123,35 @@ public class VerifyXML
XmlElement xmlDigitalSignature = signedXml.GetXml(); XmlElement xmlDigitalSignature = signedXml.GetXml();
// Append the element to the XML document. // Append the element to the XML document.
xmlDoc.DocumentElement.AppendChild(xmlDoc.ImportNode(xmlDigitalSignature, true)); unsignedDoc.DocumentElement.AppendChild(unsignedDoc.ImportNode(xmlDigitalSignature, true));
// unsignedDoc.Save(signedFilename);
Console.WriteLine("Xml signed."); Console.WriteLine("Xml signed.");
} }
// Verify the signature of an XML file against an asymmetric // Verify the signature of an XML file against an asymmetric
// algorithm and return the result. // algorithm and return the result.
private static Boolean VerifyXml(XmlDocument xmlDoc, RSA key) internal static async Task ValidateXml(string signedFilename, string certFilename)
{ {
// Check arguments. // Check arguments.
if (xmlDoc == null) if (String.IsNullOrWhiteSpace(signedFilename))
throw new ArgumentException("xmlDoc"); throw new ArgumentException(nameof(signedFilename));
if (key == null) if (certFilename == null)
throw new ArgumentException("key"); throw new ArgumentException(nameof(certFilename));
Console.Write("Verifying signature..."); Console.Write("Verifying signature...");
// Create a new SignedXml object and pass it // Create a new SignedXml object and pass it
// the XML document class. // the XML document class.
SignedXml signedXml = new SignedXml(xmlDoc); 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 // Find the "Signature" node and create a new
// XmlNodeList object. // XmlNodeList object.
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Signature"); XmlNodeList nodeList = signedDoc.GetElementsByTagName("Signature");
// Throw an exception if no signature was found. // Throw an exception if no signature was found.
if (nodeList.Count <= 0) if (nodeList.Count <= 0)
@ -149,7 +172,7 @@ public class VerifyXML
Boolean isValid = false; Boolean isValid = false;
try try
{ {
isValid = signedXml.CheckSignature(key); isValid = signedXml.CheckSignature(publicKey);
} }
catch (Exception e) catch (Exception e)
{ {
@ -157,7 +180,24 @@ public class VerifyXML
} }
// Check the signature and return the result. // Check the signature and return the result.
return isValid; if (isValid)
{
Console.WriteLine("Signature is valid!");
} else
{
Console.WriteLine("Signature is not valid.");
}
}
internal static async Task DebugRim(string filename)
{
if (String.IsNullOrWhiteSpace(filename))
{
throw new ArgumentException(nameof(filename));
}
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(filename);
} }
} }