【问题标题】:Requesting a SAML 2.0 security token from a WS-Trust STS using WIF 4.5使用 WIF 4.5 从 WS-Trust STS 请求 SAML 2.0 安全令牌
【发布时间】:2019-05-15 18:22:52
【问题描述】:

我可以通过在 RequestSecurityToken 消息中指定 TokenType=SecurityTokenTypes.Saml 来请求 SAML 1.1 令牌。我可以将其转换为 ClaimsPrincipal 并查看声明。

但是,当我想通过将 TokenType 更改为命名空间“http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0”来请求 SAML 2.0 令牌时,我得到一个包含以下内容的异常。

System.Xml.XmlException: '无法从元素中读取 KeyIdentifierClause 带有命名空间的“参考” 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'。 自定义 KeyIdentifierClauses 需要自定义 SecurityTokenSerializers, 示例请参考 SDK。'

我想知道当从 WS-Trust 1.3 安全令牌服务请求时是否支持 SAML 2.0 令牌,如果支持,它是如何请求的? Microsoft 提供的 SecurityTokenTypes 常量类仅包含一个未指定版本的“Saml”,而且很有趣,它引用了 Microsoft 特定的命名空间。

我知道 Microsoft 已经为 SAML 2.0 令牌提供了支持类,例如 Saml2SecurityTokenHandler,但我似乎无法真正从 STS 请求一个。

下面是我的代码。

using System;
using System.IdentityModel.Protocols.WSTrust;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.Xml;

namespace WsTrustActiveSTSClient
{
    internal class Program
    {
        private const string relyingPartyId = "http://localhost/myApp";
        private const string stsEndpoint = "https://localhost:9443/services/wso2carbon-sts";

        private static void Main(string[] args)
        {
            WS2007HttpBinding binding = new WS2007HttpBinding(SecurityMode.TransportWithMessageCredential, false);

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


            EndpointAddress endpoint = new EndpointAddress(stsEndpoint);

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

            factory.Credentials.UserName.UserName = "admin";
            factory.Credentials.UserName.Password = "admin";

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

            RequestSecurityToken rst = new RequestSecurityToken
            {
                RequestType = RequestTypes.Issue,
                KeyType = KeyTypes.Bearer,
                AppliesTo = new EndpointReference(relyingPartyId),              
                Claims =
                {
                    new RequestClaim("http://wso2.org/claims/givenname"),
                    new RequestClaim("http://wso2.org/claims/emailaddress")
                },
                //TokenType = SecurityTokenTypes.Saml
                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);          

            Console.WriteLine("{0}\n{1}\n\n", genericXmlSecurityToken, genericXmlSecurityToken.TokenXml.OuterXml);

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

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

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

            ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(tokenHandlers.ValidateToken(token).First());

            Console.WriteLine("Name : " + claimsPrincipal.Identity.Name);
            Console.WriteLine("Auth Type : " + claimsPrincipal.Identity.AuthenticationType);
            Console.WriteLine("Is Authed : " + claimsPrincipal.Identity.IsAuthenticated);
            foreach (Claim c in claimsPrincipal.Claims)
                Console.WriteLine("{0}:{1}", c.Type, c.Value);

            Console.ReadLine();
        }

        public class TrustedIssuerNameRegistry : IssuerNameRegistry
        {
            public override string GetIssuerName(SecurityToken securityToken)
            {
                return "Trusted Issuer";
                // throw new SecurityTokenException("Untrusted issuer.");
            }
        }
    }
}

