WCF SOAP 1.1 i WS-Security 1.0, client Cert transport auth, service cert for message body signature, UsernameToken, Password Digest, Nonce

Podsumowanie: Pracuję nad klientem. NET 4.0 WCF, aby korzystać z usługi internetowej (DataPower, usługa Java na drugim końcu) przy użyciu SOAP 1.1 i WS-Security 1.0. Klient WCF musi zaimplementować certyfikat klienta do wzajemnego uwierzytelniania w warstwie transportowej. Treść wiadomości musi być podpisana przy użyciu oddzielnego certyfikatu usługi/podpisywania. Nagłówek SOAP musi również zawierać Token nazwy użytkownika z skrótem haseł oraz zawierać znaczniki Nonce I Created.

Jestem w stanie konsumować ten serwis internetowy korzysta z GPW 3.0 z BasicHTTPBinding. Ale do tej pory nie udało mi się zaimplementować tego samego z WCF przy użyciu WSHttpBinding lub CustomBinding. Próbowałem wszystkich elementów wiążących i do tej pory nie miałem szczęścia.

Korzystam również z biblioteki usernametoken stąd ( http://blogs.msdn.com/b/aszego/archive/2010/06/24/usernametoken-profile-vs-wcf.aspx) więc Mogę dodać hasło digest / nonce / created w UsernameToken w SOAP nagłówek.

Obecnie używam SecurityBindingElement.CreateMutualCertificateBindingElement wypróbowałem również kilka innych, takich jak AsymmetricSecurityBindingElement, TransportSecurityBindingElement itp.]}

CERTS: Mam zarówno certyfikat klienta jak i certyfikat usługi załadowany do sklepu certyfikatów za pomocą MMC (jestem na Windows 7 btw.) Zarówno Cert klienta, jak i Cert usługi mają klucze prywatne. Załadowałem oba pliki PFX do LocalMachine / Personal, LocalMachine / Root i LocalMachine / TrustedPeople. Uruchomiłem również FindPrivateKey / ICACLS, aby zezwolić na konto "IIS App Pool / DefaultAppPool". Chociaż nic z tego nie powinno mieć znaczenia, ponieważ mogę uruchomić kod GPW 3.0 z mojego komputera i działa bez żadnych problemów z cert.

polecenia Uruchom:

FindPrivateKey.exe My LocalMachine -t "thumbprint of client cert"
FindPrivateKey.exe My LocalMachine -t "thumbprint of service cert"
icacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\{privateKeyOfClientCert} /grant "IIS AppPool\DefaultAppPool":R      <<Successfully processed 1 files; Failed processing 0 files>>
icacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\{privateKeyOfServiceCert} /grant "IIS AppPool\DefaultAppPool":R     <<Successfully processed 1 files; Failed processing 0 files>>

WYDANIE WCF: Obecnie otrzymuję "nie można ustanowić bezpiecznego kanału dla SSL/TLS z autorytetem "x.x.com" wiadomość z DataPower gateway. Domyślam się, że może to być spowodowane tym, że brama bierze certyfikat usługi i używa go do uwierzytelniania klienta zamiast używać certyfikatu klienta, który wysyłam. Mówię to, ponieważ gdy nie określę tożsamości DNS dla punktu końcowego, otrzymuję komunikat, że brama oczekuje, że tożsamość DNS będzie " {subject name of service/signing certificate}".

Oto żądanie SOAP wygenerowane przez WCF, które daje powyższy błąd. WCF SOAP request wygląda bardzo podobnie do GPW SOAP request. Powyższy błąd najprawdopodobniej występuje z powodu problemu z cert na warstwie SSL/Transport.

