Службе WCF с WS-Security требуется только подписанная метка времени

Мне нужно предоставить услугу третьей стороне, которая будет отправлять мыльные сообщения с подписанной меткой времени.

Как я могу настроить свой сервис для поддержки этого?

ОБНОВИТЬ Мне удалось приблизиться к формату сообщения Soap, к которому мы стремимся, но WCF настаивает на подписании как имени пользователя, так и маркеров метки времени. Есть ли способ изменить привязку, чтобы подписывать только метку времени?

Дальнейшее обновление Вот наши требования:

Элемент Timestamp ДОЛЖЕН быть подписан.Имя CN в сертификате, используемом для подписи, ДОЛЖНО совпадать с именем пользователя, указанным в элементе UsernameToken.Сертификат, используемый для подписи, ДОЛЖЕН быть отправлен в элементе BinarySecurityToken.Элемент KeyInfo ДОЛЖЕН содержать только элемент SecurityTokenReference, который должен использоваться для ссылки на BinarySecurityToken.НЕОБХОДИМО указать алгоритм канонизации.Метод подписи ДОЛЖЕН быть указан и ДОЛЖЕН быть алгоритмом SHA-1 или SHA-2.Отдельные подписи ДОЛЖНЫ использоваться.

Какие-либо предложения?

ТЕКУЩИЙ КОНФИГ

Привязка клиента

<bindings>
  <wsHttpBinding>
    <binding name="WSBC">
      <security mode="TransportWithMessageCredential">
        <transport clientCredentialType="Certificate" proxyCredentialType="None"></transport>
        <message clientCredentialType="UserName" negotiateServiceCredential="false" establishSecurityContext="false" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

Конечная точка клиента

<client>
  <endpoint address="https://localhost/WcfTestService/Service2.svc"
  behaviorConfiguration="CCB" binding="wsHttpBinding"
  bindingConfiguration="WSBC"
  contract="ServiceReference2.IService2"
  name="wsHttpBinding_IService2" />
</client>

Поведение клиента

<behaviors>,
  <endpointBehaviors>
    <behavior name="MBB">
      <clientCredentials>
        <clientCertificate  findValue="03 58 d3 bf 4b e7 67 2e 57 05 47 dc e6 3b 52 7f f8 66 d5 2a"
                            storeLocation="LocalMachine"
                            storeName="My"
                            x509FindType="FindByThumbprint" />
        <serviceCertificate>
          <defaultCertificate findValue="03 58 d3 bf 4b e7 67 2e 57 05 47 dc e6 3b 52 7f f8 66 d5 2a"
                              storeLocation="LocalMachine"
                              storeName="My"
                              x509FindType="FindByThumbprint"  />
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>

Служба привязки

<bindings>
  <wsHttpBinding>
    <binding name="ICB">
      <security mode="TransportWithMessageCredential">
        <transport clientCredentialType="Certificate" proxyCredentialType="None"></transport>
        <message    clientCredentialType="UserName" 
                    negotiateServiceCredential="false"
                    establishSecurityContext="false" />
      </security>
    </binding>
  </wsHttpBinding>
</bindings>

Serice Endpoint

<service name="WcfTestService.Service2" behaviorConfiguration="SCB">
    <endpoint     address="" binding="wsHttpBinding" contract="WcfTestService.IService2"
    bindingConfiguration="ICB" name="MS" />
</service>

Поведение службы

<behaviors>
  <serviceBehaviors>
    <behavior name="SCB">
      <serviceCredentials>
        <serviceCertificate     findValue="4d a9 d8 f2 fb 4e 74 bd a7 36 d7 20 a8 51 e2 e6 ea 7d 30 08"
                                storeLocation="LocalMachine"
                                storeName="TrustedPeople"   
                                x509FindType="FindByThumbprint" />
        <userNameAuthentication 
            userNamePasswordValidationMode="Custom" 
            customUserNamePasswordValidatorType="WcfTestService.UsernameValidator, WcfTestService" />
        <clientCertificate>
          <authentication certificateValidationMode="None" revocationMode="NoCheck" />
        </clientCertificate>
      </serviceCredentials>
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
    </behavior>
  </serviceBehaviors>
</behaviors>
 Edward06 мая 2011 г., 17:17