当我没有在 RST 中指定 TokenType 时,这是发送到 WS-Trust STS 的 SOAP 消息。

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
   <s:Header>
      <a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>
      <a:MessageID>urn:uuid:bb24c76a-b737-4a9b-8526-26b84a28bbe8</a:MessageID>
      <a:ReplyTo>
         <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </a:ReplyTo>
      <a:To s:mustUnderstand="1">https://localhost:9443/services/wso2carbon-sts</a:To>
      <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
         <u:Timestamp u:Id="_0">
            <u:Created>2017-10-17T14:50:01.517Z</u:Created>
            <u:Expires>2017-10-17T14:55:01.517Z</u:Expires>
         </u:Timestamp>
         <o:UsernameToken u:Id="uuid-b6a803fc-b1fe-4186-8c3e-dcf4b1a647e5-1">
            <o:Username>admin</o:Username>
            <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">admin</o:Password>
         </o:UsernameToken>
      </o:Security>
   </s:Header>
   <s:Body>
      <trust:RequestSecurityToken xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
         <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
            <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
               <wsa:Address>http://localhost/myApp</wsa:Address>
            </wsa:EndpointReference>
         </wsp:AppliesTo>
         <trust:Claims xmlns:i="http://schemas.xmlsoap.org/ws/2005/05/identity" Dialect="http://schemas.xmlsoap.org/ws/2005/05/identity">
            <i:ClaimType Uri="http://wso2.org/claims/givenname" Optional="false"/>
            <i:ClaimType Uri="http://wso2.org/claims/emailaddress" Optional="false"/>
         </trust:Claims>
         <trust:KeyType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
         <trust:RequestType>http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>
      </trust:RequestSecurityToken>
   </s:Body>
</s:Envelope>

