我最终使用自定义 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;
}
}