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

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

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

UPDATE Мне удалось приблизиться к формату сообщения Soap, к которому мы стремимся, но WCF настаивает на подписи токенов имени пользователя и метки времени. Есть ли способ изменить обязательна только подпись метки времени?


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

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

Есть предложения?

CURRENT CONFIG

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

<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

<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>

Ответы [ 4 ]

3 голосов
/ 23 февраля 2011

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

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

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

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

1 голос
/ 14 августа 2013

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

0 голосов
/ 09 марта 2019

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

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

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

  1. Определить пользовательский MessageHeader, который отвечает за создание всего заголовка безопасности.
  2. Определить пользовательский 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:

  1. Необходимо создать дайджест пароля с одноразовыми номерами.
  2. Необходимо включить представление Base64 BinarySecurityToken.
  3. Отметка времени должнабыть канонизирован (только этот раздел извлечен и переформатирован) через спецификации xml-exc-c14n, обязательно включив пространство именs wsse, ns1, soapenv и xsd в заголовке.
  4. Этот раздел временной метки должен быть хеширован SHA256 и добавлен в поле DigestValue в разделе SignedInfo.
  5. Необходимо канонизировать раздел SignedInfo с новым DigestValue, убедившись, что в него включены пространства имен ns1, soapenvи xsd.
  6. Подписанная информация должна быть хеширована 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 и мою подпись, поэтому я оставил это простым.

Чтобы убедиться, что мои разделы были канонизированы правильно, я использовал инструмент под названием SC14N https://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, UsernameToken Время создания, поля Timestamp, BinarySecurityToken и BinarySecurityTokenID (убедитесь, что этот идентификатор также упоминается в разделе KeyInfo), метка времени, идентификаторы и, наконец, моя подпись. Замечание об идентификаторах. Я не думаю, что значения имеют значение, поскольку они уникальны в документе, просто убедитесь, что они являются одинаковыми идентификаторами, если на них ссылаются в другом месте в запросе, ищите '#' знак.

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

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

0 голосов
/ 24 февраля 2011

Вы можете сделать это с контрактами сообщений, см .: 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

...