这是来自 WSO2 身份服务器的响应。

   <soapenv:Envelope xmlns:soapenv="http://www.w3.org/2003/05/soap-envelope">
      <soapenv:Header xmlns:wsa="http://www.w3.org/2005/08/addressing">
         <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="true">
            <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-1">
               <wsu:Created>2017-10-17T14:50:02.407Z</wsu:Created>
               <wsu:Expires>2017-10-17T14:55:02.407Z</wsu:Expires>
            </wsu:Timestamp>
         </wsse:Security>
         <wsa:Action>http://docs.oasis-open.org/ws-sx/ws-trust/200512/RSTR/Issue</wsa:Action>
         <wsa:RelatesTo>urn:uuid:bb24c76a-b737-4a9b-8526-26b84a28bbe8</wsa:RelatesTo>
      </soapenv:Header>
      <soapenv:Body>
         <wst:RequestSecurityTokenResponseCollection xmlns:wst="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
            <wst:RequestSecurityTokenResponse>
               <wst:TokenType>http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</wst:TokenType>
               <wst:RequestedAttachedReference>
                  <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                     <wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">_d8c5ef71f6665284b3ba5f7aca69f08b</wsse:KeyIdentifier>
                  </wsse:SecurityTokenReference>
               </wst:RequestedAttachedReference>
               <wst:RequestedUnattachedReference>
                  <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
                     <wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">_d8c5ef71f6665284b3ba5f7aca69f08b</wsse:KeyIdentifier>
                  </wsse:SecurityTokenReference>
               </wst:RequestedUnattachedReference>
               <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
                  <wsa:EndpointReference xmlns:wsa="http://www.w3.org/2005/08/addressing">
                     <wsa:Address>http://localhost/myApp</wsa:Address>
                  </wsa:EndpointReference>
               </wsp:AppliesTo>
               <wst:Lifetime>
                  <wsu:Created xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-10-17T14:50:02.236Z</wsu:Created>
                  <wsu:Expires xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">2017-10-17T14:55:02.236Z</wsu:Expires>
               </wst:Lifetime>
               <wst:RequestedSecurityToken>
                  <Assertion xmlns="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:samlp="urn:oasis:names:tc:SAML:1.0:protocol" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" AssertionID="_d8c5ef71f6665284b3ba5f7aca69f08b" IssueInstant="2017-10-17T14:50:02.282Z" Issuer="https://localhost" MajorVersion="1" MinorVersion="1">
                     <Conditions NotBefore="2017-10-17T14:50:02.236Z" NotOnOrAfter="2017-10-17T14:55:02.236Z">
                        <AudienceRestrictionCondition>
                           <Audience>http://localhost/myApp</Audience>
                        </AudienceRestrictionCondition>
                     </Conditions>
                     <AuthenticationStatement AuthenticationInstant="2017-10-17T14:50:02.236Z" AuthenticationMethod="urn:oasis:names:tc:SAML:1.0:am:password">
                        <Subject>
                           <NameIdentifier Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">admin</NameIdentifier>
                           <SubjectConfirmation>
                              <ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</ConfirmationMethod>
                           </SubjectConfirmation>
                        </Subject>
                     </AuthenticationStatement>
                     <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/2000/09/xmldsig#rsa-sha1"/>
                           <ds:Reference URI="#_d8c5ef71f6665284b3ba5f7aca69f08b">
                              <ds:Transforms>
                                 <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
                                 <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                                    <ec:InclusiveNamespaces xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#" PrefixList="code ds kind rw saml samlp typens #default xsd xsi"/>
                                 </ds:Transform>
                              </ds:Transforms>
                              <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                              <ds:DigestValue>8w9YHsb+YIyUCzGjqh6q0JrjxTI=</ds:DigestValue>
                           </ds:Reference>
                        </ds:SignedInfo>
                        <ds:SignatureValue>Evm0H2+hOMWdsrK0Rp8HPCDDldMJ+AHPgv4hrqKW6IuPGFT25DhTRoIc+cPuerFOABYX5B1Om0v4VlmqsalpK2V7tdzHlrDbrOCiENL4FhdATd48o/IiRjde8XM0B7gHAIJoMSimg3Fc/jPXH4kyMsLAWM+l0GdK8VxKVLPtrhY=</ds:SignatureValue>
                        <ds:KeyInfo>
                           <ds:X509Data>
                              <ds:X509Certificate>MIICNTCCAZ6gAwIBAgIES343gjANBgkqhkiG9w0BAQUFADBVMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExFjAUBgNVBAcMDU1vdW50YWluIFZpZXcxDTALBgNVBAoMBFdTTzIxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMDAyMTkwNzAyMjZaFw0zNTAyMTMwNzAyMjZaMFUxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzENMAsGA1UECgwEV1NPMjESMBAGA1UEAwwJbG9jYWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCUp/oV1vWc8/TkQSiAvTousMzOM4asB2iltr2QKozni5aVFu818MpOLZIr8LMnTzWllJvvaA5RAAdpbECb+48FjbBe0hseUdN5HpwvnH/DW8ZccGvk53I6Orq7hLCv1ZHtuOCokghz/ATrhyPq+QktMfXnRS4HrKGJTzxaCcU7OQIDAQABoxIwEDAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADgYEAW5wPR7cr1LAdq+IrR44iQlRG5ITCZXY9hI0PygLP2rHANh+PYfTmxbuOnykNGyhM6FjFLbW2uZHQTY1jMrPprjOrmyK5sjJRO4d1DeGHT/YnIjs9JogRKv4XHECwLtIVdAbIdWHEtVZJyMSktcyysFcvuhPQK8Qc/E/Wq8uHSCo=</ds:X509Certificate>
                           </ds:X509Data>
                        </ds:KeyInfo>
                     </ds:Signature>
                  </Assertion>
               </wst:RequestedSecurityToken>
            </wst:RequestSecurityTokenResponse>
         </wst:RequestSecurityTokenResponseCollection>
      </soapenv:Body>
   </soapenv:Envelope>

如您所见,WSO2 身份服务器返回 SAML 1.0 和 SAML 1.1 命名空间的混合。

【问题讨论】:

  • 我使用 WSO2 Identity Server 5.3.0 作为 WS-Trust STS。

标签: c# web-services wcf wif ws-trust


【解决方案1】:

我从未使用过 WSO 身份服务器,但这段代码在 Thinktecture IdentityServer 上运行良好。我已将端点地址和领域替换为您示例中的信息。

