Friday, July 15, 2016

Authenticating with Web Services written in a different language

Ah, the great promise of web services! We'll all speak the same language, XML/SOAP/JSON! Yeah, not so much. My latest interaction with this was a service that wanted a WS-Security authentication header but didn't want all the fields that Microsoft uses by default; without laying blame my understanding is some Java-based services don't accept a timestamp and this was my experience. Here's how I solved the problem:

WCF wasn’t really an option; because one of the functions had a callback it wanted to do duplex communication which among other things doesn’t support SSL. So here’s what I did:
  1.  Added WSE3 to the project (Microsoft.Web.Services3.dll)

  2. Create an old-school Web Reference in .NET (advanced option in “Add Service Reference”)

  3. Changed the resulting Referencs.cs class from System.Web.Services.Protocols.SoapHttpClientProtocol to Microsoft.Web.Services3.WebServicesClientProtocol

  4. Created a policy to allow me to filter the SOAP

  5. public class ClientUserNamePolicyAssertion : SecurityPolicyAssertion
        {
            public string UserName { get; set; }
            public string Password { get; set; }
           
            public ClientUserNamePolicyAssertion(string userName, string password)
            {
                this.UserName = userName;
                this.Password = password;
            }

            // and let the assertion know you have an output filter for outgoing SOAP packets
            public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
            {
                return new ClientSendSecurityFilter(this);
            }

            public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
            {
                throw new NotImplementedException();
            }

            public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
            {
                throw new NotImplementedException();
            }

            public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
            {
                return null;
            }
        } 

  6. Created the filter – here is where I remove the timestamp header, right before the packet goes out

  7. public class ClientSendSecurityFilter : SendSecurityFilter
        {
            private ClientUserNamePolicyAssertion clientUserNamePolicyAssertion = null;

            public ClientSendSecurityFilter(ClientUserNamePolicyAssertion userNamePolicyAssertion)
                : base(userNamePolicyAssertion.ServiceActor, true)
            {
                this.clientUserNamePolicyAssertion = userNamePolicyAssertion;
            }

            public override void SecureMessage(SoapEnvelope soapEnvelope, Security security)
            {
                UsernameToken usernameToken = new UsernameToken(clientUserNamePolicyAssertion.UserName, clientUserNamePolicyAssertion.Password, PasswordOption.SendPlainText);
                security.Tokens.Add(usernameToken);  
            }

            public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
            {
                // go ahead and run the base so the headers and security are added
                SoapFilterResult res = base.ProcessMessage(envelope);

                // remove the timestamp from the security header
                foreach (XmlNode n in envelope.Header.ChildNodes) {
                    if (n.Name == "wsse:Security")
                    {
                        foreach (XmlNode p in n)
                        {
                            if (p.Name == "wsu:Timestamp") n.RemoveChild(p);
                        }
                    }
                }

                //Debug.Write(envelope.OuterXml);

                return res;
            }
        }


  8. Then in the main I can create the service, apply my policy and make calls

TheirJavaService svc = new TheirJavaService();
ClientUserNamePolicyAssertion assert = new ClientUserNamePolicyAssertion("myusername", "mypassword");
Policy policy = new Policy();
policy.Assertions.Add(assert);
svc.SetPolicy(policy);
TheirServiceRequest req = new TheirServiceRequest();
TheirServiceResponse res = svc.GetResponseFromTheirService(req);

No comments:

Post a Comment