Подтвердить что ты не робот

Как заставить WCF-клиент соответствовать определенной WS-Security - подписать UsernameToken и SecurityTokenReference

Мне нужно создать wcf-клиент для вызова службы, с которой я не контролирую.

Мне дали wsdl и рабочий проект soapui.

Служба использует как имя пользователя/пароль, так и сертификат x509.


ОБНОВЛЕНИЕ

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

Мне нужно подписать как UsernameToken, так и SecurityTokenReference.

Код, который я должен был создать, был удален из этого сообщения, поскольку его больше не используют. Я больше не добавляю SecurityBindingElement к привязке, вместо этого добавляю новое поведение, которое записывает элемент безопасности в заголовок.

Таким образом, безопасность node создается с нуля путем подклассификации класса SignedXml, добавления ссылок на подпись и последующего вызова ComputeSignature для создания Signature node в заголовке Security.

Вам нужно передать xml для входа в конструктор SignedXml, чтобы он работал. Это не проблема, проходящая в UsernameToken, и это выглядит правильно.

Проблема заключается в том, что SecurityTokenReference создается только тогда, когда вызывается ComputeSignature(), поэтому я не могу добавить ссылку подписи к этому элементу, поскольку она не существует в то время, когда она требуется (в пределах переопределенного GetIdElement метод SignedXml, который вызывается до ComputeSignature())


Код, который я использую для создания блока подписи для вставки в заголовок безопасности, выглядит следующим образом

   string certificatePath = System.Windows.Forms.Application.StartupPath + "\\" + "Certs\\sign-and-    enc.p12";

    XmlDocument xd = new XmlDocument();
    xd.LoadXml(xml);

    // Set Certificate 
    System.Security.Cryptography.X509Certificates.X509Certificate2 cert = new X509Certificate2(certificatePath, "password");
    MySignedXml signedXml = new MySignedXml(xd);
    signedXml.SigningKey = cert.PrivateKey;

    // Create a new KeyInfo object. 
    KeyInfo keyInfo = new KeyInfo();
    keyInfo.Id = "";

    MemoryStream keyInfoStream = new MemoryStream();
    XmlWriter keyInfoWriter = XmlWriter.Create(keyInfoStream);

    WSSecurityTokenSerializer.DefaultInstance.WriteKeyIdentifierClause(keyInfoWriter, new LocalIdKeyIdentifierClause("token_reference", typeof(X509SecurityToken)));   

    keyInfoWriter.Flush();
    keyInfoStream.Position = 0;
    XmlDocument keyInfoDocument = new XmlDocument();
    keyInfoDocument.Load(keyInfoStream);

    XmlAttribute attrib = keyInfoDocument.CreateAttribute("ValueType");
    attrib.InnerText = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3";
    keyInfoDocument.ChildNodes[1].ChildNodes[0].Attributes.Append(attrib);

    KeyInfoNode keyInfoNode = new KeyInfoNode();
    keyInfoNode.LoadXml(keyInfoDocument.DocumentElement);
    keyInfo.AddClause(keyInfoNode);

    // Add the KeyInfo object to the SignedXml object. 
    signedXml.KeyInfo = keyInfo;

    // Need to use External Canonicalization method. 
    signedXml.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#";

    // Create a reference to be signed. 
    Reference reference = new Reference();
    reference.Uri = "#UsernameToken-1";

    XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform();
    env.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
    reference.AddTransform(env);
    reference.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
    signedXml.AddReference(reference);


    Reference reference2 = new Reference();
    reference2.Uri = "#token_reference";
    XmlDsigEnvelopedSignatureTransform env2 = new XmlDsigEnvelopedSignatureTransform();
    env2.Algorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
    reference2.AddTransform(env2);
    reference2.DigestMethod = "http://www.w3.org/2000/09/xmldsig#sha1";
    signedXml.AddReference(reference2);

    // Add the Signature Id 
    signedXml.Signature.Id = "MYSIG_ID";

    // Compute the signature. 
    signedXml.ComputeSignature();

    XmlElement xmlDigitalSignature = signedXml.GetXml();

где переменная xml - это строка xml UsernameToken, а класс MySignedXml является подклассом SignedXml с переопределенным методом GetIdElement (чтобы попытаться найти и правильно устранить несуществующий SecurityTokenReference)

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

