Исключение SecurityNegotiationException — извлечение токена из веб-службы

#c# #web-services #adfs #wif

#c# #веб-службы #adfs #с помощью

Вопрос:

Я пытаюсь перейти с WIF 3.5 на WIF 4.5. Однако преобразование оказывается более сложным, чем я ожидал. Вопросы будут соответствовать комментариям в коде.

Полное сообщение об ошибке:

System.Web.Services.Протоколы.Исключение SoapException: ‘System.Web.Services.Протоколы.Исключение SoapException: ошибка аутентификации —>

Система.ServiceModel.Безопасность.Исключение SecurityNegotiationException: не удается открыть защищенный канал, поскольку не удалось согласовать безопасность с удаленной конечной точкой. Это может быть связано с отсутствием или неправильно указанным EndpointIdentity в EndpointAddress, используемом для создания канала. Пожалуйста, убедитесь, что EndpointIdentity, указанный или подразумеваемый EndpointAddress, правильно идентифицирует удаленную конечную точку.

Не удается открыть защищенный канал, поскольку не удалось согласовать безопасность с удаленной конечной точкой. Это может быть связано с отсутствием или неправильно указанным EndpointIdentity в EndpointAddress, используемом для создания канала. Пожалуйста, убедитесь, что EndpointIdentity, указанный или подразумеваемый EndpointAddress, правильно идентифицирует удаленную конечную точку.

# 1. Какая комбинация имени пользователя и пароля необходима, а какая нет?

#2. Именно здесь возникает исключение SecurityNegotiationException. Чего именно мне не хватает?

Итак, я ошибаюсь или мне не хватает чего-то простого? Нужно ли мне полностью переписывать, как создается WSTrustChannelFactory ?

Код:

 public string GetToken(string url, string domain, string realm, string username, string password)
{
    string rp = realm;
    string token = "";

    WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory
    (
        new WSHttpBinding(SecurityMode.TransportWithMessageCredential),
        new EndpointAddress(new Uri(url))
    );
        
    trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
    trustChannelFactory.Credentials.Windows.ClientCredential.Domain = domain;
    trustChannelFactory.Credentials.Windows.ClientCredential.UserName = username; // #1; not sure which pair is needed?
    trustChannelFactory.Credentials.Windows.ClientCredential.Password = password;

    trustChannelFactory.Credentials.UserName.Password = password;
    trustChannelFactory.Credentials.UserName.UserName = username;

    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

    try
    {
        RequestSecurityToken rst = new RequestSecurityToken(RequestTypes.Issue, KeyTypes.Bearer);
        rst.AppliesTo = new EndpointReference(rp);
        rst.TokenType = SecurityTokenTypes.Saml;

        WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
        GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken; // #2; Exception thrown here
        token = token.TokenXml.OuterXml;
    }
    catch (SecurityNegotiationException e)
    {
        LogError("Authentication Failed", e);
    }
    catch (TimeoutException e)
    {
        LogError("Unable to authenticate", e);
    }
    catch (CommunicationException e)
    {
        LogError("Communication exception", e);
    }
    catch (Exception e)
    {
        LogError("Unknown exception", e);
    }
    return token;
}
  

Ответ №1:

Вам необходимо использовать SecurityTokenHandlerCollection

         public SecurityToken GetToken(string url, string realm, string username, string password)
        {
            string rp = realm;
        
            WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential, false);

            binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
            binding.Security.Message.EstablishSecurityContext = false;

            EndpointAddress endpoint = new EndpointAddress(url);

            WSTrustChannelFactory factory = new WSTrustChannelFactory(binding, endpoint);
            factory.TrustVersion = TrustVersion.WSTrust13;

            factory.Credentials.UserName.UserName = username;
            factory.Credentials.UserName.Password = password;

            WSTrustChannel channel = (WSTrustChannel) factory.CreateChannel();

            RequestSecurityToken rst = new RequestSecurityToken
            {
                RequestType = RequestTypes.Issue,
                KeyType = KeyTypes.Bearer,
                AppliesTo = new EndpointReference(rp),              
                TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0"
            };

            GenericXmlSecurityToken genericXmlSecurityToken = (GenericXmlSecurityToken) channel.Issue(rst, out RequestSecurityTokenResponse rstr);          

            SecurityTokenHandlerCollection tokenHandlers = new SecurityTokenHandlerCollection(
                new SecurityTokenHandler[]
                {
                    new SamlSecurityTokenHandler(), 
                    new Saml2SecurityTokenHandler()
                }
            );
            tokenHandlers.Configuration.AudienceRestriction = new AudienceRestriction();
            tokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(rp));

            TrustedIssuerNameRegistry trustedIssuerNameRegistry = new TrustedIssuerNameRegistry();
            tokenHandlers.Configuration.IssuerNameRegistry = trustedIssuerNameRegistry;

            SecurityToken token =
                tokenHandlers.ReadToken(
                    new XmlTextReader(new StringReader(genericXmlSecurityToken.TokenXml.OuterXml)));

            return token;
        }

        public class TrustedIssuerNameRegistry : IssuerNameRegistry
        {
            public override string GetIssuerName(SecurityToken securityToken)
            {
                return "Trusted Issuer";
            }
        }
  

Ответ №2:

Мы решили пока продолжать использовать WIF 3.5 и полностью перепишем для WIF 4.5 вместо того, чтобы пытаться сделать что-то невозможное.

Было просто слишком много изменений и недостаточно документации, чтобы «включить» наш существующий код с версии WIF 3.4 на версию WIF 4.5