Prośba o mydło WCF:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header>
    <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <u:Timestamp u:Id="uuid-8533d9a5-865e-4a4b-a750-fadb7c1ce36c-1">
            <u:Created>2013-02-06T20:53:04.679Z</u:Created>
            <u:Expires>2013-02-06T20:58:04.679Z</u:Expires>
        </u:Timestamp>
        <o:BinarySecurityToken u:Id="uuid-0bab08ce-3e3b-4360-a44b-694b06a3dd67-2" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">Removed Service Cert Encoded Value</o:BinarySecurityToken>
        <wsse:UsernameToken wsu:Id="7843ab92-f69a-4d00-a5ba-117e32a74f49" 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:Username>USER_Removed</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">XXX=</wsse:Password>
            <wsse:Nonce>XXX==</wsse:Nonce>
            <wsu:Created>2013-02-06T20:53:04Z</wsu:Created>
        </wsse:UsernameToken>
        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
                <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
                <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
                <Reference URI="#_1">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#uuid-8533d9a5-865e-4a4b-a750-fadb7c1ce36c-1">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#7843ab92-f69a-4d00-a5ba-117e32a74f49">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
            </SignedInfo>
            <SignatureValue>XXXLongXXX=</SignatureValue>
            <KeyInfo>
                <o:SecurityTokenReference>
                    <o:Reference URI="#uuid-0bab08ce-3e3b-4360-a44b-694b06a3dd67-2"></o:Reference>
                </o:SecurityTokenReference>
            </KeyInfo>
        </Signature>
    </o:Security>
</s:Header>
<s:Body u:Id="_1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <ping xmlns="https://x.x.com/xxx/v1">
        <pingRequest xmlns="">hello</pingRequest>
    </ping>
</s:Body>

GPW 3.0 soap request (to działa):

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
           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">
<soap:Header>
    <wsa:Action wsu:Id="Id-4271fb72-464a-467d-ab1f-4d32542e20f0"/>
    <wsa:MessageID wsu:Id="Id-11657f64-d856-47d8-b600-d5379fb91a0d">urn:uuid:ff8becb7-74c2-4844-ab46-8ae23f1355a7</wsa:MessageID>
    <wsa:ReplyTo wsu:Id="Id-40b2e6e8-e67b-4a6c-a545-071ce0f0107a">
        <wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
    </wsa:ReplyTo>
    <wsa:To wsu:Id="Id-d5e0b488-6f8a-479c-940d-2b85833dbc66">https://x.x.com/xxx/v1</wsa:To>
    <wsse:Security soap:mustUnderstand="1">
        <wsu:Timestamp wsu:Id="Timestamp-68476551-5c58-4a47-967b-54ec18257b1b">
            <wsu:Created>2013-02-06T19:38:39Z</wsu:Created>
            <wsu:Expires>2013-02-06T19:43:39Z</wsu:Expires>
        </wsu:Timestamp>
        <wsse:UsernameToken wsu:Id="SecurityToken-e5f65166-a825-48cb-a939-8e515a637e01">
            <wsse:Username>USER_Removed</wsse:Username>
            <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest">XXX=</wsse:Password>
            <wsse:Nonce>XXX==</wsse:Nonce>
            <wsu:Created>2013-02-06T19:38:39Z</wsu:Created>
        </wsse:UsernameToken>
        <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
                <ds:CanonicalizationMethod xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                <Reference URI="#Id-4271fb72-464a-467d-ab1f-4d32542e20f0">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#Id-11657f64-d856-47d8-b600-d5379fb91a0d">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#Id-40b2e6e8-e67b-4a6c-a545-071ce0f0107a">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#Id-d5e0b488-6f8a-479c-940d-2b85833dbc66">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#Timestamp-68476551-5c58-4a47-967b-54ec18257b1b">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
                <Reference URI="#Id-6f76e50e-932c-4878-bbc0-3ef4c8a36990">
                    <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    </Transforms>
                    <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                    <DigestValue>XXX=</DigestValue>
                </Reference>
            </SignedInfo>
            <SignatureValue>XXXLongXXX=</SignatureValue>
            <KeyInfo>
                <wsse:SecurityTokenReference>
                    <wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier"
                                        EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">XXX=</wsse:KeyIdentifier>
                </wsse:SecurityTokenReference>
            </KeyInfo>
        </Signature>
    </wsse:Security>
</soap:Header>
<soap:Body wsu:Id="Id-6f76e50e-932c-4878-bbc0-3ef4c8a36990">
    <ping xmlns="https://x.x.com/xxx/v1">
        <pingRequest xmlns="">hello</pingRequest>
    </ping>