public static SecurityToken GetToken(string userName, string password)
{
    const string stsEndpoint = "https://localhost:9443/services/wso2carbon-sts";
    const string realm = "http://localhost/myApp";

    var factory = new WSTrustChannelFactory(
        new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential),
        stsEndpoint) { TrustVersion = TrustVersion.WSTrust13 };

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

    var rst = new RequestSecurityToken
    {
        AppliesTo = new EndpointReference(realm),
        RequestType = RequestTypes.Issue,
        KeyType = KeyTypes.Bearer
    };

    var channel = factory.CreateChannel();
    var token = channel.Issue(rst);
    return token;
}

【讨论】:

  • 只是看一下我认为它也可以与 WSO2 身份服务器一起使用,唯一的问题是它没有在 RequestSecurityToken 元素中指定 TokenType。我假设这让 STS 返回其默认令牌类型。
  • 您是否在 Thinktecture Identity Server 的响应中返回了 SAML 2.0 令牌?
  • @Jeffrey 是很久以前的事了,但我认为它总是 SAML 2.0 断言。正如您所说,IdentityServer 默认设置为发出 SAML 2.0 令牌。
  • 明白了。这很有意义。我看到支持 SAML 2.0 工件,因为我在库中看到了它们。这就是您在与 WS-Trust 端点通信时具体请求 SAML 2.0 的方式。每次我尝试 WIF 4.5 时,SOAP XML 都会出现问题。感谢您的回复!
  • 只是在这里扔一个弯曲的球。 WIF 是 WS-Fed,仅支持 SAML 1.1 令牌。
【解决方案2】:

您可以从 WS-Trust 1.3 STS 获取 SAML2.0 令牌,但请记住:

  1. 由 STS 来满足您的请求。例如,无论请求什么,它都可能始终生成特定类型的令牌。
  2. 并非所有 STS 都可以生成 SAML2.0 令牌。我知道 WIF 4.5 允许这样做,但是您的 STS 使用它吗?我不确定旧版本 (WIF 1.0/3.5) 是否可以生成 SAML2.0 令牌。

假设您的特定 STS 确实支持这些请求并且可以生成 SAML2.0 令牌,那么试试这个:

TokenType = "urn:oasis:names:tc:SAML:2.0:assertion"

或者,如果它仍然不起作用,也许:

AuthenticationType = "urn:oasis:names:tc:SAML:2.0:assertion"

【讨论】:

    【解决方案3】:

    我偶尔发现将 RST 的 tokenType 设置为 saml2.0 URL 会使 STS 返回 SAML 2.0 断言。最有趣的是,微软没有记录这一点。

    试试这样的:

            ServicePointManager.ServerCertificateValidationCallback =
                ((sender, certificate, chain, sslPolicyErrors) => true);
            var rst = new RequestSecurityToken(RequestTypes.Issue);
            rst.AppliesTo = new EndpointReference("https://RelyingParty/*");
            rst.KeyType = KeyTypes.Bearer;
            rst.TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
    
            using (var trustChannelFactory = new WSTrustChannelFactory("WS2007HttpBinding_IWSTrust13_Saml20Sync"))
            {
                trustChannelFactory.Credentials.UserName.UserName = userName;
                trustChannelFactory.Credentials.UserName.Password = userPassword;
    
                var channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
                try
                {
                    _authToken = channel.Issue(rst);
                }
                catch (MessageSecurityException ex)
                {
                    channel.Abort();
                    throw new SecurityTokenException(ex.InnerException.Message, ex);
                }
                catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }
                UserIdenity = CreateUserIdentityFromSecurityToken(_authToken);
            }
    

    【讨论】:

      猜你喜欢
      • 2012-05-28
      • 1970-01-01
      • 2012-11-10
      • 1970-01-01
      • 2018-05-13
      • 1970-01-01
      • 1970-01-01
      • 2012-04-05
      • 2017-10-05
      相关资源
      最近更新 更多