在 WCF Web 服务的 SOAP 消息中,如何将 KeyIdentifier 直接放在 SecurityTokenReference 中(内联,不使用引用令牌)

In SOAP message to WCF web service, how to put KeyIdentifier directly inside SecurityTokenReference (inline, without using Reference token)

我成功地通过 SoapUI 与 WCF 服务通信(我得到了关于如何配置它的规范),但我在将这些设置复制到 .NET 应用程序时遇到了问题。原来生成的 SOAP 消息的形状(通过 Fiddler 窥视)被 Web 服务拒绝,它期望更严格的信封布局。

我很亲近。在这张照片上...

...您可以看到三个 SOAP 消息:

1. X509SecurityTokenParameters.InclusionMode 设置为 AlwaysToRecipient

2. X509SecurityTokenParameters.InclusionMode 设置为 Never

3. 预期的安全令牌,已在 SoapUI 上测试。

如何使用 C# 代码实现第 3 点的信封? 我没有使用 app.config 文件,整个配置都在 C# 代码中(但我没有致力于保持这种状态,它只是发生了)。当前代码:

using System;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using System.ServiceModel.Security.Tokens;
using System.Text;



public class CustomAlgorithmSuite : SecurityAlgorithmSuite
{
    public override string DefaultAsymmetricKeyWrapAlgorithm    { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
    public override string DefaultAsymmetricSignatureAlgorithm  { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
    public override string DefaultCanonicalizationAlgorithm     { get { return "http://www.w3.org/2001/10/xml-exc-c14n#"; }}
    public override string DefaultDigestAlgorithm               { get { return "http://www.w3.org/2000/09/xmldsig#sha1"; }}
    public override string DefaultEncryptionAlgorithm           { get { return "http://www.w3.org/2001/04/xmlenc#aes256-cbc"; }}
    public override int    DefaultEncryptionKeyDerivationLength { get { return SecurityAlgorithmSuite.Default.DefaultEncryptionKeyDerivationLength; }}
    public override int    DefaultSignatureKeyDerivationLength  { get { return SecurityAlgorithmSuite.Default.DefaultSignatureKeyDerivationLength; }}
    public override int    DefaultSymmetricKeyLength            { get { return SecurityAlgorithmSuite.Default.DefaultSymmetricKeyLength; }}
    public override string DefaultSymmetricKeyWrapAlgorithm     { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
    public override string DefaultSymmetricSignatureAlgorithm   { get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }}
    public override bool   IsAsymmetricKeyLengthSupported(int length) { return true; }
    public override bool   IsSymmetricKeyLengthSupported(int length)  { return true; }
}



class Program
{
    static void Main()
    {
        X509SecurityTokenParameters x509Params = new X509SecurityTokenParameters()
        {
            X509ReferenceStyle = X509KeyIdentifierClauseType.RawDataKeyIdentifier,
            InclusionMode      = SecurityTokenInclusionMode.AlwaysToRecipient,
            ReferenceStyle     = SecurityTokenReferenceStyle.External,
            RequireDerivedKeys = false
        };

        SecurityBindingElement security = new TransportSecurityBindingElement()
        {
            MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10,
            DefaultAlgorithmSuite  = new CustomAlgorithmSuite()
        };
        security.EndpointSupportingTokenParameters.Endorsing.Add(x509Params);
        security.SetKeyDerivation(false);
        //security.IncludeTimestamp = false;

        TextMessageEncodingBindingElement encoding = new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8);
        HttpsTransportBindingElement transport = new HttpsTransportBindingElement();
        //transport.RequireClientCertificate = true;
        CustomBinding customBinding = new CustomBinding(security, encoding, transport);

        ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;

        var twoCertificatesInOneFile = new X509Certificate2Collection();
        twoCertificatesInOneFile.Import("foo path", "foo cert pass", X509KeyStorageFlags.Exportable);
        someGeneratedServiceClass client = new someGeneratedServiceClass(customBinding, new EndpointAddress(new Uri("foo webservice address"), EndpointIdentity.CreateDnsIdentity(twoCertificatesInOneFile[0].FriendlyName)));
        client.ClientCredentials.ServiceCertificate.DefaultCertificate = twoCertificatesInOneFile[0];
        client.ClientCredentials.ClientCertificate.Certificate = twoCertificatesInOneFile[1];
        //client.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
        client.ClientCredentials.UserName.UserName = "foo user";
        client.ClientCredentials.UserName.Password = "foo pass";

        client.someServiceCall("foo", "foo", false, out i1, out i2);
    }
}

我最终使用了 InclusionMode = SecurityTokenInclusionMode.Never,然后劫持了邮件并手动替换了不正确的标签。

public class CustomProxy_portClient : GeneratedProxy_portClient
{
    public CustomProxy_portClient() : base()
    {
        Endpoint.Behaviors.Remove(typeof(ClientCredentials));
        Endpoint.Behaviors.Add(new CustomClientCredentials());
    }
}



class CustomClientCredentials : ClientCredentials
{
    public CustomClientCredentials() : base() { }
    public CustomClientCredentials(ClientCredentials ClientCredentials) : base(ClientCredentials) { }

    public override SecurityTokenManager CreateSecurityTokenManager()
    {
        return new CustomSecurityTokenManager(this);
    }

    protected override ClientCredentials CloneCore()
    {
        return new CustomClientCredentials(this);
    }
}



class CustomSecurityTokenManager : ClientCredentialsSecurityTokenManager
{
    public CustomSecurityTokenManager(ClientCredentials clientCredentials) : base(clientCredentials) { }

    public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
    {
        return new CustomWSSecurityTokenSerializer();
    }
}



class CustomWSSecurityTokenSerializer : WSSecurityTokenSerializer
{
    protected override void WriteKeyIdentifierClauseCore(XmlWriter writer, SecurityKeyIdentifierClause keyIdentifierClause)
    {
        string xml;
        using(MemoryStream ms = new MemoryStream())
        {
            XmlTextWriter tempWriter = new XmlTextWriter(ms, new UTF8Encoding(false));
            base.WriteKeyIdentifierClauseCore(tempWriter, keyIdentifierClause);
            xml = Encoding.UTF8.GetString(ms.ToArray());
        }
        XmlDocument originalKeyIdentifierClause = new XmlDocument();
        originalKeyIdentifierClause.LoadXml(xml);

        writer.WriteStartElement("SecurityTokenReference");
        writer.WriteElementString("KeyIdentifier", originalKeyIdentifierClause.InnerText);
        writer.WriteEndElement();
    }
}