none
Problem Creating Custom Binding with both Binary Security Token (Sha256RSA) and UserNameToken (include created timestamp and nonce) RRS feed

  • Question

  • Hi, 

    We have a 3rd party webservice to consume and it requires us to sign the soap message with both binary security token and user name token in the following format:

    <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509PKIPathv1" wsu:Id="X509-e47ac957-8209-44f4-b60d-d1039b8e84c1">MIIDSDCCAjCgAw...</wsse:BinarySecurityToken>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
    <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
    <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
    <ds:Reference URI=""><ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms>
    <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
    <ds:DigestValue>j9/X7CW...</ds:DigestValue></ds:Reference>
    <ds:Reference URI="">
    <ds:Transforms><ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" /></ds:Transforms>
    <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" /><ds:DigestValue>j9/X7CWz1...</ds:DigestValue></ds:Reference></ds:SignedInfo>
    <ds:SignatureValue>WvAU6hYaj10xI...</ds:SignatureValue>
    <ds:KeyInfo><wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" /></ds:KeyInfo></ds:Signature>
    <wsse:UsernameToken wsu:Id="UsernameToken-NTlmNzM...">
    <wsse:Username>username</wsse:Username>
    <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
    <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">ZmJjY2VhOTktZ...</wsse:Nonce>
    <wsu:Created>2018-05-10T17:21:14+08:00</wsu:Created>
    </wsse:UsernameToken>
    </wsse:Security>
    </soapenv:Header>

    We have tried to create custom binding like following, but realized that the usernametoken does not include 'created' and 'nonce'.

       private static System.ServiceModel.Channels.Binding GetCustomBinding()
            {
                AsymmetricSecurityBindingElement asbe = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement
                    (MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
     
                asbe.InitiatorTokenParameters = new X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
                asbe.RecipientTokenParameters = new X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
    
                asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
    
    
                asbe.EnableUnsecuredResponse = true;
                asbe.IncludeTimestamp = false;
             
                asbe.SetKeyDerivation(false);
                asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic256Sha256Rsa15;
                asbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
    
                asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
                asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
    
                CustomBinding myBinding = new CustomBinding();
                myBinding.Elements.Add(asbe);
    
                myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
    
                HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
                httpsBindingElement.RequireClientCertificate = true;
                
             
               
                myBinding.Elements.Add(httpsBindingElement);
    
                return new CustomBinding( myBinding);
            }
    

    Therefore we added a cutomcredential as per suggested from some web references. 

     public class CustomCredentials : ClientCredentials
        {
            public CustomCredentials()
            { }
    
            protected CustomCredentials(CustomCredentials cc)
                : base(cc)
            { }
    
            public override System.IdentityModel.Selectors.SecurityTokenManager CreateSecurityTokenManager()
            {
                return new CustomSecurityTokenManager(this);
            }
    
            protected override ClientCredentials CloneCore()
            {
                return new CustomCredentials(this);
            }
        }
    
        public class CustomSecurityTokenManager : ClientCredentialsSecurityTokenManager
        {
            public CustomSecurityTokenManager(CustomCredentials cred)
                : base(cred)
            { }
    
            public override System.IdentityModel.Selectors.SecurityTokenSerializer CreateSecurityTokenSerializer(System.IdentityModel.Selectors.SecurityTokenVersion version)
            {
                return new CustomTokenSerializer(System.ServiceModel.Security.SecurityVersion.WSSecurity11);
            }
        }
    
        public class CustomTokenSerializer : WSSecurityTokenSerializer
        {
            public CustomTokenSerializer(SecurityVersion sv)
                : base(sv)
            { }
    
            protected override void WriteTokenCore(System.Xml.XmlWriter writer,
                                                    System.IdentityModel.Tokens.SecurityToken token)
            {
              
                UserNameSecurityToken userToken = token as UserNameSecurityToken;
                if (userToken == null)
                    return; 
                string tokennamespace = "o";
    
                DateTime created = DateTime.Now;
                string createdStr = created.ToString("yyyy-MM-ddThh:mm:ss.fffZ");
    
                // unique Nonce value - encode with SHA-1 for 'randomness'
                // in theory the nonce could just be the GUID by itself
                string phrase = Guid.NewGuid().ToString();
                UTF8Encoding encoder = new UTF8Encoding();
                var nonce = Convert.ToBase64String(encoder.GetBytes(phrase));
    
                // in this case password is plain text
                // for digest mode password needs to be encoded as:
                // PasswordAsDigest = Base64(SHA-1(Nonce + Created + Password))
                // and profile needs to change to
                //string password = GetSHA1String(nonce + createdStr + userToken.Password);
               
                string password = userToken.Password;
    
    
    
                writer.WriteRaw(string.Format(
                "<{0}:UsernameToken u:Id=\"" + token.Id +
                "\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
                "<{0}:Username>" + userToken.UserName + "</{0}:Username>" +
                "<{0}:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" +
                password + "</{0}:Password>" +
                "<{0}:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" +
                nonce + "</{0}:Nonce>" +
                "<u:Created>" + createdStr + "</u:Created></{0}:UsernameToken>", tokennamespace));
    
            }

    Then add the customcredential to endpoint behavior:

    client.ChannelFactory.Endpoint.Behaviors.Remove<System.ServiceModel.Description.ClientCredentials>();
                client.Endpoint.EndpointBehaviors.Add(new CustomCredentials());


    However, when the service was called and before sending, it hits runtime error "Text cannot be written outside the root element." at the point when the CustomTokenSerializer class tries to overwrite the WriteTokenCore with WriteRaw. 

    writer.WriteRaw(string.Format(
                "<{0}:UsernameToken u:Id=\"" + token.Id +
                "\" xmlns:u=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd\">" +
                "<{0}:Username>" + userToken.UserName + "</{0}:Username>" +
                "<{0}:Password Type=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText\">" +
                password + "</{0}:Password>" +
                "<{0}:Nonce EncodingType=\"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary\">" +
                nonce + "</{0}:Nonce>" +
                "<u:Created>" + createdStr + "</u:Created></{0}:UsernameToken>", tokennamespace));

    The error can be dismissed if we uses TransportSecurityBindingElement instead of AsymmetricSecurityBindingElement, however, the x509securitytoken would be gone. 

    Can anyone help to see how we can include both binarysecuritytoken and usernametoken(with created timestamp and nonce)?

    Thank you very much!

    Shawlyx

    Thursday, May 24, 2018 9:53 AM

All replies