问题
I'm successful in communicating with a WCF service via SoapUI (I was given specification on how to configure it), but I'm having trouble in copying those settings to .NET application. Turns out the shape of generated SOAP message (peeked via Fiddler) is being rejected by the web service, who expects a stricter layout of envelope.
I'm very close. On this picture...
... you can see three SOAP messages:
1. With X509SecurityTokenParameters.InclusionMode
set to AlwaysToRecipient
2. With X509SecurityTokenParameters.InclusionMode
set to Never
3. Expected security token, tested on SoapUI.
How do I achieve envelope from point 3 using C# code? I'm not using app.config
file, entire config is inside C# code (but I'm not dedicated on keeping it that way, it just happened). Current code:
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:
I ended up using InclusionMode = SecurityTokenInclusionMode.Never
, then hijacked the message and replaced incorrect tags manually.
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();
}
}
来源:https://stackoverflow.com/questions/34655693/in-soap-message-to-wcf-web-service-how-to-put-keyidentifier-directly-inside-sec