</soap:Body>

Tutaj jest cała konfiguracja, proszę dać mi znać, co robię źle!

WCF web.config: usunąłem wszystko z sieci.config jak robię wszystkie config w kod.

Konfiguracja WCF w kodzie:

var proxy = GetProxy();
pingResponseMessage resp = proxy.ping("hello");
lblStatus.Text = resp.status.ToString();

private XXXClient GetProxy()
{

    System.Net.ServicePointManager.ServerCertificateValidationCallback += (se, cert, chain, sslerror) => { return true; };

    XXXClient proxy = new XXXClient(GetCustomBinding(), new EndpointAddress(new Uri("https://xxx"), EndpointIdentity.CreateDnsIdentity("I am forced to put the signing cert subject here, nothing else works"), new AddressHeaderCollection()));

    proxy.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
    proxy.Endpoint.Behaviors.Add(new UsernameClientCredentials(new UsernameInfo(@"USER_Removed", "X")));

    proxy.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "REMOVED");
    proxy.ClientCredentials.ServiceCertificate.SetDefaultCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, "REMOVED");
    proxy.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None;

    return proxy;
}

private Binding GetCustomBinding()
{
    //TransportSecurityBindingElement secBE = SecurityBindingElement.CreateCertificateOverTransportBindingElement(MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
    //AsymmetricSecurityBindingElement secBE = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
    //secBE.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient, RequireDerivedKeys = false, X509ReferenceStyle = X509KeyIdentifierClauseType.SubjectKeyIdentifier };
    //secBE.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToInitiator, RequireDerivedKeys = false, X509ReferenceStyle = X509KeyIdentifierClauseType.SubjectKeyIdentifier };
    //secBE.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
    //secBE.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters() { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient, RequireDerivedKeys = false });
    //secBE.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.Never) { InclusionMode = SecurityTokenInclusionMode.Never, RequireDerivedKeys = false, X509ReferenceStyle = X509KeyIdentifierClauseType.SubjectKeyIdentifier });
    //secBE.ProtectionTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient };
    //secBE.DefaultAlgorithmSuite = new CustomSecurityAlgorithm();

    SecurityBindingElement secBE = SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
    secBE.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
    secBE.EndpointSupportingTokenParameters.Signed.Add(new UsernameTokenParameters() { InclusionMode= SecurityTokenInclusionMode.AlwaysToRecipient, ReferenceStyle = SecurityTokenReferenceStyle.External, RequireDerivedKeys = false });
    secBE.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
    //secBE.AllowInsecureTransport = false;
    //secBE.AllowSerializedSigningTokenOnReply = false;
    secBE.EnableUnsecuredResponse = true;
   secBE.IncludeTimestamp = true;
    secBE.SetKeyDerivation(false);

    TextMessageEncodingBindingElement textEncBE = new TextMessageEncodingBindingElement(MessageVersion.Soap11, System.Text.Encoding.UTF8);

    HttpsTransportBindingElement httpsBE = new HttpsTransportBindingElement();
    httpsBE.RequireClientCertificate = true;
    //httpsBindingElement.AllowCookies = false;
    //httpsBindingElement.AuthenticationScheme = System.Net.AuthenticationSchemes.Basic;
    httpsBE.BypassProxyOnLocal = false;
    httpsBE.HostNameComparisonMode = HostNameComparisonMode.StrongWildcard;
    //httpsBindingElement.KeepAliveEnabled = false;
    httpsBE.TransferMode = TransferMode.Buffered;
    httpsBE.UseDefaultWebProxy = true;

    CustomBinding myBinding = new CustomBinding();
    myBinding.Elements.Add(secBE);
    myBinding.Elements.Add(textEncBE);
    myBinding.Elements.Add(httpsBE);

    return myBinding;
}

Dodałem poziom ochrony.Podpisz w Serwisiekontrakt i umowy operacyjne, ponieważ muszę tylko podpisać treść wiadomości. Nie zaszedłem tak daleko, żeby to jeszcze zweryfikować.