Удалось ли вам заставить конфигурацию работать так, чтобы был подписан только элемент Timestamp? В настоящее время я пытаюсь достичь того же.
 S. ten Brinke14 июн. 2018 г., 16:55
@ Edward В настоящее время я нахожусь в ситуации, когда мне нужно подписать несколько элементов (указанных в моем WSDL), но также необходимо подписать мою метку времени, которая НЕ указана в моем wsdl. Любая подсказка, как добавить это, ИЛИ как подписать его с помощью пользовательского кодировщика / как вы использовали для добавления заголовков впоследствии?
 S. ten Brinke14 июн. 2018 г., 09:46
@ Эдвард Эй, чувак, тебе удалось решить это?
 Edward14 июн. 2018 г., 13:34
@ S.tenBrinke Мне удалось сделать это, указав messageVersion как Soap11 вместо Soap11WSAddressing10, а затем вручную добавив заголовки WS-Addresing, что позволило избежать необходимости вручную реализовывать механизм подписывания.

Ответы на вопрос(4)

но не имя пользователя. Во-первых, я уверен, что это не связано с проблемой, с которой вы сталкиваетесь - сервер должен уметь обрабатывать оба случая. Если вам это нужно, то я предлагаю вообще не использовать имя пользователя в безопасности (например, режим безопасности «anonymousForCertificate»), а затем реализовать собственный кодировщик сообщений, чтобы вручную вставлять теги имени пользователя / пароля в заголовок в нужном месте (возьмите не изменяйте подписанную часть в сообщении, в основном метку времени).

 Yaron Naveh14 авг. 2013 г., 17:24
 Dog Ears14 авг. 2013 г., 17:21
Это старый вопрос, который я просто хотел решить. Первоначально требовалось только имя пользователя без пароля (имя пользователя должно было содержать общее имя сертификата SSL). Нам диктовали, нам нужно было реализовать и сервер, и клиент, которые подключались бы к сторонней системе, над которой мы не имели никакого контроля (Java). Я приведу кодировщик, любые ссылки на информацию о пользовательском кодировании будут полезны. С уважением.

но ни на один из них нет однозначных ответов, поэтому, потратив много времени на это, я оставляю свой ответ на этот восьмилетний вопрос в надежде, что он кому-нибудь поможет.

Мне пришлось отправить сообщение SOAP с паролем дайджеста и подписанной меткой времени (только подписать метку времени) на сервер черного ящика, я думаю, что это был Axis2. Я разбирался с различными конфигурациями безопасности и производными вариациями класса SignedXml и преуспел в том, чтобы мое сообщение выглядело несколько правильно, но так и не смог создать действительную подпись. Согласно Microsoft, WCF не может канонизировать так же, как серверы, не относящиеся к WCF, и WCF оставляет некоторые пространства имен и по-разному переименовывает префиксы пространств имен, так что я никогда не смогу совпасть с подписями своих подписей.

Итак, после тонны проб и ошибок вот мой DIY способ сделать это:

Определите пользовательский MessageHeader, который отвечает за создание всего заголовка безопасности.Определите пользовательский MessageInspector для переименования пространств имен, добавления отсутствующих пространств имен и добавления моего настраиваемого заголовка безопасности в заголовки запроса.

Вот пример запроса, который мне нужно было сделать:

