【问题标题】:Federated Security - Separate SSL and RP certificates (.NET 4.5 & WIF)联合安全性 - 单独的 SSL 和 RP 证书(.NET 4.5 和 WIF)
【发布时间】:2014-08-23 12:54:32
【问题描述】:

我目前正在开发一个使用 STS、客户端和客户端使用的 WCF 服务的解决方案。目前,这一切都是通过配置完成的,客户端成功检索令牌并将其传递给 WCF 服务。

问题出现在证书上,我们使用的是 net.tcp 绑定,使用传输安全性和安全令牌进行保护,作为要求,我们需要 SSL 证书。这个证书配置如下(我已经去掉了无关的xml):

<behavior name="Federated">
  <serviceAuthorization principalPermissionMode="Always" />
  <serviceCredentials  useIdentityConfiguration="true">
    <serviceCertificate findValue="CN=SSLCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName" />
  </serviceCredentials>
</behavior>

问题是这里指定的服务证书也是 WIF 用来解密它收到的令牌的证书,因为在这种情况下,依赖方分布在多台机器上,令牌在它们之间传递,这是不可接受的使用 SSL 证书作为加密 (RP) 证书。

如果有办法为 net.tcp 绑定指定单独的 SSL 证书和加密证书,还是它们必须始终相同?

只是重新迭代一下token的流程如下:

sts*(加密)* > 客户端*(加密)* > dmz-broker*(需要解密)* > internal-服务器*(需要解密)*

我尝试将服务证书更改为加密证书,但随后将其用于 SSL 并失败。我还尝试设置指定证书和 DNS 值的端点的身份,但都没有任何运气。

提前感谢您的帮助。