[System.ServiceModel.ServiceContractAttribute(Namespace = "https://x.x.com/xxx/v1", ConfigurationName = "x.x", ProtectionLevel = System.Net.Security.ProtectionLevel.Sign)]
public interface XXXService {
    [System.ServiceModel.OperationContractAttribute(Action = "", ReplyAction = "*", ProtectionLevel = System.Net.Security.ProtectionLevel.Sign)]
    [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
    [return: System.ServiceModel.MessageParameterAttribute(Name="return")]
    XXX.pingResponse ping(XXX.ping request);

[System.ServiceModel.ServiceContractAttribute(Namespace = "https://x.x.com/xxx/v1", ProtectionLevel = System.Net.Security.ProtectionLevel.Sign)]
public partial class XXXClient : System.ServiceModel.ClientBase<XXXService> {
    [System.ServiceModel.OperationContractAttribute(Action = "", ReplyAction = "*", ProtectionLevel = System.Net.Security.ProtectionLevel.Sign)]
    public XXX.pingResponseMessage ping(string pingRequest) {

Dodałem poniżej do web.konfiguracja pozwalająca na rejestrowanie całego soap łącznie z danymi pii

(for pii, also added <machineSettings enableLoggingKnownPii="true" /> under <system.serviceModel> to C:\Windows\Microsoft.NET\Framework\vX\CONFIG\machine.config)

<system.serviceModel>
<diagnostics>
  <messageLogging logKnownPii="true" logEntireMessage="true" logMalformedMessages="true" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" maxMessagesToLog="3000"/>
</diagnostics>
</system.serviceModel>
<system.diagnostics>
<sources>
  <source name="System.ServiceModel.MessageLogging" logKnownPii="true">
    <listeners>
      <add initializeData="C:\trace.log" type="System.Diagnostics.XmlWriterTraceListener" name="messages"/>
    </listeners>
  </source>
</sources>
</system.diagnostics>

===============

GPW 3.0 (Working config and code): www.config:

<system.serviceModel>
<bindings>
  <basicHttpBinding>
    <binding name="myBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
      <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
      <security mode="Transport">
        <transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
        <message clientCredentialType="UserName" algorithmSuite="Default"/>
      </security>
    </binding>
  </basicHttpBinding>
</bindings>
<client>
  <endpoint address="https://x.x.com/xxx/v1" binding="basicHttpBinding" bindingConfiguration="myBinding" contract="XXXService" name="XXX"/>
</client>
</system.serviceModel>
<appSettings>
<add key="XXXImplService" value="https://x.x.com/xxx/v1"/>
</appSettings>

... oraz Kod GPW:

var proxy = new XXXImplServiceWse();

UsernameToken usernameToken = new UsernameToken(@"USER_Removed", "X");
proxy.RequestSoapContext.Security.Tokens.Add(usernameToken);

X509Certificate2 mutualCert = LoadCertFromStore(StoreLocation.LocalMachine, StoreName.My, "Client Cert Subject Name");
proxy.ClientCertificates.Add(mutualCert);

X509Certificate2 signCert = LoadCertFromStore(StoreLocation.LocalMachine, StoreName.My, "Service Cert Subject Name");

X509SecurityToken signatureToken = new X509SecurityToken(signCert);

MessageSignature signature = new MessageSignature(signatureToken); // <!-- IS THIS SAME AS THIS STEP IN WCF: secBE.EndpointSupportingTokenParameters.Signed.Add(new UsernameTokenParameters()) -->

proxy.RequestSoapContext.Security.Elements.Add(signature);

==========

Jak przekonwertować powyższy kod GPW 3.0 na WCF?

Author: John Saunders, 2013-02-07

2 answers

Udało mi się rozwiązać mój problem i połączyć się z bramką usług Web Service DataPower (IBM Xi50) za pomocą następujących rozwiązań WCF CustomBinding (CertificateOverTransport) i CustomCredentials (UsernameToken z password Digest, Client cert do uwierzytelniania w transporcie i Service cert do podpisywania treści wiadomości.) Nie jestem pewien, co dokładnie rozwiązało problem, ale oto mój działający kod WCF! Mam nadzieję, że pomoże to innym, którzy są w podobnej sytuacji jak ja.

Proszę sprawdzić, czy DataPower Xi50 gateway jest również skonfigurowany dla WCF. Od IBM: "podczas korzystania z BasicHttpBinding z SSL: możesz użyć parametru disable-ssl-cipher-check, aby wyłączyć sprawdzanie szyfrowania dla dowolnych twierdzeń transportowych. Nagłówek Basic Auth nie jest domyślnie obsługiwany w serwerze proxy Web services. W przeciwieństwie do WCF, nie jest to możliwe, ale jest to możliwe tylko w przypadku, gdy nie jest to możliwe."Po szczegóły, przejdź tutaj: https://publib.boulder.ibm.com/infocenter/ieduasst/v1r1m0/index.jsp?topic=/com.ibm.iea.wdatapower/wdatapower/1.0/xa35/380DataPowerWCFIntegration/player.html.

Upewnij się, że ustawiłeś poziom ochrony.Podpisz umowę serwisową, jeśli chcesz, aby treść wiadomości była podpisana (a nie zaszyfrowana.)

W przypadku tożsamości DNS, z którą miałem wcześniej problemy, mogłem teraz umieścić nazwę przedmiotu certyfikatu klienta - wcześniej to nie działało.

Nie mam żadnych konfiguracja w mojej sieci.config.

Oto Proxy używające CustomBinding:

private ClientProxy GetProxy()
{
    XXXServiceClient proxy = new XXXServiceClient(GetCustomBinding(), new EndpointAddress(new Uri("<<GatewayURLHere>>"), EndpointIdentity.CreateDnsIdentity("<<DNS or Client Cert Subject Name>>"), new AddressHeaderCollection()));
    proxy.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
    proxy.Endpoint.Behaviors.Add(new CustomCredentials(<clientCertHere>, <signingCertHere>));
    proxy.ClientCredentials.UserName.UserName = @"XXX";
    proxy.ClientCredentials.UserName.Password = "yyy";
    return proxy;
}

private Binding GetCustomBinding()
{
    TransportSecurityBindingElement secBE = SecurityBindingElement.CreateCertificateOverTransportBindingElement(MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);
    secBE.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never, RequireDerivedKeys = false });
    secBE.EnableUnsecuredResponse = true;
    secBE.IncludeTimestamp = true;
    TextMessageEncodingBindingElement textEncBE = new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressingAugust2004, System.Text.Encoding.UTF8);
    HttpsTransportBindingElement httpsBE = new HttpsTransportBindingElement();
    httpsBE.RequireClientCertificate = true;

    CustomBinding myBinding = new CustomBinding();
    myBinding.Elements.Add(secBE);
    myBinding.Elements.Add(textEncBE);
    myBinding.Elements.Add(httpsBE);

    return myBinding;
}

Oto moja klasa CustomCredentials, którą zebrałem z wielu źródeł, w tym wyżej wymienionego usernametoken library-sets client certificate for (mutual?) uwierzytelnianie w warstwie transportowej, certyfikat service/signing do podpisywania treści wiadomości i UsernameToken z hasłem w nagłówku SOAP:

using System;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Security;
using System.Text;

namespace XXX_WCF
{
    public class CustomCredentials : ClientCredentials
    {
        private X509Certificate2 clientAuthCert;
        private X509Certificate2 clientSigningCert;

