Search code examples
c#wcfsoapws-security

In WCF SOAP message, how to include certificate key inline (without Reference token)?


enter image description here

I need a C# configuration which would generate envelope shown on the third part of the screenshot. Currently I can only get as close as shown on the first two. My current config:

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);
    }
}

Is there any property I'm missing, or must I implement a custom token / encoder for this job?


Solution

  • This has the same answer as this question.

    There was no standard way of achieving this nonstandard implementation, so I ended up rewriting parts of the generated SOAP message manually.