using System; using System.Collections; using System.Text; using System.Xml; using System.IO; using Microsoft.Win32; using Microsoft.Web.Services3; using Microsoft.Web.Services3.Design; using Microsoft.Web.Services3.Security; using Microsoft.Web.Services3.Security.Cryptography; using Microsoft.Web.Services3.Security.Tokens; using Microsoft.Web.Services3.Security.X509; using Microsoft.Web.Services3.Security.Xml; using System.Web.Services; using System.Security; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; namespace SpilB2BConsoleApplication { /* * This class modifies the response soap xml structure to eliminate stuff that .NET can't handle yet * * @author René Hjortskov Nielsen, SKAT * @version 1.0 */ class ClientSecurityInputHandler : ReceiveSecurityFilter { private FilterCreationContext context = null; private MyPolicyAssertion myassertion = null; private System.Text.Encoding encode = System.Text.Encoding.UTF8; public ClientSecurityInputHandler(MyPolicyAssertion myassertion, FilterCreationContext context) : base(myassertion.ServiceActor, true, myassertion.ClientActor) { Console.WriteLine("+ClientSecurityInputHandler"); this.context = context; this.myassertion = myassertion; Console.WriteLine("-ClientSecurityInputHandler"); } /* * Last issue to solve is decryption * * Security.LoadXML throws a load exception, possible due to some formatting * * 1) EncryptedKey.LoadXml fails no matter how the XML element WSE 3 EncryptedKey is created * Have even tried to use the EncryptedKey structure created in the request and substitute the SKI and cipher * Anyway, was inspired by the error => WSE590, which lead me to http://support.microsoft.com/kb/922779 * * This approach was a no go. * * 2) Second approach will try and find a programatic way manually decrypting in this pipeline step * * This approach can decrypt the body, which is great! * */ public override SoapFilterResult ProcessMessage(SoapEnvelope envelope) { Console.WriteLine("+ProcessMessage"); if (myassertion.performEncrypt) { Console.WriteLine("+Decrypting"); // Lookup EncryptedKey element and remove it from the SOAP XML Header XmlElement encKeyElem = null; XmlNodeList nl = envelope.DocumentElement.FirstChild.FirstChild.ChildNodes; // Header/Security for (int i = nl.Count - 1; i >= 0; i--) { XmlNode nn = nl.Item(i); if (nn.LocalName.Equals("EncryptedKey")) { encKeyElem = (XmlElement)nn; // Need to remove the EncryptedKey element from Security Header in order to avoid error WSE590 nn.ParentNode.RemoveChild(nn); } } try { //Console.WriteLine("Locating EncryptedData element"); nl = envelope.DocumentElement.OwnerDocument.FirstChild.LastChild.ChildNodes; // Body XmlElement encDataElem = (XmlElement)nl.Item(0); //Console.WriteLine("Populating crypto objects key and data"); System.Security.Cryptography.Xml.EncryptedKey encKey = new System.Security.Cryptography.Xml.EncryptedKey(); encKey.LoadXml(encKeyElem); System.Security.Cryptography.Xml.EncryptedData encData = new System.Security.Cryptography.Xml.EncryptedData(); encData.LoadXml(encDataElem); //Console.WriteLine("Initialize symmetric decryption algorithm"); String symAlgUri = encData.EncryptionMethod.KeyAlgorithm; SymmetricAlgorithm symAlgo = SymmetricAlgorithm.Create("TripleDES"); byte[] iv = new Byte[symAlgo.BlockSize / 8]; Buffer.BlockCopy(encData.CipherData.CipherValue, 0, iv, 0, iv.Length); symAlgo.IV = iv; //Console.WriteLine("Decrypt the asymmetric EncryptedKey a.k.a sessionKey"); EncryptedXml ex = new EncryptedXml(envelope.DocumentElement.OwnerDocument); RSACryptoServiceProvider rsaCrypto = myassertion.cert.PrivateKey as RSACryptoServiceProvider; AsymmetricKeyExchangeDeformatter deformatter = new RSAPKCS1KeyExchangeDeformatter(rsaCrypto); // fOAEP=false //Console.WriteLine("Cipher is " + encode.GetString(encKey.CipherData.CipherValue)); byte[] decKey = deformatter.DecryptKeyExchange(encKey.CipherData.CipherValue); symAlgo.Key = decKey; Console.WriteLine("Decrypted KEY=[" + encode.GetString(decKey) + "]"); // Decrypt EncryptedData and replace it with plaintext; byte[] decBody = ex.DecryptData(encData, symAlgo); Console.WriteLine("Decrypted DATA=[" + encode.GetString(decBody) + "]"); // Replace EncryptedData with decrypted plaintext XmlDocument ownerDocument = new XmlDocument();//encDataElem.OwnerDocument; ownerDocument.LoadXml(encode.GetString(decBody)); XmlNode decNodeData = (XmlNode)ownerDocument.DocumentElement; //output((XmlElement)decNodeData); XmlNode removedEncData = encDataElem.ParentNode.RemoveChild(encDataElem); decNodeData = encDataElem.OwnerDocument.ImportNode(decNodeData, true); envelope.DocumentElement.OwnerDocument.FirstChild.LastChild.AppendChild(decNodeData); nl = envelope.DocumentElement.OwnerDocument.FirstChild.LastChild.ChildNodes; // Body encDataElem = (XmlElement)nl.Item(0); output(encDataElem); Console.WriteLine("-Decrypting"); } catch (Exception e) { dumpXml(envelope.DocumentElement.OwnerDocument, "decrypted.xml"); Console.WriteLine("------> " + e.ToString()); } } // Just process normal signing as usual Console.WriteLine("-ProcessMessage"); return base.ProcessMessage(envelope); } /* * We override the ValidateMessageSecurity in the request pipeline. */ public override void ValidateMessageSecurity(SoapEnvelope envelope, Security security) { Console.WriteLine("+ValidateMessageSecurity"); //security.DidUnderstand = true; Console.WriteLine("-ValidateMessageSecurity"); } /* * Conveniance method * Output is placed in bin/debug folder when running in debug mode */ private void dumpXml(XmlDocument doc, String filename) { XmlTextWriter writer = new XmlTextWriter(filename, Encoding.UTF8); doc.WriteContentTo(writer); writer.Flush(); writer.Close(); } /* * Conveniance method * Output is placed System.out when running in debug mode */ private void output(XmlElement elem) { Console.WriteLine("XML ------------------------"); XmlTextWriter tw = new XmlTextWriter(Console.Out); tw.Indentation = 2; elem.WriteTo(tw); Console.WriteLine("------------------------ XML "); } /* * This section contain methods that were used during development */ private bool verifyToken(XmlElement elem) { try { Microsoft.Web.Services3.Security.EncryptedKey key = new Microsoft.Web.Services3.Security.EncryptedKey(elem); EncryptedKeyToken key2 = new EncryptedKeyToken(elem); EncryptedKeyTokenManager manager = new EncryptedKeyTokenManager(); Console.WriteLine("EncryptedKeyTokenManager verify"); manager.VerifyToken(key2); return true; } catch (Exception e) { Console.WriteLine("+++++++++++++"); Console.WriteLine("" + e.ToString()); Console.WriteLine("-------------"); } return false; } private XmlNodeList KeyInfoNodeList = null; private XmlNodeList SecurityTokenReferenceNodeList = null; private XmlNodeList EncryptedKeyNodeList = null; private void createXmlIndexes(SoapEnvelope envelope) { XmlNamespaceManager nsmanager = new XmlNamespaceManager(envelope.NameTable); nsmanager.AddNamespace("wsse", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); nsmanager.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/"); nsmanager.AddNamespace("ds", "http://www.w3.org/2000/09/xmldsig#"); nsmanager.AddNamespace("soapenc", "http://schemas.xmlsoap.org/soap/encoding/"); nsmanager.AddNamespace("e", "http://www.w3.org/2001/04/xmlenc#"); KeyInfoNodeList = envelope.SelectNodes("/soapenv:Envelope/soapenv:Header/wsse:Security/e:EncryptedKey/ds:KeyInfo", nsmanager); SecurityTokenReferenceNodeList = KeyInfoNodeList[0].SelectNodes("wsse:SecurityTokenReference", nsmanager); EncryptedKeyNodeList = envelope.SelectNodes("/soapenv:Envelope/soapenv:Header/wsse:Security/e:EncryptedKey", nsmanager); //if (verifyToken((XmlElement)EncryptedKeyNodeList[0])) Console.WriteLine("EncryptedKey token is valid"); else Console.WriteLine("EncryptedKey token is invalid"); } } }