<soapenv:Envelope xmlns:ns1="http://somewebsite.com/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="https://anotherwebsite.com/xsd">
<soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsse:UsernameToken wsu:Id="UsernameToken-1">
            <wsse:Username>username</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">aABCDiUsrOy8ScJkdABCD/ZABCD=</wsse:Password>
            <wsse:Nonce EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">ABCDxZ8IABCDg/pTK6E0Q==</wsse:Nonce>
            <wsu:Created>2019-03-07T21:31:00.281Z</wsu:Created>
        </wsse:UsernameToken>
        <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="X509-1">...</wsse:BinarySecurityToken>
        <wsu:Timestamp wsu:Id="TS-1">
            <wsu:Created>2019-03-07T21:31:00Z</wsu:Created>
            <wsu:Expires>2019-03-07T21:31:05Z</wsu:Expires>
        </wsu:Timestamp>
        <ds:Signature Id="SIG-1" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
            <ds:SignedInfo>
                <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                    <ec:InclusiveNamespaces PrefixList="ns1 soapenv xsd" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </ds:CanonicalizationMethod>
                <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>
                <ds:Reference URI="#TS-1">
                    <ds:Transforms>
                        <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                            <ec:InclusiveNamespaces PrefixList="wsse ns1 soapenv xsd" xmlns:ec="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                        </ds:Transform>
                    </ds:Transforms>
                    <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>
                    <ds:DigestValue>ABCDmhUOmjhBRPabcdB1wni53mabcdOzRMo3ABCDVbw=</ds:DigestValue>
                </ds:Reference>
            </ds:SignedInfo>
            <ds:SignatureValue>...</ds:SignatureValue>
            <ds:KeyInfo Id="KI-1">
                <wsse:SecurityTokenReference wsu:Id="STR-1">
                    <wsse:Reference URI="#X509-1" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3"/>
                </wsse:SecurityTokenReference>
            </ds:KeyInfo>
        </ds:Signature>
    </wsse:Security>
</soapenv:Header>
<soapenv:Body>
    ...
</soapenv:Body>

Вот что говорит XML:

Необходимо создать дайджест пароля с одноразовым номером.Base64 представление BinarySecurityToken должно быть включено.Временная метка должна быть канонизирована (только этот раздел извлечен и переформатирован) с помощью спецификаций xml-exc-c14n, чтобы убедиться, что в заголовке указаны пространства имен wsse, ns1, soapenv и xsd.Эта временная метка должна быть хеширована SHA256 и добавлена ​​в поле DigestValue в разделе SignedInfo.Раздел SignedInfo с новым DigestValue необходимо канонизировать, убедившись, что он включает пространства имен ns1, soapenv и xsd.Подписанная информация должна быть хеширована SHA256, а затем зашифрована RSA с результатом, добавленным в поле SignatureValue.

Пользовательский заголовок сообщения

Внедряя пользовательский заголовок сообщения, я могу записать любой xml-файл в заголовок запроса. Этот пост указал мне в правильном направленииhttps://stackoverflow.com/a/39090724/6077517

Это заголовок, который я использовал:

class CustomSecurityHeader : MessageHeader
{
    // This is data I'm passing into my header from the MessageInspector 
    // that will be used to create the security header contents
    public HeaderData HeaderData { get; set; }

    // Name of the header
    public override string Name
    {
        get { return "Security"; }
    }

    // Header namespace
    public override string Namespace
    {
        get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"; }
    }

    // Additional namespace I needed
    public string wsuNamespace
    {
        get { return "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"; }
    }

    // This is where the start tag of the header gets written
    // add any required namespaces here
    protected override void OnWriteStartHeader(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        writer.WriteStartElement("wsse", Name, Namespace);
        writer.WriteXmlnsAttribute("wsse", Namespace);
        writer.WriteXmlnsAttribute("wsu", wsuNamespace);
    }

    // This is where the header content will be written into the request
    protected override void OnWriteHeaderContents(XmlDictionaryWriter writer, MessageVersion messageVersion)
    {
        XmlDocument xmlDoc = MyCreateSecurityHeaderFunction(HeaderData); // My function that creates the security header contents.
        var securityElement = doc.FirstChild; // This is the "<security.." portion of the xml returned
        foreach(XmlNode node in securityElement.ChildNodes)
        {
            writer.WriteNode(node.CreateNavigator(), false);
        }
        return;
    }
}

Инспектор сообщений

Чтобы получить заголовок в запросе, я переопределяю класс MessageInspector. Это в значительной степени позволяет вам изменить что-либо в запросе до вставки заголовков и передачи сообщения.

Здесь есть хорошая статья, в которой используется эта схема для добавления одноразового имени пользователя в сообщение:https://weblog.west-wind.com/posts/2012/nov/24/wcf-wssecurity-and-wse-nonce-authentication

Вы должны создать пользовательский EndpointBehavior для внедрения инспектора.

public class CustomInspectorBehavior : IEndpointBehavior
{
    // Data I'm passing to my EndpointBehavior that will be used to create the security header
    public HeaderData HeaderData
    {
        get { return this.messageInspector.HeaderData; }
        set { this.messageInspector.HeaderData = value; }
    }