Полное рабочее мыльное сообщение (проект soapUI)

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:XXXXX">
   <soapenv:Header xmlns:ebxml="http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/">
   <wsse:Security soapenv:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
    <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="CertId-D05E596B5ABC341FEB13505090224061" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">MIIEnDCCBAWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnowHhcNMTEwOTE1MDIwNjIwWhcNMjEwOTEyMDIwNjIwWjCByTELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxJTAjBgNVBAMTHHNpZ24tYW5kLWVuYy5kZXYuaXJkLmdvdnQubnoxFTATBgNVBCkTDHNpZ24tYW5kLWVuYzEoMCYGCSqGSIb3DQEJARYZY2hyaXMuc2NodWx0ekBpcmQuZ292dC5uejCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAykyZHVnXjsG220zB3kNOsGBeGP2rdNbLlIqW1D8yZO1fcj3/RhRiqsopbUrb8AU1ClpfhbH2K68kg7V91VAY0qrwNxP+pPPo1vYKMU6pT38qJGQzffr+iV2BCJshZvSk9E7QSWO5mFNstdg19xc+5ST1Lgb3fefuRG2KZVxPx0sCAwEAAaOCAZUwggGRMAkGA1UdEwQCMAAwEQYJYIZIAYb4QgEBBAQDAgZAMDQGCWCGSAGG+EIBDQQnFiVFYXN5LVJTQSBHZW5lcmF0ZWQgU2VydmVyIENlcnRpZmljYXRlMB0GA1UdDgQWBBSczRKXKPe3Sin7eFrVXfI7MXckzzCB+QYDVR0jBIHxMIHugBSLWxPSZd9otEj16vhLyovMCI9OMaGByqSBxzCBxDELMAkGA1UEBhMCTloxDTALBgNVBAgTBFdHVE4xEzARBgNVBAcTCldlbGxpbmd0b24xGDAWBgNVBAoTD0lSRFN0dWR5bGlua0IyQjEUMBIGA1UECxMLRGV2ZWxvcG1lbnQxHjAcBgNVBAMTFWRldmNhLmIyYi5pcmQuZ292dC5uejEXMBUGA1UEKRMORGV2ZWxvcG1lbnQgQ0ExKDAmBgkqhkiG9w0BCQEWGWNocmlzLnNjaHVsdHpAaXJkLmdvdnQubnqCCQDL/qDdlx2j6DATBgNVHSUEDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEFBQADgYEAS4ZPIVVpgTOGN4XcIC3SiYlxF8wYg7qnUhH5wJsAD3VCKfj68j9FSJtdBWLlWvvRxEoDP2lZ0IbFl6Rjnl+2ibzFnyac2G1AVm5mwPrNKHBQJ9J5eDJi0iUVY7Wphz86tKnqj34GvlHPNXmrF7oGEcDhPwK0T8zRdE/pvKIUiJc=</wsse:BinarySecurityToken>
    <ds:Signature Id="Signature-2" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
            <ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
            <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
            <ds:Reference URI="#CertId-D05E596B5ABC341FEB13505090224061">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue>3iVAUEAt8vAb7Ku+jf2gwSkSm0Q=</ds:DigestValue>
            </ds:Reference>
            <ds:Reference URI="#UsernameToken-1">
                <ds:Transforms>
                    <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                </ds:Transforms>
                <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                <ds:DigestValue>r4HLEAWJldJwmEmcAqV6Y8rnTPE=</ds:DigestValue>
            </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>
        YGh2I3VcukqjT0O7hKItiykWN5tlID18ZXRCwQjXriHmnVsO4wGcHjWfmhuNDecq+xRN+SjG8E7M
        2Rx/5/BbFKbVlNOkQOSbSxIs1YT9GaThK16pMrX5KRkkJme1W3R0pGIIQh6gGRSUf79RZUIYxyVl
        LqdIe561TXXDdtbt/6Q=
        </ds:SignatureValue>
        <ds:KeyInfo Id="KeyId-D05E596B5ABC341FEB13505090224372">
            <wsse:SecurityTokenReference wsu:Id="STRId-D05E596B5ABC341FEB13505090224373" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
                <wsse:Reference URI="#CertId-D05E596B5ABC341FEB13505090224061" 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:UsernameToken wsu:Id="UsernameToken-1" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
        <wsse:Username>XXXXXX</wsse:Username>
        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXXXXX</wsse:Password>
        </wsse:UsernameToken>
    </wsse:Security>
  <ebxml:Messaging xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     <ebxml:UserMessage>
        <ebxml:MessageInfo>
           <ebxml:Timestamp>2002-02-02T14:18:02.0Z</ebxml:Timestamp>
           <ebxml:MessageId>bf9433d9-c6e9-4c12-9c98-724008a09c21</ebxml:MessageId>
        </ebxml:MessageInfo>
        <ebxml:PartyInfo>
           <ebxml:From>
              <ebxml:PartyId type="identifier">Trading Partner X</ebxml:PartyId>
              <ebxml:Role>Provider</ebxml:Role>
           </ebxml:From>
           <ebxml:To>
              <ebxml:PartyId type="identifier">XXXXXXX</ebxml:PartyId>
              <ebxml:Role>Requestor</ebxml:Role>
           </ebxml:To>
        </ebxml:PartyInfo>
        <ebxml:CollaborationInfo>
           <ebxml:AgreementRef>urn:XXXXXXXXX</ebxml:AgreementRef>
           <ebxml:Service type="Web Service">urn:XXXXXXXX</ebxml:Service>
           <ebxml:Action>customerInformation</ebxml:Action>
           <ebxml:ConversationId>e302426a-b2d9-4ff1-a14b-fbbc2f40a017</ebxml:ConversationId>
        </ebxml:CollaborationInfo>
     </ebxml:UserMessage>
  </ebxml:Messaging>
   </soapenv:Header>
   <soapenv:Body>
  <urn:ConnectionTest>
     <Message>Bonjour</Message>
  </urn:ConnectionTest>
   </soapenv:Body>