        public CustomCredentials() : base() { }

        public CustomCredentials(CustomCredentials other)
            : base(other)
        {
            clientSigningCert = other.clientSigningCert;
            clientAuthCert = other.clientAuthCert;
        }

        protected override ClientCredentials CloneCore()
        {
            CustomCredentials scc = new CustomCredentials(this);
            return scc;
        }

        public CustomCredentials(X509Certificate2 ClientAuthCert, X509Certificate2 ClientSigningCert)
            : base()
        {
            clientAuthCert = ClientAuthCert;
            clientSigningCert = ClientSigningCert;
        }

        public X509Certificate2 ClientAuthCert
        {
            get { return clientAuthCert; }
            set { clientAuthCert = value; }
        }

        public X509Certificate2 ClientSigningCert
        {
            get { return clientSigningCert; }
            set { clientSigningCert = value; }
        }

        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new CustomTokenManager(this);
        }
    }

    public class CustomTokenManager : ClientCredentialsSecurityTokenManager
    {
        private CustomCredentials custCreds;

        public CustomTokenManager(CustomCredentials CustCreds)
            : base(CustCreds)
        {
            custCreds = CustCreds;
        }

        public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
        {
            if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
            {
                x509CustomSecurityTokenProvider prov;
                object temp = null;
                TransportSecurityBindingElement secBE = null;

                if (tokenRequirement.Properties.TryGetValue("http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/SecurityBindingElement", out temp))
                {
                    secBE = (TransportSecurityBindingElement)temp;
                }

                if (secBE == null)
                    prov = new x509CustomSecurityTokenProvider(custCreds.ClientAuthCert);
                else
                    prov = new x509CustomSecurityTokenProvider(custCreds.ClientSigningCert);
                return prov;
            }

            return base.CreateSecurityTokenProvider(tokenRequirement);
        }

