mirror of
https://github.com/nsacyber/HIRS.git
synced 2025-04-06 19:06:52 +00:00
Merge pull request #488 from nsacyber/c#_xmldsig_validation
[#487] Create xml_dsig_tool
This commit is contained in:
commit
e5da94fa08
30
tools/xml_dsig_tool/README.md
Normal file
30
tools/xml_dsig_tool/README.md
Normal file
@ -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
|
||||
|
23
tools/xml_dsig_tool/RimSignCert.pem
Normal file
23
tools/xml_dsig_tool/RimSignCert.pem
Normal file
@ -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-----
|
28
tools/xml_dsig_tool/privateRimKey.pem
Normal file
28
tools/xml_dsig_tool/privateRimKey.pem
Normal file
@ -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-----
|
11
tools/xml_dsig_tool/unsigned.xml
Normal file
11
tools/xml_dsig_tool/unsigned.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<SoftwareIdentity xmlns="http://standards.iso.org/iso/19770/-2/2015/schema.xsd" xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" corpus="false" name="Example.com BIOS" patch="false" supplemental="false" tagId="94f6b457-9ac9-4d35-9b3f-78804173b65as" tagVersion="0" version="01" versionScheme="multipartnumeric" xml:lang="en">
|
||||
<Entity name="Example Inc" regid="http://Example.com" role="softwareCreator tagCreator"/>
|
||||
<Link href="https://Example.com/support/ProductA/firmware/installfiles" rel="installationmedia"/>
|
||||
<Meta xmlns:n8060="http://csrc.nist.gov/ns/swid/2015-extensions/1.0" xmlns:rim="https://trustedcomputinggroup.org/wp-content/uploads/TCG_RIM_Model" n8060:colloquialVersion="Firmware_2019" n8060:edition="12" n8060:product="ProductA" n8060:revision="r2" rim:BindingSpec="PC Client RIM" rim:BindingSpecVersion="1.2" rim:PayloadType="direct" rim:firmwareManufacturerId="00213022" rim:firmwareManufacturerStr="BIOSVendorA" rim:firmwareModel="A0" rim:firmwareVersion="12" rim:pcURIGlobal="https://Example.com/support/ProductA/" rim:pcURILocal="/boot/tcg/manifest/switag/" rim:platformManufacturerId="00201234" rim:platformManufacturerStr="Example.com" rim:platformModel="ProductA" rim:platformVersion="01"/>
|
||||
<Payload xmlns:rim="https://trustedcomputinggroup.org/wp-content/uploads/TCG_RIM_Model" rim:supportRIMFormat="TCG_EventLog_Assertion" rim:supportRIMURIGlobal="https://Example.com/support/ProductA/firmware/rims/">
|
||||
<Directory name="rim">
|
||||
<File xmlns:SHA256="http://www.w3.org/2001/04/xmlenc#sha256" SHA256:hash="4479ca722623f8c47b703996ced3cbd981b06b1ae8a897db70137e0b7c546848" name="Example.com.BIOS.01.rimel" size="7549"/>
|
||||
</Directory>
|
||||
</Payload>
|
||||
</SoftwareIdentity>
|
249
tools/xml_dsig_tool/xmlDsig.cs
Normal file
249
tools/xml_dsig_tool/xmlDsig.cs
Normal file
@ -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<int> Main(String[] args)
|
||||
{
|
||||
var fileOption = new Option<string>(
|
||||
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<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.")
|
||||
{
|
||||
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 <signature> 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));
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user