</soapenv:Envelope>

Нам предоставили некоторую документацию. Раздел безопасности скопирован ниже

Для каждого запроса в следующем порядке должна применяться следующая безопасность: Wsse: UsernameToken должен быть включен и содержать:

  • Имя портала агента для значения элемента wsse: Username
  • Пароль портала агента для значения wsse: PasswordText (чистый текст) в элементе Password Блок подписи: Цифровые подписи создаются с использованием сертификатов x509. Каждому поставщику программного обеспечения требуется хранить и защищать действительный сертификат и закрытый ключ, выпущенные [To Be Determined]. При каждом запросе на обслуживание программное обеспечение должно:

  • Включить сертификат, который соответствует закрытому ключу, используемому для подписи как wsse: BinarySecurityToken, и использовать его как wsse: SecurityTokenReference для Подписи

  • Подпишите wsse: BinarySecurityToken
  • Подпишите wsse: UsernameToken
  • Использовать алгоритм метода канонизации: http://www.w3.org/2001/10/xml-exc-c14n#
  • Алгоритм метода подписи: http://www.w3.org/2000/09/xmldsig#rsa-sha1
  • Использовать алгоритм метода дайджеста: http://www.w3.org/2000/09/xmldsig#sha1

Обновление

Изображение конфигурации SoapUI, которое я изначально дал SoapUIConfig

4b9b3361

Ответ 1

Наконец, отсортирована проблема сегодня. Что касается терминологии, мне нужно не подписывать SecurityTokenReference, а токен двоичной безопасности.

Для этого мне нужно было скрыть сертификаты для Инициатора и Получателя и добавить подписанный подтверждающий токен.

Я вернулся к использованию конфигурации для создания и подписания сообщения, вместо того, чтобы пытаться добавить подпись вручную.

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

Код для создания привязки следует

    private System.ServiceModel.Channels.Binding GetCustomBinding()
    {
        System.ServiceModel.Channels.AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement();
        asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;

        asbe.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
        asbe.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };
        asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;

        asbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
        asbe.EnableUnsecuredResponse = true;
        asbe.IncludeTimestamp = false;
        asbe.SetKeyDerivation(false);
        asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15;
        asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
        asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());

        CustomBinding myBinding = new CustomBinding();
        myBinding.Elements.Add(asbe);
        myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));

        HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
        httpsBindingElement.RequireClientCertificate = true;
        myBinding.Elements.Add(httpsBindingElement);

        return myBinding;
    }

При использовании привязки я устанавливаю ClientCredentials UserName, ServiceCertificate и ClientCertificate, и все работает как ожидалось.


Использование кода выглядит следующим образом

using (CredentialingService.SOAPPortTypeClient client = GetCredentialingClient())
{
    client.Open();
    etc....
}

private static CredentialingService.SOAPPortTypeClient GetCredentialingClient()
{
    CredentialingService.SOAPPortTypeClient client = new CredentialingService.SOAPPortTypeClient(GetCustomBinding(), new EndpointAddress(new Uri(Settings.AppSettings.B2BUrl), new DnsEndpointIdentity(Settings.AppSettings.B2BDNSEndpoint), new AddressHeaderCollection()));
    client.Endpoint.Contract.ProtectionLevel = System.Net.Security.ProtectionLevel.None;
    SetClientCredentialsSecurity(client.ClientCredentials);

    return client;
}

где GetCustomBinding указывается в моем сообщении

SetClientCredentialsSecurity - это место, где установлен сертификат, и выглядит следующим образом

private static void SetClientCredentialsSecurity(ClientCredentials clientCredentials)
{
    clientCredentials.UserName.UserName = Settings.AppSettings.B2BUserName;
    clientCredentials.UserName.Password = Settings.AppSettings.B2BPassword;

    string directoryName = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
    clientCredentials.ServiceCertificate.DefaultCertificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BServerCertificateName));
    clientCredentials.ClientCertificate.Certificate = new X509Certificate2(Path.Combine(directoryName, Settings.AppSettings.B2BClientCertificateName), Settings.AppSettings.B2BClientCertificatePassword);
}

Надеюсь, это немного упростит

Ответ 2

Возможно, это потому, что ваш сертификат не является общедоступным. Попробуйте следующее:

System.Net.ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(delegate { return true; });
//...
using (var client = new SrvClient())
{
    client.ClientCredentials.UserName.UserName = "usr";
    client.ClientCredentials.UserName.Password = "psw";
    //...
}