        public override System.IdentityModel.Selectors.SecurityTokenSerializer CreateSecurityTokenSerializer(System.IdentityModel.Selectors.SecurityTokenVersion version)
        {
            return new CustomTokenSerializer(System.ServiceModel.Security.SecurityVersion.WSSecurity10);
        }
    }

    class x509CustomSecurityTokenProvider : SecurityTokenProvider
    {
        private X509Certificate2 clientCert;

        public x509CustomSecurityTokenProvider(X509Certificate2 cert)
            : base()
        {
            clientCert = cert;
        }

        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            return new X509SecurityToken(clientCert);
        }
    }

    public class CustomTokenSerializer : WSSecurityTokenSerializer
    {
        public CustomTokenSerializer(SecurityVersion sv) : base(sv) { }

        protected override void WriteTokenCore(System.Xml.XmlWriter writer, System.IdentityModel.Tokens.SecurityToken token)
        {
            if (writer == null)
            {
                throw new ArgumentNullException("writer");
            }
            if (token == null)
            {
                throw new ArgumentNullException("token");
            }

            if (token.GetType() == new UserNameSecurityToken("x", "y").GetType())
            {
                UserNameSecurityToken userToken = token as UserNameSecurityToken;

                if (userToken == null)
                {
                    throw new ArgumentNullException("userToken: " + token.ToString());
                }

                string tokennamespace = "o";

                DateTime created = DateTime.Now;
                string createdStr = created.ToString("yyyy-MM-ddThh:mm:ss.fffZ");
                string phrase = Guid.NewGuid().ToString();
                string nonce = GetSHA1String(phrase);
                string password = GetSHA1String(nonce + createdStr + userToken.Password);
                //string password = userToken.Password;

                writer.WriteStartElement(tokennamespace, "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
                writer.WriteAttributeString("u", "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", token.Id);
                writer.WriteElementString(tokennamespace, "Username", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", userToken.UserName);
                writer.WriteStartElement(tokennamespace, "Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
                writer.WriteAttributeString("Type", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest");
                writer.WriteValue(password);
                writer.WriteEndElement();
                writer.WriteStartElement(tokennamespace, "Nonce", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd");
                writer.WriteAttributeString("EncodingType", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary");
                writer.WriteValue(nonce);
                writer.WriteEndElement();
                writer.WriteElementString(tokennamespace, "Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", createdStr);
                writer.WriteEndElement();
                writer.Flush();
            }
            else
            {
                base.WriteTokenCore(writer, token);
            }
        }

        protected string GetSHA1String(string phrase)
        {
            SHA1CryptoServiceProvider sha1Hasher = new SHA1CryptoServiceProvider();
            byte[] hashedDataBytes = sha1Hasher.ComputeHash(Encoding.UTF8.GetBytes(phrase));
            return Convert.ToBase64String(hashedDataBytes);
        }
    }//CustomTokenSerializer
}
Powodzenia!
 6
Author: Jawad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-11 22:07:59

Przejrzałem Twój kod i wygląda na poprawny. Istnieje niewielka różnica w wiadomościach GPW i WCF soap, ale różnica dotyczy tylko sposobu, w jaki certyfikat używany do podpisania wiadomości jest określany.

Myślę, że głównym problemem jest niewłaściwe użycie certyfikatów. Korzystasz zarówno z transportu, jak i wzajemnego bezpieczeństwa wiadomości. Teoretycznie wymaga to czterech certyfikatów. Potrzebujesz

  • Service certificate for transport security - certyfikat ten jest używany przez serwer do budowy SSL połączenie. Aby pomyślnie zbudować połączenie, klient musi zaufać certyfikatowi (musisz zaufać organowi, który wydał certyfikat lub certyfikat serwera musi być umieszczony w sklepie zaufanych osób).
  • Client certificate for transport security - ten certyfikat służy do uwierzytelniania klienta na serwerze na poziomie transportu-musisz mieć certyfikat i jego klucz prywatny w swoim osobistym sklepie
  • Service certificate for message security-ten certyfikat służy do szyfrowania żądania i podpisywania odpowiedzi (w przypadku WS-Security 1.0). Musisz mieć ten certyfikat gdzieś na swoim komputerze(od Ciebie zależy, w jakiej lokalizacji zostanie on załadowany).
  • Client certificate for message security - certyfikat ten służy do szyfrowania odpowiedzi i podpisywania żądań (w przypadku WS-Security 1.0). Musisz mieć ten certyfikat i jego klucz prywatny gdzieś na komputerze (od Ciebie zależy, w jakiej lokalizacji zostanie użyty załadować certyfikat).

Wygląda na to, że masz tylko dwa certyfikaty-jednego klienta i jednego serwera. W takim przypadku powinny być prawdopodobnie używane zarówno do zabezpieczenia transportu, jak i wiadomości. Ale tu pojawia się ciekawy problem - twój certyfikat" podpisujący " po stronie klienta w przykładzie GPW jest faktycznie certyfikatem serwisu. Jeśli tak jest naprawdę, oznacza to, że Klient musi mieć dostęp do klucza prywatnego serwera - to nigdy nie powinno się zdarzyć. To jest najgorsze naruszenie infrastruktury PKI. Infrastruktura PKI opiera się na zaufaniu do organów certyfikujących i zabezpieczeniu kluczy prywatnych, w których każdy uczestnik ma swój własny klucz prywatny niedostępny dla nikogo innego. Udostępnianie kluczy prywatnych zmniejsza bezpieczeństwo. W najgorszym przypadku może to być równoznaczne z brakiem bezpieczeństwa, ponieważ każdy, kto ma dostęp do klucza prywatnego, może przechwycić komunikację lub fałszywy podpis na wiadomości.

Jeśli mam rację, powinieneś użyć GPW 3.0 i być z tego zadowolony. Zmuszając WCF do używania innego certyfikatu klienta dla HTTPS i bezpieczeństwo wiadomości może być dość trudne. Masz jedną właściwość ClientCertificate, ale musisz załadować inny certyfikat dla HTTPS i bezpieczeństwa wiadomości. Wymaga utworzenia custom ClientCredentials z dwoma właściwościami i custom SecurityTokenManager, aby zwrócić poprawnego dostawcę certyfikatu (poprzez implementację dla każdego użycia (taka jest teoria - nigdy nie próbowałem).

Btw. twój problem z EndpointIdentity polega na tym, że Twoja usługa jest narażona na niektóre DNS i jeśli temat w certyfikacie usługi (który musisz utworzyć nową tożsamość DNS dla punktu końcowego. W przeciwnym razie WCF nie ufa certyfikatowi. Certyfikat serwera powinien być wystawiony z pasującą do tematu nazwą DNS używaną do uzyskania dostępu do serwera.

 2
Author: Ladislav Mrnka,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-10 18:38:01