XML Digital Signatures With SignedXML
XML Digital Signatures With SignedXML
Jason uses the SignedXML class in .NET to create and verify an XML
<Signature> block using the W3C's XMLDSIG standard
Applying a digital signature to data involves computing a hash code for the data and then encrypting
the hash code with an asymmetric algorithm and a private key. This results in a small amount of
ciphertext. Typically, the number of bits of ciphertext is equal to the number of bits selected for the
encryption key. Because data you might wish to sign in the real world can be very large, encrypting
the hash code of the data rather than the data itself is more efficient, though it provides no
confidentiality protection over the data because the signed data is not encrypted itself.
When you receive signed data, it must contain at least a copy of the original data and the ciphertext
version of the hash code of that data. If you know in advance what key to use to decrypt the
ciphertext and you know what hash algorithm was used on the digitally signed data, then you could
verify the signature without difficulty. In the real world, we anticipate not knowing such things in
advance. It's possible that we won't even know in advance what the data format is of the original data
(often referred to in crypto documentation as the “message”), which could cause a real problem as we
attempt to figure out where the plaintext data of the message stops and where the ciphertext data of
its digital signature begins. If we were to create digital signatures in their most basic form as just
described, we might attach ciphertext of the data's encrypted hash code to the beginning or the end of
the plaintext message data so that it becomes a part of the message. There are times when this
approach works well, but it is also possible that we'll need to store the signature as a separate
detached data block so that we don't corrupt the original data format.
Alternatively, we could design a digital signature envelope standard and write up complex
contingency rules that tell us how to apply the envelope standard in various scenarios where a simple
attached or detached envelope structure would corrupt the data being signed or create some other
failure mode. RSA's Public Key Cryptography Standards (PKCS) define just such envelope structures
and their encoding rules—while leaving room to adapt and extend algorithms, identifiers, language
and character sets, and so forth, so that an organization's Public Key Infrastructure (PKI) will
function for everyone, everywhere, at all times, even across organizational boundaries. The PKCS
standards are a big part of the reason that PKI is no fun (see http://www.rsasecurity.com/rsalabs/
pkcs).
Since XML offers improvements over fixed data structures, such as the ability to add arbitrary
branches and embeddings without adversely impacting any properly written XML application,
naturally we'll want a consistent way to attach digital signatures to XML data. The W3C XMLDSIG
specification serves this purpose (see http://www.w3.org/Signature). Microsoft's .NET Framework
provides a class called SignedXML in the System.Security.Cryptography.Xml namespace that
implements XMLDSIG for you. The SignedXML class can, and should, be used as a general purpose
XML signature creation and verification mechanism because it attempts to conform to the XMLDSIG
standard. Compatibility with other XMLDSIG-based systems is, therefore, supposed to be automatic,
although differences in encoding and formatting, including things like how whitespace is handled (or
ignored) by different XMLDSIG implementations, still causes incompatibilities in practice.
Using XMLDSIG through the SignedXML class gives you a standards-based solution for attaching
and later parsing an XML-encoded digital signature so that it can be verified or removed from the
signed data. Detached signature envelope files are also easy to create with SignedXML so that the
signature data can be stored apart from the data that was signed. However, the real power of the
XML-based signature is its ability to become a part of the data without causing problems for any
software already in existence that expects XML data without attached signatures. The XMLDSIG
standard defines the way in which signature verification public key and cryptographic-algorithm
information is stored, along with the ciphertext of the signed data's encrypted hash code.
To create and attach a signature to an XML data block using the SignedXML class requires only a
few steps. First, populate a new XmlDocument with the XML to be signed. Next, create an instance
of the SignedXml class and create or load an RSA key pair using the RSA class. Set the SigningKey
property of the signedXML object and write the XmlDocument to a DataObject. Finally, add the
object and an intradocument URI reference for it to the SignedXML object, along with an instance of
the KeyInfo class containing the signature key pair to the SignedXML object, and you're ready to
compute and display the XMLDSIG signature wrapped around the plaintext message. Listing 1
shows these steps:
Listing 1
The SignedXML class creates an XML Signature tag (node) that wraps around (encapsulates) the
XML data that is signed. Shown below is an outline of the basic XMLDSIG Signature as created by
the .NET implementation with the DigestValue, SignatureValue, and RSAKeyValue element
contents removed for readability. The message that is signed appears in the XML within an Object
node that is assigned an object ID during the signature creation shown in Listing 1.
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315" />
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-
sha1"
/> <Reference URI="#Message"><DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue> </DigestValue>
</Reference></SignedInfo>
<SignatureValue> </SignatureValue>
<KeyInfo><KeyValue xmlns="http://www.w3.org/2000/09/xmldsig#">
<RSAKeyValue><Modulus> </Modulus>
<Exponent> </Exponent>
</RSAKeyValue></KeyValue></KeyInfo>
<Object Id="Message"><Plaintext xmlns="msg">This is the plaintext
message.
Verifying a signature that has previously been attached to signed XML requires fewer steps because
the key and hash information is already selected for us by whomever or whatever created the
XMLDSIG Signature tag. The SignedXML class automatically creates the proper objects and
extracts the public key from the signature's KeyValue tag, uses it to decrypt the hash code, and then
recomputes the hash code of the original data (the message) to confirm that it matches the one found
in the signature. Listing 2 shows the steps required to verify an XML signature:
Listing 2
AsymmetricAlgorithm pubkey;
XmlDocument xmlsignature = new XmlDocument();
xmlsignature.PreserveWhitespace = true;
xmlsignature.LoadXml(signature);
System.Security.Cryptography.Xml.SignedXml sigverif = new SignedXml
();
sigverif.LoadXml(xmlsignature.DocumentElement);
if (sigverif.CheckSignatureReturningKey(out pubkey))
{ Console.Out.WriteLine("Signature Verified. Public Key: " + pubkey.
ToXmlString(false));
}
Correcting a Mistake
The last article in this newsletter contained a rough sample application written in C# that was
intended to extract the public key and information about its apparent owner from a digitally signed
file. Unfortunately, the sample was a little too rough and it didn't compile as shown. Below is a
replacement sample that does compile, with a minor usage modification whereby errors caught in the
try/catch blocks are output to standard error rather than standard output. This makes it possible to
redirect the output of the program to a file, giving you only the signatures, one per line, and no
extraneous information in the file.
using System.Security.Cryptography.X509Certificates;
using System.IO;
namespace PKInventory
{ class main
{ [STAThread] static void Main(string[] args)
{ X509Certificate xcert = null;
try { DirectoryInfo d = new DirectoryInfo(Directory.
GetCurrentDirectory());
FileInfo[] allFiles = d.GetFiles();
foreach (FileInfo f in allFiles) { try { xcert =
X509Certificate.CreateFromSignedFile(f.Name);
Console.Out.WriteLine(f.Name + "\t" + xcert.GetName() +
"\t"
+ xcert.GetPublicKeyString());
}
catch (Exception sigErr)
{ Console.Error.WriteLine(f.Name + ": Unable to read DER-
encoded signature.");
}
}
}
catch (Exception e)
{Console.Error.WriteLine("Unable to read directory. Error: "
+ e);}
}
}
}
It should also have been noted in the previous article that most Windows binaries ship without a
digital signature. Instead of signing each file, Microsoft relies on Windows File Protection (WFP)
and the Security Catalog (.CAT file) mechanism to do whatever it was that Microsoft thought WFP
was supposed to do (i.e., put an end to DLL Hell) with the help of these detached signatures. As
discussed previously in this newsletter, WFP has some serious design flaws that pose security risks
and that also undermine the relief from DLL Hell that WFP might otherwise provide. Use the code
shown here to extract signature details from each .CAT file that you find under \system32\CatRoot\
in addition to directories that contain actual signed binaries in order to get a more complete picture of
the software publisher key rings upon which your trust policies are presently based. If we don't
manually examine keys and keep track of the ones that we already trust, then we do away with any
meaningful security protection that PKI is supposed to provide for interorganization trust. After all,
the whole point of relying on PKI for service identity authentication or software publishing is to
improve our ability to make judgment calls about interorganization trust.
Jason Coombs works as forensic analyst and expert witness in court cases involving digital evidence.
Information security and network programming are his areas of special expertise. He can be reached
at [email protected].
Read previous newsletters online at http://www.windevnet.com/newsletters/.