我目前正在研究一个使用STS、客户端和客户端使用的WCF服务的解决方案。目前,这一切都是通过配置完成的,客户端成功检索令牌并将其传递给WCF服务。
证书出现问题,我们使用的是网络。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)证书是不可接受的。
是否有办法为网络指定单独的SSL证书和加密证书。TCP绑定还是它们必须总是相同的?
只是重复一下令牌的流程如下:
sts*(加密)*> client*(加密)*> dmz-broker*(需要解密)*> internal-server*(需要解密)*
我已经尝试将服务证书更改为加密证书,但随后它将其用于SSL并且失败。我还尝试设置指定证书和DNS值的端点的身份,但都没有任何运气。
提前感谢您的帮助。
我最终使用自定义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;
}
}
无需创建自定义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://learn.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