using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography; using System.Xml; using System.Security.Cryptography.Xml; namespace xmlSignDemoTest { class signDemo { public static XmlDocument signXmlDomucument(XmlDocument xmlDoc, string certsn) { try { xmlDoc.PreserveWhitespace = true; X509Store store = new X509Store("MY", StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); if (store.Certificates.Find(X509FindType.FindBySerialNumber, certsn, true).Count == 1) { X509Certificate2 x509 = store.Certificates.Find(X509FindType.FindBySerialNumber, certsn, true)[0]; signDemo.SignXml(xmlDoc, x509); } else throw new Exception("Certifikát nebyl nalzen"); return xmlDoc; } catch (Exception ex) { //ošetřit throw; } } public static void SignXml(XmlDocument xmlDoc, X509Certificate2 cert) { // ověření neprázdnosti. if (xmlDoc == null) throw new ArgumentException("xmlDoc"); if (cert == null) throw new ArgumentException("Cert"); CryptoConfig.AddAlgorithm(typeof(Security.Cryptography.RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); //generuji Id pro body a timestamp, a certifikát, aby mohly být referencovány string bodyIdStr = "id-" + Guid.NewGuid().ToString(); string timestampIdStr = "ts-" + Guid.NewGuid().ToString(); string certIdStr = "cer-" + Guid.NewGuid().ToString(); XmlNamespaceManager nmgr = new XmlNamespaceManager(xmlDoc.NameTable); nmgr.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); nmgr.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); nmgr.AddNamespace("s", "http://schemas.xmlsoap.org/soap/envelope/"); XmlElement body = xmlDoc.DocumentElement.SelectSingleNode("//s:Envelope/s:Body", nmgr) as XmlElement; XmlAttribute bodyId = xmlDoc.CreateAttribute("wsu", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); bodyId.Value = bodyIdStr; body.Attributes.Append(bodyId); //nalzenu Soap Header hlavičku ve vstupním XML XmlElement node = xmlDoc.DocumentElement.SelectSingleNode("//s:Envelope/s:Header", nmgr) as XmlElement; // do hlavičky přídám element Security XmlElement wsse = xmlDoc.CreateElement("wsse", "Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); node.AppendChild(wsse); XmlElement bst = xmlDoc.CreateElement("wsse", "BinarySecurityToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); bst.InnerText = Convert.ToBase64String(cert.Export(X509ContentType.Cert)); XmlAttribute encType = xmlDoc.CreateAttribute("EncodingType"); encType.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"; bst.Attributes.Append(encType); XmlAttribute valType = xmlDoc.CreateAttribute("ValueType"); valType.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; bst.Attributes.Append(valType); XmlAttribute certId = xmlDoc.CreateAttribute("wsu", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); certId.Value = certIdStr; bst.Attributes.Append(certId); xmlDoc.SelectSingleNode("//s:Envelope/s:Header/wsse:Security", nmgr).AppendChild(bst); DateTime timestampUTCvalue = DateTime.UtcNow; //vytvoříme timestamp hlavičku pro uvedení platnosti a vypršení XmlElement timestamp = xmlDoc.CreateElement("wsu", "Timestamp", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); XmlElement created = xmlDoc.CreateElement("wsu", "Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); created.InnerText = timestampUTCvalue.ToString("yyyy-MM-ddTHH:mm:ssZ"); timestamp.AppendChild(created); XmlElement expires = xmlDoc.CreateElement("wsu", "Expires", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); expires.InnerText = timestampUTCvalue.AddMinutes(10).ToString("yyyy-MM-ddTHH:mm:ssZ"); timestamp.AppendChild(expires); XmlAttribute timestampId = xmlDoc.CreateAttribute("wsu", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); timestampId.Value = timestampIdStr; timestamp.Attributes.Append(timestampId); //doplníme halvičku do vstupního XML xmlDoc.SelectSingleNode("//s:Envelope/s:Header/wsse:Security", nmgr).AppendChild(timestamp); // vytvářím SignedXmlWithId object. SignedXmlWithId signedXml = new SignedXmlWithId(xmlDoc); //nastavím kanonizaci signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#"; //vytvořím a vložím informaci o použitém certiikátu KeyInfo keyInfo = new KeyInfo(); XmlElement token = xmlDoc.CreateElement("wsse", "SecurityTokenReference", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); XmlElement tokenRef = xmlDoc.CreateElement("wsse", "Reference", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); XmlAttribute tokenValType = xmlDoc.CreateAttribute("ValueType"); tokenValType.Value = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"; tokenRef.Attributes.Append(tokenValType); XmlAttribute certURI = xmlDoc.CreateAttribute("URI"); certURI.Value = "#" + certIdStr; tokenRef.Attributes.Append(certURI); token.AppendChild(tokenRef); KeyInfoNode keyNode = new KeyInfoNode(token); keyInfo.AddClause(keyNode); signedXml.KeyInfo = keyInfo; // nastavím podpisový certifikát signedXml.SigningKey = cert.PrivateKey; // vytvořím referenci na podepsanou část (Body), přes Id, které jsme vytvářeli výše. Reference reference = new Reference("#" + bodyIdStr); reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1"; // nastavení transformace reference. reference.AddTransform(new XmlDsigExcC14NTransform()); // vložíme referenci do signedXml objektu signedXml.AddReference(reference); // vytvořím referenci na timestamp, přes Id, které jsme vytvářeli výše. Reference refTimestamp = new Reference("#" + timestampIdStr); // nastavení transformace reference. refTimestamp.AddTransform(new XmlDsigExcC14NTransform()); // vložíme referenci do signedXml objektu signedXml.AddReference(refTimestamp); //nastavím podpisový algoritmus signedXml.SignedInfo.SignatureMethod = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; try { // Vlastní vypočtení podposu. signedXml.ComputeSignature(); } catch (Exception ex) { //ošetři throw ex; } // Vytáhnu data podpisu do XmlElementu xmlDigitalSignature XmlElement xmlDigitalSignature = signedXml.GetXml(); //do elementu Security přidám xmlDigitalSignature xmlDoc.SelectSingleNode("//s:Envelope/s:Header/wsse:Security", nmgr).AppendChild(xmlDigitalSignature); //hotovo :) } public class SignedXmlWithId : SignedXml { public SignedXmlWithId(XmlDocument xml) : base(xml) { } public SignedXmlWithId(XmlElement xmlElement) : base(xmlElement) { } //toto přetížení je nutné aby v nových .net bylo možné referencovat přes Id atribut v namespace http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd. public override XmlElement GetIdElement(XmlDocument doc, string id) { // ověřím, zda reference nebyla provedena v anonymným namespace XmlElement idElem = base.GetIdElement(doc, id); if (idElem == null) //pokud nebylo nalezeno zkouším heldat v namespace http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd { XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable); nsManager.AddNamespace("wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"); idElem = doc.SelectSingleNode("//*[@wsu:Id=\"" + id + "\"]", nsManager) as XmlElement; } return idElem; } } } //definice nutná pro nastavení parametrů podpisu namespace Security.Cryptography { public class RSAPKCS1SHA256SignatureDescription : SignatureDescription { public RSAPKCS1SHA256SignatureDescription() { base.KeyAlgorithm = "System.Security.Cryptography.RSACryptoServiceProvider"; base.DigestAlgorithm = "System.Security.Cryptography.SHA256Managed"; base.FormatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureFormatter"; base.DeformatterAlgorithm = "System.Security.Cryptography.RSAPKCS1SignatureDeformatter"; } public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key) { AsymmetricSignatureDeformatter asymmetricSignatureDeformatter = (AsymmetricSignatureDeformatter) CryptoConfig.CreateFromName(base.DeformatterAlgorithm); asymmetricSignatureDeformatter.SetKey(key); asymmetricSignatureDeformatter.SetHashAlgorithm("SHA256"); return asymmetricSignatureDeformatter; } } } }