Signing XML Documents With RSA Keys
// December 6th, 2009 // Useful Code
This is a very useful tool when attempting to create a simple licensing protocol. You can distribute a plain text XML document which has been signed using a private key and validate it remotely using the public key. Just make sure to not store the public key in plain text otherwise a smart user can simply replace your public key and regenerate their own license file.
/// <summary>
/// SIGN AN XML DOCUMENT USING THE PRIVATE KEY
/// </summary>
/// <param name="Doc">XML DOCUMENT TO BE SHOULD SIGNED</param>
/// <param name="PrivateKey">PRIVATE RSA KEY USED TO SIGN XML</param>
public static void SignXml(XmlDocument Doc, string PrivateKey)
{
RSACryptoServiceProvider tmpRsa = new RSACryptoServiceProvider();
//VERIFY ALL ARGUMENTS HAVE BEEN PASSED IN
if (Doc == null)
throw new ArgumentException("Doc");
if (PrivateKey == null)
throw new ArgumentException("Key");
//IMPORT THE PRIVATE KEY INTO AN RSA PROVIDER
tmpRsa.ImportCspBlob(Convert.FromBase64String(PrivateKey));
//CREATE A SIGNED XML DOCUMENT
SignedXml signedXml = new SignedXml(Doc);
//ADD THE RSA KEY TO THE SIGNED DOCUMENT
signedXml.SigningKey = tmpRsa;
//CREATE A REFERENCE TO BE SIGNED
Reference reference = new Reference();
reference.Uri = "";
//CREATE AN ENVELOPED SIGNATURE WHICH
XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(true);
reference.AddTransform(env);
//ADD THE REFERENCE TO THE SIGNED DOCUMENT
signedXml.AddReference(reference);
//COMPUTE THE DOCUMENTS SIGNATURE
signedXml.ComputeSignature();
//RETRIEVE THE XML SIGNATURE FROM THE DOCUMENT
XmlElement xmlDigitalSignature = signedXml.GetXml();
//APPEND THE SIGNATURE TO THE END OF THE DOCUMENT
//Doc.DocumentElement.AppendChild(Doc.ImportNode(xmlDigitalSignature, true));
Doc.DocumentElement.AppendChild(xmlDigitalSignature);
}
/// <summary>
/// VERIFY A SIGNED XML DOCUMENT
/// </summary>
/// <param name="Doc">SIGNED XML DOCUMENT</param>
/// <param name="PublicKey">PUBLIC KEY TO VERIFY SIGNATURE AGAINST</param>
/// <returns></returns>
public static bool VerifyXml(XmlDocument Doc, string PublicKey)
{
RSACryptoServiceProvider tmpRsa = new RSACryptoServiceProvider();
//VERIFY ALL ARGUMENTS HAVE BEEN PASSED IN
if (Doc == null)
throw new ArgumentException("Doc");
if (PublicKey == null)
throw new ArgumentException("Key");
//IMPORT THE PUBLIC KEY INTO AN RSA PROVIDER
tmpRsa.ImportCspBlob(Convert.FromBase64String(PublicKey));
//HOLD THE SIGNED DOCUMENT
SignedXml signedXml = new SignedXml(Doc);
//LOCATE THE SIGNATURE NODE IN THE DOCUMENT
XmlNodeList nodeList = Doc.GetElementsByTagName("Signature");
//IF WE CANT FIND THE NODE THEN THIS DOCUMENT IS NOT SIGNED
if (nodeList.Count <= 0)
{
throw new CryptographicException("Verification failed: No Signature was found in the document.");
}
//IF THERE ARE MORE THEN ONE SIGNATURES THEN FAIL
if (nodeList.Count >= 2)
{
throw new CryptographicException("Verification failed: More that one signature was found for the document.");
}
//LOAD THE SIGNATURE NODE INTO THE SIGNEDXML DOCUMENT
signedXml.LoadXml((XmlElement)nodeList[0]);
//CHECK THE SIGNATURE AND SEND THE RESULT
return signedXml.CheckSignature(tmpRsa);
}
Michael E. Chancey Jr. Software Engineer Extraordinaire