联合安全 - 单独的SSL和RP证书(.NET 4.5和WIF)

时间:2014-07-02 16:11:53

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

我目前正在研究使用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证书和加密证书,或者它们总是必须相同吗?

重新迭代令牌的流程如下:

sts *(加密)* &gt; 客户*(已加密)* &gt; dmz-broker *(需要解密)* &gt; internal-server *(需要解密)*

我尝试将服务证书更改为加密证书,但之后将其用于SSL并失败。我还试图设置端点的标识,指定证书和DNS值,所有这些都没有任何好运。

提前感谢您的帮助。

2 个答案:

答案 0 :(得分:3)

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

我们可以在.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;
    }
}

答案 1 :(得分:0)

可以在不创建自定义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