    // My custom MessageInspector class
    private MessageInspector messageInspector = new MessageInspector();

    public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
    }

    public void Validate(ServiceEndpoint endpoint)
    {
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        // Add the custom message inspector here
        clientRuntime.MessageInspectors.Add(messageInspector);
    }
}

А вот код для моего инспектора сообщений:

public class MessageInspector : IClientMessageInspector
{
    // Data to be used to create the security header
    public HeaderData HeaderData { get; set; }

    public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
    {
        var lastResponseXML = reply.ToString(); // Not necessary but useful for debugging if you want to see the response.
    }

    public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
    {
        // This might not be necessary for your case but I remove a bunch of unnecessary WCF-created headers from the request.
        List<string> removeHeaders = new List<string>() { "Action", "VsDebuggerCausalityData", "ActivityId" };
        for (int h = request.Headers.Count() - 1; h >= 0; h--)
        {
            if (removeHeaders.Contains(request.Headers[h].Name))
            {
                request.Headers.RemoveAt(h);
            }
        }

        // Make changes to the request.
        // For this case I'm adding/renaming namespaces in the header.
        var container = XElement.Parse(request.ToString()); // Parse request into XElement
        // Change "s" namespace to "soapenv"
        container.Add(new XAttribute(XNamespace.Xmlns + "soapenv", "http://schemas.xmlsoap.org/soap/envelope/"));
        container.Attributes().Where(a => a.Name.LocalName == "s").Remove();
        // Add other missing namespace
        container.Add(new XAttribute(XNamespace.Xmlns + "ns1", "http://somewebsite.com/"));
        container.Add(new XAttribute(XNamespace.Xmlns + "xsd", "http://anotherwebsite.com/xsd"));
        requestXml = container.ToString();

        // Create a new message out of the updated request.
        var ms = new MemoryStream();
        var sr = new StreamWriter(ms);
        var writer = new StreamWriter(ms);
        writer.Write(requestXml);
        writer.Flush();
        ms.Position = 0;

        var reader = XmlReader.Create(ms);
        request = Message.CreateMessage(reader, int.MaxValue, request.Version);

        // Add my custom security header
        // This is responsible for writing the security headers to the message
        CustomSecurityHeader header = new CustomSecurityHeader();
        // Pass data required to build security header
        header.HeaderData = new HeaderData()
        {
            Certificate = this.HeaderData.Certificate,
            Username = this.HeaderData.Username,
            Password = this.HeaderData.Password
            // ... Whatever else might be needed
        };

        // Add custom header to request headers
        request.Headers.Add(header);

        return request;
    }
}

Добавить инспектор сообщений на прокси клиента

Я сохранил свое связывание довольно простым, так как сам добавлял все элементы безопасности и не хотел добавлять неожиданные заголовки.

// IMPORTANT - my service required TLS 1.2, add this to make that happen
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;

// Encoding
var encoding = new TextMessageEncodingBindingElement();
encoding.MessageVersion = MessageVersion.Soap11;

// Transport
var transport = new HttpsTransportBindingElement();

CustomBinding binding = new CustomBinding();
binding.Elements.Add(encoding);
binding.Elements.Add(transport);

var myProxy = new MyProxyClass(binding, new EndpointAddress(endpoint));

// Add message inspector behavior to alter security header.
// data contains info to create the header such as username, password, certificate, etc.
MessageInspector = new CustomInspectorBehavior() { HeaderData = data }; 
myProxy.ChannelFactory.Endpoint.EndpointBehaviors.Add(MessageInspector);

Создать заголовок безопасности XML

Это немного уродливо, но в итоге я создал XML-шаблоны для канонизированных разделов заголовка безопасности, заполняя значения, хэшируя и подписывая раздел SignedInfo, а затем объединяя части в полный заголовок безопасности. Я бы предпочел встраивать их в код, но XmlDocument не поддерживал порядок добавляемых мной атрибутов, которые портили мой канонизированный XML и мою подпись, поэтому я оставил это простым.

