The melody of logic will always play out the truth. ~ Narumi Ayumu, Spiral
I was trying to tackle this issue in my last job using Delphi accessing the Windows Crypto API provided by Microsoft and the final result was a disaster. My boss didn't like my asnwer. I quitted my job shortly after that.
Now with C# and the powerful .NET library, it's time to give it one more shot.
By the way, I read the article published on CodeProject and downloaded their source code to toy with. Like you, I ran into so many problems and eventually jump back to MSDN for more reading. Oh well, I can't complain even though I can't make their codes work as it's claimed. I have to give the author full credit for doing a good job in explaining the article.
After many hours of reading and so much more to read, I at least came up with a working sample:
using System;using System.Xml;using System.Security.Cryptography;using System.Security.Cryptography.Xml;
namespace Test{ public class XmlSigning { public XmlSigning() {} private XmlDocument CreateDocument() { // CreateDocument creates 2 xml nodes to be signed: key and expire XmlDocument XmlDoc = new XmlDocument(); XmlNode Node = XmlDoc.CreateElement("license"); XmlDoc.AppendChild(Node); Node = XmlDoc.CreateElement("key"); Node.InnerText = "1w5t00kg7~\\??><M."; XmlDoc.DocumentElement.AppendChild(Node); Node = XmlDoc.CreateElement("expire"); Node.InnerText = "1/1/5000"; XmlDoc.DocumentElement.AppendChild(Node); return XmlDoc; } private void SignXmlDocument(RSA Key, XmlDocument XmlDoc) { SignedXml sxml = new SignedXml(XmlDoc); sxml.SigningKey = Key; // Canonicalization removes white space and formatting sxml.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigCanonicalizationUrl; // Create a reference for packaging everything into the message. Reference r = new Reference(""); // A signature transformation is applied to encrypt the hash r.AddTransform(new XmlDsigEnvelopedSignatureTransform(false)); sxml.AddReference(r); sxml.ComputeSignature(); // Get the xml representation of the signature XmlElement Signature = sxml.GetXml(); XmlDoc.DocumentElement.AppendChild(Signature); } private bool VerifyXmlDocument(RSA Key, XmlDocument XmlDoc) { SignedXml sxml = new SignedXml(XmlDoc); // I need to read more about this XmlNode Signature = XmlDoc.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0]; sxml.LoadXml((XmlElement) Signature); if (sxml.CheckSignature(Key)) { return true; } else { return false; } } public void DigitizeXml() { RSA Key = RSA.Create(); XmlDocument XmlDoc = CreateDocument(); SignXmlDocument(Key, XmlDoc); XmlDoc.Save("Signature.xml"); // Verify the signature on our XML Document is good if (VerifyXmlDocument(Key, XmlDoc)) { Console.WriteLine("OK"); } else { Console.WriteLine("BAD SIGNATURE"); } // Get the key node from the xml file. Change the value of the key node XmlNode XmlNode = XmlDoc.GetElementsByTagName("key")[0]; XmlNode.InnerText = "1w5t00kg7~\\??><M"; // Get the expire node from the xml file. Change the value of the expire node //XmlNode XmlNode = XmlDoc.GetElementsByTagName("expire")[0]; //XmlNode.InnerText = "2/1/5000"; // Now the signature check should fail if (VerifyXmlDocument(Key, XmlDoc)) { Console.WriteLine("OK"); } else { Console.WriteLine("BAD SIGNATURE"); } } } public class Test { public static void Main(string[] args) { XmlSigning ds = new XmlSigning(); ds.DigitizeXml(); } } }
Below is a list of points implemented differently from the CodeProject article:
1. I removed the code to generate the key and machine code because it causes so many problems2. I put everything into one single file for posting in here, not seperating them into 3 different files: ExtractPubKey.cs, Sign.cs and Verify.cs3. I believe the code posted in here is a little easier to follow and understand. 4. I still don't have a clear idea which is the best way to control license. a revisit of this issue is required until I have a better understanding5. Expiry date is not yet implemented
Oh well, that's all for now.
Hope it helps