【问题讨论】:

    标签: .net wcf wif net.tcp sts-securitytokenservice


    【解决方案1】:

    我最终使用自定义 SecurityToken 解析器解决了这个问题。这涉及复制作为标准 .NET 类 (http://referencesource.microsoft.com/#System.IdentityModel/System/IdentityModel/Selectors/SecurityTokenResolver.cs) 的 SimpleTokenResolver,然后通过与用于解密令牌的证书相关的安全令牌来创建它。

    我们可以在 .NET 4.5 源代码中看到,在初始化 WIF 时,会创建一个令牌解析器,并将服务证书作为令牌传入:

     SecurityTokenResolver serviceCertificateResolver = SecurityTokenResolver.CreateDefaultSecurityTokenResolver(new ReadOnlyCollection<SecurityToken>(
                                                      new SecurityToken[] { new X509SecurityToken(this.ServiceCertificate) }), false);
    

    这意味着框架默认创建一个解析器,该解析器使用您为 SSL 指定的完全相同的证书进行解密。

    不幸的是,CreateDefaultSecurityTokenResolver 方法内部使用的 SimpleTokenResolver 是私有的,不能继承或覆盖,但是通过从上面的链接获取代码并在构造函数中传入正确的证书(可以从应用程序中读取)设置)您可以添加自己的解析器。

    public CustomSecurityTokenResolver()
                : this(new ReadOnlyCollection<SecurityToken>(new SecurityToken[] { new X509SecurityToken(CertificateHelper.GetFromAppSetting("EncryptionCertificate")) }), false)
    {
    
    }
    

    然后可以在配置中指定此令牌解析器,如下所示:

    <system.identityModel>
      <identityConfiguration>
        <securityTokenHandlers>
          <securityTokenHandlerConfiguration>
            <serviceTokenResolver type="MySecurity.CustomSecurityTokenResolver, MySecurity">
            </serviceTokenResolver>
          </securityTokenHandlerConfiguration>
        </securityTokenHandlers>
      </identityConfiguration>
    </system.identityModel>
    

    请注意,其他解析器仍会添加到安全令牌解析器集合中,并且会在框架创建默认值之后命中该解析器。

    整个自定义解析器的代码如下所示:

    public class CustomSecurityTokenResolver: SecurityTokenResolver
    {
        ReadOnlyCollection<SecurityToken> tokens;
            bool canMatchLocalId;
    
    
        public CustomSecurityTokenResolver()
                : this(new ReadOnlyCollection<SecurityToken>(new SecurityToken[] { new X509SecurityToken(CertificateHelper.GetFromAppSetting("EncryptionCertificate")) }), false)
        {
    
        }
    
        public CustomSecurityTokenResolver(ReadOnlyCollection<SecurityToken> tokens, bool canMatchLocalId)
        {
            this.tokens = tokens;
            this.canMatchLocalId = canMatchLocalId;
        }
    
        protected override bool TryResolveSecurityKeyCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityKey key)
        {
    
    
            key = null;
            for (int i = 0; i < this.tokens.Count; ++i)
            {
                SecurityKey securityKey = this.tokens[i].ResolveKeyIdentifierClause(keyIdentifierClause);
                if (securityKey != null)
                {
                    key = securityKey;
                    return true;
                }
            }
    
            if (keyIdentifierClause is EncryptedKeyIdentifierClause)
            {
                EncryptedKeyIdentifierClause keyClause = (EncryptedKeyIdentifierClause)keyIdentifierClause;
                SecurityKeyIdentifier keyIdentifier = keyClause.EncryptingKeyIdentifier;
                if (keyIdentifier != null && keyIdentifier.Count > 0)
                {
                    for (int i = 0; i < keyIdentifier.Count; i++)
                    {
                        SecurityKey unwrappingSecurityKey = null;
                        if (TryResolveSecurityKey(keyIdentifier[i], out unwrappingSecurityKey))
                        {
                            byte[] wrappedKey = keyClause.GetEncryptedKey();
                            string wrappingAlgorithm = keyClause.EncryptionMethod;
                            byte[] unwrappedKey = unwrappingSecurityKey.DecryptKey(wrappingAlgorithm, wrappedKey);
                            key = new InMemorySymmetricSecurityKey(unwrappedKey, false);
                            return true;
                        }
                    }
                }
            }
    
            return key != null;
        }
    
        protected override bool TryResolveTokenCore(SecurityKeyIdentifier keyIdentifier, out SecurityToken token)
        {
            token = null;
            for (int i = 0; i < keyIdentifier.Count; ++i)
            {
    
                SecurityToken securityToken = ResolveSecurityToken(keyIdentifier[i]);
                if (securityToken != null)
                {
                    token = securityToken;
                    break;
                }
            }
    
                return (token != null);
        }
    
        protected override bool TryResolveTokenCore(SecurityKeyIdentifierClause keyIdentifierClause, out SecurityToken token)
        {
    
            token = null;
    
            SecurityToken securityToken = ResolveSecurityToken(keyIdentifierClause);
            if (securityToken != null)
                token = securityToken;
    
            return (token != null);
        }
    
        SecurityToken ResolveSecurityToken(SecurityKeyIdentifierClause keyIdentifierClause)
        {
    
            if (!this.canMatchLocalId && keyIdentifierClause is LocalIdKeyIdentifierClause)
                return null;
    
            for (int i = 0; i < this.tokens.Count; ++i)
            {
                if (this.tokens[i].MatchesKeyIdentifierClause(keyIdentifierClause))
                    return this.tokens[i];
            }
    
            return null;
        }
    }
    

    【讨论】:

    【解决方案2】:

    无需创建自定义 ServiceTokenResolver 即可解决此问题。

    .NET 4.5+:使用System.IdentityModel.ServiceConfiguration

    public class Service1 : IService1
    {
        public static void Configure(ServiceConfiguration config)
        {
            config.IdentityConfiguration.SecurityTokenHandlers.Configuration.ServiceTokenResolver =
              SecurityTokenResolver.CreateDefaultSecurityTokenResolver(new ReadOnlyCollection<SecurityToken>(
                  new SecurityToken[]
                  {
                      new X509SecurityToken(Util.GetEncryptionCert())
                  }), false);
        }
    }
    

    https://docs.microsoft.com/en-us/dotnet/framework/wcf/configuring-wcf-services-in-code

    4.5 之前:使用Microsoft.IdentityModel.Configuration.ServiceConfiguration

    using (ServiceHost host = new ServiceHost(typeof(HelloWorldService), baseAddress))
    {
        var config = new Microsoft.IdentityModel.Configuration.ServiceConfiguration();
    
        config.SecurityTokenHandlers.Configuration.ServiceTokenResolver =
            SecurityTokenResolver.CreateDefaultSecurityTokenResolver(new ReadOnlyCollection<SecurityToken>(
            new SecurityToken[]
            {
                new X509SecurityToken(Util.GetEncryptionCert())
            }), false);
    
        FederatedServiceCredentials.ConfigureServiceHost(serviceHost, config);
    
        host.Open();
    
        // Close the ServiceHost.
        host.Close();
    }
    

    https://msdn.microsoft.com/en-us/library/microsoft.identitymodel.tokens.federatedservicecredentials.configureservicehost.aspx

    【讨论】:

      猜你喜欢
      • 2012-04-11
      • 2016-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-10
      • 2018-02-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多