Чтобы убедиться, что мои разделы были канонизированы правильно, я использовал инструмент под названием SC14Nhttps://www.cryptosys.net/sc14n/index.html, Я ввел образец XML-запроса и ссылку на раздел, который я хотел, канонизировал вместе со всеми включенными пространствами имен, и он вернул соответствующий XML. Я сохранил XML, который он возвратил, в шаблон, заменив значения и идентификаторы тегами, которые я мог бы заменить позже. Я создал шаблон для раздела Timestamp, шаблон для раздела SignedInfo и шаблон для всего раздела заголовка Security.

Интервал, конечно, важен, поэтому убедитесь, что xml остается неформатированным, и если вы загружаете XmlDocument, всегда полезно установить для PreserveWhitespace значение true:

XmlDocument doc = new XmlDocument() { PreserveWhitespace = true;}

Итак, теперь мои шаблоны сохранены в ресурсах, когда мне нужно подписать свою метку времени, я загружаю шаблон метки времени в строку, заменяю теги соответствующими полями Timestamp ID, Created и Expires, поэтому у меня есть что-то вроде этого (с правильные пространства имен и без разрывов строк, конечно):

<wsu:Timestamp xmlns:ns1="..." xmlns:soapenv="..." xmlns:wsse=".." xmlns:wsu=".." wsu:Id="TI-3">
    <wsu:Created>2019-05-07T21:31:00Z</wsu:Created>
    <wsu:Expires>2019-05-07T21:36:00Z</wsu:Expires>
</wsu:Timestamp>

Тогда получите хеш:

// Get hash of timestamp.
SHA256Managed shHash = new SHA256Managed();
var fileBytes = System.Text.Encoding.UTF8.GetBytes(timestampXmlString);
var hashBytes = shHash.ComputeHash(fileBytes);
var digestValue = Convert.ToBase64String(hashBytes);

Далее мне нужен шаблон моего раздела SignedInfo. Я извлекаю это из своих ресурсов и заменяю соответствующие теги (в моем случае ссылочный идентификатор метки времени и вычисленное выше значение метки времени digestValue), а затем получаю хэш этого раздела SignedInfo:

// Get hash of the signed info
SHA256Managed shHash = new SHA256Managed();
fileBytes = System.Text.Encoding.UTF8.GetBytes(signedInfoXmlString);
hashBytes = shHash.ComputeHash(fileBytes);
var signedInfoHashValue = Convert.ToBase64String(hashBytes);

Затем я подписываю хеш подписанной информации, чтобы получить подпись:

using (var rsa = MyX509Certificate.GetRSAPrivateKey())
{
    var signatureBytes = rsa.SignHash(hashBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
    SignatureValue = Convert.ToBase64String(signatureBytes); // This is my signature!
}

Если это не удается, убедитесь, что ваш сертификат настроен правильно, он также должен иметь закрытый ключ. Если вы используете старую версию фреймворка, вам, возможно, придется прыгнуть через несколько обручей, чтобы получить ключ RSA. Увидетьhttps://stackoverflow.com/a/38380835/6077517

Имя пользователя Пароль Дайджест Nonce

Мне не нужно было подписывать имя пользователя, но я должен был вычислить дайджест пароля. Он определяется как Base64 (SHA1 (Nonce + CreationTime + Password)).

    // Create nonce
    SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
    var nonce = Guid.NewGuid().ToString("N");
    var nonceHash = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(nonce));
    var NonceValue = Convert.ToBase64String(nonceHash);

    var NonceCreatedTime = DateTimeOffset.UtcNow.ToString("yyyy-MM-ddThh:mm:ss.fffZ");

    // Create password digest Base64( SHA1(Nonce + Created + Password) )
    var nonceBytes = Convert.FromBase64String(NonceValue); // Important - convert from Base64
    var createdBytes = Encoding.UTF8.GetBytes(NonceCreatedTime);
    var passwordBytes = Encoding.UTF8.GetBytes(Password);

    var concatBytes = new byte[nonceBytes.Length + createdBytes.Length + passwordBytes.Length];
    System.Buffer.BlockCopy(nonceBytes, 0, concatBytes, 0, nonceBytes.Length);
    System.Buffer.BlockCopy(createdBytes, 0, concatBytes, nonceBytes.Length, createdBytes.Length);
    System.Buffer.BlockCopy(passwordBytes, 0, concatBytes, nonceBytes.Length + createdBytes.Length, passwordBytes.Length);

    // Hash the combined buffer
    var hashedConcatBytes = sha1Hasher.ComputeHash(concatBytes);
    var PasswordDigest = Convert.ToBase64String(hashedConcatBytes);

