/* ' $Id: MakeDigestValue.cs $ ' $Date: 2013-09-04 14:34 $ ' $Author: dai $ ' This module uses methods from the CryptoSys (tm) PKI Toolkit available from ' <www.cryptosys.net/pki/>. ' Add a reference to `diCrSysPKINet.dll` in your project. ' *************************** COPYRIGHT NOTICE ****************************** ' This code was originally written by David Ireland and is copyright ' (C) 2013 DI Management Services Pty Ltd <www.di-mgt.com.au>. ' Provided "as is". No warranties. Use at your own risk. You must make your ' own assessment of its accuracy and suitability for your own purposes. ' It is not to be altered or distributed, except as part of an application. ' You are free to use it in any application, provided this copyright notice ' is left unchanged. ' ************************ END OF COPYRIGHT NOTICE ************************** */ using System; using System.Text; using CryptoSysPKI; namespace MakeDigestValue { class MakeDigestValue { static void Main(string[] args) { Test_DigestSignedInfo(); } static void Test_DigestSignedInfo() { string strComputed, strCorrect; Console.WriteLine("\n" + "F60T33 Outer XML-Dsig:"); strCorrect = "98B466FFE6A5E73B85962BC7300841C5F1693D3A"; strComputed = MakeDigestValueOfSignedInfo("4OTWXyRl5fw3htjTyZXQtYEsC3E=", "SetDoc", true); Console.WriteLine("COMPUTED={0}", strComputed); Console.WriteLine("CORRECT= {0}", strCorrect); Console.WriteLine("\n" + "F60T33 Inner XML-Dsig:"); strCorrect = "353CABFE369AAD9BEA21F5FA06CB367960FFB7D4"; strComputed = MakeDigestValueOfSignedInfo("hlmQtu/AyjUjTDhM3852wvRCr8w=", "F60T33", false); Console.WriteLine("COMPUTED={0}", strComputed); Console.WriteLine("CORRECT= {0}", strCorrect); } /* 'SPECIFIC FORM (no whitespace before elements): ' <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="#..."> ' <Transforms> ' <Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> ' </Transforms> ' <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> ' <DigestValue>...</DigestValue> ' </Reference> ' </SignedInfo> ' To canonicalize the SignedInfo, do the following:- ' 1. Replace any CR-LF pairs with single LF char ' 2. Add the xmlns attributes to the SignedInfo tag ' 3. Convert the empty 'Method' elements to start-end tag pairs ' 4. Use double-quotes (") around attribute values ' 5. Do NOT change any other whitespace chars outside the tags ' -- assumes all other c14n aspects are dealt with in the hard-coding */ /// <summary> /// Create digest value of SignedInfo /// </summary> /// <param name="strDigestValue64">DigestValue of data in base64 format</param> /// <param name="strReference">Reference as in <Reference URI="#..."></param> /// <param name="fIncludeXMLSchema">Flag to include XMLSchema-instance attribute</param> /// <param name="ShowDebug">[Optional] Show debugging output</param> /// <returns>SHA-1 digest of canonicalized <SignedInfo> element in HEX format</returns> static string MakeDigestValueOfSignedInfo(string strDigestValue64, string strReference, bool fIncludeXMLSchema, bool ShowDebug) { string signedInfoCanonic, digestHex, firstLine; // Create a hard-coded version of the canonicalized SignedInfo // Note that "\n" is just a single newline character (ASCII 10) if (fIncludeXMLSchema) { firstLine = @"<SignedInfo xmlns=""http://www.w3.org/2000/09/xmldsig#"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"">"; } else { firstLine = @"<SignedInfo xmlns=""http://www.w3.org/2000/09/xmldsig#"">"; } signedInfoCanonic = firstLine + "\n" + @"<CanonicalizationMethod Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315""></CanonicalizationMethod>" + "\n" + @"<SignatureMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#rsa-sha1""></SignatureMethod>" + "\n" + @"<Reference URI=""#" + strReference + @""">" + "\n" + @"<Transforms>" + "\n" + @"<Transform Algorithm=""http://www.w3.org/TR/2001/REC-xml-c14n-20010315""></Transform>" + "\n" + @"</Transforms>" + "\n" + @"<DigestMethod Algorithm=""http://www.w3.org/2000/09/xmldsig#sha1""></DigestMethod>" + "\n" + @"<DigestValue>" + strDigestValue64 + "</DigestValue>" + "\n" + @"</Reference>" + "\n" + @"</SignedInfo>"; if (ShowDebug) { int cols = 0; for (int i = 0; i < signedInfoCanonic.Length; i++) { char c = signedInfoCanonic[i]; byte b = (byte)c; if (' ' <= b && b <= '~') Console.Write(c); else Console.Write('.'); cols++; if (cols >= 79) { Console.Write('\n'); cols = 0; } } Console.Write('\n'); } // Compute the digest in hex form digestHex = Hash.HexFromString(signedInfoCanonic, HashAlgorithm.Sha1); return digestHex; } // Overload... static string MakeDigestValueOfSignedInfo(string strDigestValue64, string strReference, bool fIncludeXMLSchema) { return MakeDigestValueOfSignedInfo(strDigestValue64, strReference, fIncludeXMLSchema, false); } } }