WCF - create UserNameToken with timestamp and password digest for oasis-200401-wss-username-token-profile-1.0

别来无恙 提交于 2019-12-23 17:31:13

问题


The objective is, by using WCF, connect to a Web service that requires UserNameToken ws-security as specified by the standard "oasis-200401-wss-soap-message-security-1.0".

Full specification can be find here http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0.pdf

There are few examples of how to create the token and none worked for me. When i posted this i had already found the answer but since it was not easy, i let here the solution i found and hope it can be improved. So i will just jump to the answer.


回答1:


using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.ServiceModel.Channels;

using System.Text;
using System.Threading.Tasks;

namespace WSClient
{

    //Create this custom credentials class to implement UserNameToken autentication
    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.WSSecurity10);
        }
    }

    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;

            string tokennamespace = "o";

            DateTime created = DateTime.UtcNow;
            string createdStr = created.ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); 

            string phrase = Guid.NewGuid().ToString();
            var nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(phrase));                                   
            SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();

             //Password_Digest = Base64 ( SHA-1 ( nonce + created + password ) )
            string Password_Digest = Convert.ToBase64String(sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase + createdStr + userToken.Password))); // pxLqPLCXU1EiUS+NnpRuCw==
            var stringToWrite = 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#PasswordDigest\">" +
                Password_Digest + "</{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);

            writer.WriteRaw(stringToWrite);
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            try
            {
            //for invalid ssl server certificate
                System.Net.ServicePointManager.ServerCertificateValidationCallback +=
(se, cert, chain, sslerror) =>
{
    return true;
};

                CustomBinding binding = new CustomBinding();

                var security = TransportSecurityBindingElement.CreateUserNameOverTransportBindingElement();
                security.IncludeTimestamp = true;
                security.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256;
                security.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
                security.EnableUnsecuredResponse = true;

                var encoding = new TextMessageEncodingBindingElement();
                encoding.MessageVersion = MessageVersion.Soap11;
                var transport = new HttpsTransportBindingElement();
                transport.MaxReceivedMessageSize = 2000000; 

                binding.Elements.Add(security);
                binding.Elements.Add(encoding);
                binding.Elements.Add(transport);

                WSClient.Proxy.TargetWS client = new Proxy.TargetWS(binding,
                    new EndpointAddress(Properties.Settings.Default.Url));

                //change credential for the custom credentials instance
                client.ChannelFactory.Endpoint.Behaviors.Remove<System.ServiceModel.Description.ClientCredentials>();
                client.ChannelFactory.Endpoint.Behaviors.Add(new CustomCredentials());

                client.ClientCredentials.UserName.UserName = Properties.Settings.Default.username;
                client.ClientCredentials.UserName.Password = Properties.Settings.Default.password;


                Proxy.Message message = new WSClient.Proxy.Message();
                message.id = "whatever";
                client.foo(message);

                System.Console.Write("Success");
            }
            catch (Exception ex)
            {
                System.Console.Write(ex.Message);
            }

        }
    }
}

a bit extensive!

Essentially, it is need to serialize the token according to Oasys specifications. To do this it is need to derivate ClientCredentials, ClientCredentialsSecurityTokenManager and WSSecurityTokenSerializer in the way presented.

About the binding, not sure if works for all or if variations are allowed by the specification.



来源:https://stackoverflow.com/questions/25776403/wcf-create-usernametoken-with-timestamp-and-password-digest-for-oasis-200401-w

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!