В моем случае была дополнительная ошибка, что пароль должен быть хеширован SHA1. Это то, что SoapUI называет «PasswordDigest Ext», если вы настраиваете имя пользователя WS-Security в SoapUI. Имейте это в виду, если у вас все еще возникают проблемы с аутентификацией, я потратил много времени, прежде чем понял, что мне нужно сначала хешировать свой пароль.

Еще одна вещь, которую я не знал, как сделать, вот как получить двоичное значение токена безопасности Base64 из вашего сертификата X509:

var bstValue = Convert.ToBase64String(myCertificate.Export(X509ContentType.Cert));

Наконец, я извлекаю свой шаблон заголовка безопасности из ресурсов и заменяю все соответствующие значения, которые я собрал или рассчитал: UsernameTokenId, Имя пользователя, Дайджест пароля, Nonce, Имя пользователяToken Время создания, Поля отметки времени, BinarySecurityToken и BinarySecurityTokenID (убедитесь, что на этот идентификатор также ссылаются в Раздел KeyInfo), метка времени, идентификаторы и, наконец, моя подпись. Замечание об идентификаторах. Я не думаю, что значения имеют значение, поскольку они уникальны в документе, просто убедитесь, что они являются одинаковыми идентификаторами, если на них ссылаются в другом месте в запросе, ищите '#' знак.

Скомпилированная строка заголовка безопасности XML - это то, что загружается в XmlDocument (не забудьте сохранить пробелы) и передается в специальный MessageHeader для сериализации в CustomHeader.OnWriteHeaderContents (см. CustomHeader выше).

Уф. Надеюсь, это сэкономит кому-то много работы, извинений за опечатки или необъяснимые шаги. Я бы хотел увидеть элегантную реализацию всего этого в чистом WCF, если бы кто-нибудь понял это.

http://msdn.microsoft.com/en-us/library/ms730255.aspx

Вот пример из приведенной выше ссылки:

[MessageContract]
public class PatientRecord 
{
   [MessageHeader(ProtectionLevel=None)] public int recordID;
   [MessageHeader(ProtectionLevel=Sign)] public string patientName;
   [MessageHeader(ProtectionLevel=EncryptAndSign)] public string SSN;
   [MessageBodyMember(ProtectionLevel=None)] public string comments;
   [MessageBodyMember(ProtectionLevel=Sign)] public string diagnosis;
   [MessageBodyMember(ProtectionLevel=EncryptAndSign)] public string medicalHistory;
}

Обратите внимание на уровни защиты Нет, Подпись, EncryptAndSign

 Dog Ears03 мар. 2011 г., 20:52
Я не думаю, что это сработает. Я в основном пытаюсь добавить wsse: UserNameToken, но использование этого подхода не работает, так как фреймворк распознает токен и выдает ошибку, так как это не является частью привязки?

вы захотите рассмотреть пользовательский класс привязки безопасности, который реализует безопасность именно так, как вы этого хотите, а не WCF по умолчанию.

Эти ссылки MSDN объясняют пользовательские привязки и абстрактный базовый класс SecurityBindingElement:

http://msdn.microsoft.com/en-us/library/ms730305.aspx

http://msdn.microsoft.com/en-us/library/system.servicemodel.channels.securitybindingelement.aspx

 Dog Ears04 мар. 2011 г., 00:30
Это был не я! Я потянул за волосы, чтобы получить правильную привязку для получения правильных результатов! Я должен обновить вопрос, так как теперь в основном то, что я хочу, это basicHttpBinding (Soap 1.1) с режимом безопасности = TransportWithMessageCredential как с сертификатом для сообщения, так и с транспортом. Но дополнительно мы должны также отправить / получить UsernameToken ..!
 John Weldon04 мар. 2011 г., 06:23
Спасибо за комментарий @Dog Ears, как вы думаете, стоит ли создавать новый вопрос с вашим более конкретным запросом?

Ваш ответ на вопрос