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

Posted

技术标签:

【中文标题】在到 WCF Web 服务的 SOAP 消息中,如何将 KeyIdentifier 直接放在 SecurityTokenReference 中(内联,不使用引用令牌)【英文标题】:In SOAP message to WCF web service, how to put KeyIdentifier directly inside SecurityTokenReference (inline, without using Reference token) 【发布时间】:2016-04-11 21:00:42 【问题描述】:

我成功地通过 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);
    

【问题讨论】:

【参考方案1】:

我最终使用了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();
    

【讨论】:

以上是关于在到 WCF Web 服务的 SOAP 消息中,如何将 KeyIdentifier 直接放在 SecurityTokenReference 中(内联,不使用引用令牌)的主要内容,如果未能解决你的问题,请参考以下文章

wcf 如何为 SOAP Web 服务设置不可为空的 DataMember

web service && WCF 学习总结

WCF 客户端异常:无法识别的消息版本

WCF SOAP

HTTP 标头或 SOAP 标头中的 WCF 肥皂操作?

读取 SOAP 消息头 WCF