2013-02-06 23 views
11

Podsumowanie: pracuję na .NET 4.0 klienta WCF zużyje usługi sieci web (DataPower, usługa Java na drugim końcu) przy użyciu SOAP 1.1 i WS-Security 1.0. Klient WCF musi wdrożyć certyfikat klienta do wzajemnego uwierzytelniania w warstwie transportu. Treść wiadomości musi być podpisana przy użyciu oddzielnego certyfikatu usługi/podpisu. Nagłówek SOAP musi również zawierać Token użytkownika z weryfikacją hasła i zawierać znaczniki "nonce" i "utworzone".WCF SOAP 1.1 i WS-Security 1.0, transport certyfikat klienta auth, cert serwis do podpisu treść wiadomości, UsernameToken, Hasło Digest, nonce

Jestem w stanie korzystać z tej usługi internetowej przy użyciu programu WSE 3.0 z BasicHTTPBinding. Ale do tej pory nie udało mi się wdrożyć tego samego z WCF przy użyciu WSHttpBinding lub CustomBinding. Wypróbowałem wszystkie elementy wiążące zabezpieczenia i do tej pory nie miałem szczęścia.

Używam również 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 nagłówku SOAP.

Obecnie używam SecurityBindingElement.CreateMutualCertificateBindingElement Próbowałem również kilka innych, takich jak AsymmetricSecurityBindingElement, TransportSecurityBindingElement etc (wypowiedziało się w kodzie poniżej)

CERT: mam zarówno certyfikat certyfikat klienta i serwis załadowane do magazynu certyfikatów za pomocą programu MMC (w systemie Windows 7 btw.) Zarówno certyfikat klienta, jak i certyfikat 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 nadać uprawnienia do konta "Pula aplikacji IIS/DefaultAppPool". Chociaż nic z tego nie powinno mieć znaczenia, ponieważ mogę uruchomić kod WSE 3.0 z mojego komputera i działa bez żadnych problemów z certyfikatami.

komend:

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

WCF numerze: Obecnie pojawia się komunikat „Nie można ustanowić bezpiecznego kanału dla SSL/TLS z organem 'x.x.com'” z powrotem od bramy DataPower. Sądzę, że może to być spowodowane tym, że brama pobiera 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ślam 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 przykładowe żądanie SOAP wygenerowane przez WCF, które podaje powyższy błąd. Żądanie SOAP WCF wygląda bardzo podobnie do żądania SOAP na GPW. Powyższy błąd najprawdopodobniej występuje z powodu problemu z certyfikatem w warstwie SSL/Transport.

WCF żądanie SOAP:

<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.Żądanie 0 SOAP (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> 

Oto cały config, proszę dać mi znać, co robię źle!

WCF web.config: Usunąłem wszystko z pliku web.config, ponieważ robię całą konfigurację w kodzie.

config 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 ProtectionLevel.Sign na ServiceContract i OperationContracts odkąd tylko trzeba podpisać treści wiadomości. Jednak nie udało mi się jeszcze tego 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 następujące niżej do web.config umożliwienia rejestrowania całego mydła w tym danych 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 (config i kod robocza): web.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> 

... i WSE3 Kod:

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

==========

Więc jak mogę przekształcić powyższy kod GPW 3,0 do WCF?

+0

Dlaczego chcesz dokonać konwersji przede wszystkim, gdy masz działające rozwiązanie na GPW 3.0? Na twoje pytanie w kodzie GPW: Brak żetonów wspierających punkt końcowy to zupełnie inna funkcja. Btw. czy potrzebujesz również WS-Addressing (jest on używany w kliencie GPW). –

+1

Mamy klienta, który nalega, abyśmy dostarczyli mu wersję WCF, ponieważ mówią, że GPW 3.0 to "stara technologia" ... Nie sądzę, żebyśmy się zajmowali sprawami, ale pewnie dla zgodności, zachowamy to Soap11WSAddressing10 – Jawad

+0

Będę spójrz na ten problem podczas weekendu - jestem zainteresowany sam, ale bądź przygotowany, że może nie być rozwiązania dla WCF (z wyjątkiem ręcznego pisania zabezpieczeń) - WCF nie zawiera wszystkich opcji dostępnych wcześniej na GPW 3.0. Z ciekawości: czy masz WS-SecurityPolicy (zwykle część wdrożenia WSDL lub Java) lub przykładowe żądanie i odpowiedź dla usługi (nie te generowane przez GPW, ale klienta Java)? –

Odpowiedz

6

Udało mi się rozwiązać problem i połączyć się z bramą usługi sieciowej DataPower (IBM Xi50) przy użyciu następującego CustomB WCF inding (CertificateOverTransport) i CustomCredentials (UsernameToken with Password Digest, certyfikat klienta do uwierzytelniania transportu i certyfikat usługi do podpisu treści wiadomości). Nie jestem pewien, co dokładnie naprawił problem, ale oto mój działający kod WCF! Mam nadzieję, że pomoże to innym osobom znajdującym się w podobnej sytuacji jak ja.

Proszę sprawdzić, czy bramka DataPower Xi50 jest również skonfigurowana dla WCF. Firma IBM: "Podczas korzystania z BasicHttpBinding z protokołem SSL: Można wyłączyć parametr sprawdzania szyfrowania SSL w celu wyłączenia sprawdzania szyfrowania dla jakichkolwiek asercji TransportBinding Podstawowy nagłówek uwierzytelniania nie jest domyślnie obsługiwany w serwerze proxy usług WWW. reguła on-error do wstrzykiwania nagłówka WWW-Authenticate jest wymagana do współdziałania z WCF. " Aby uzyskać szczegółowe informacje, 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 ustawiony ProtectionLevel.Sign na umowy o świadczenie usług, jeżeli chcesz, aby Twoje ciało wiadomość podpisana tylko (i nie zaszyfrowane.)

tożsamości DNS, którą miałem problemy z wcześniej byłem już w stanie wpisz nazwę podmiotu certyfikatu klienta - wcześniej to by nie działało.

Nie mam żadnej konfiguracji w moim pliku web.config.

Oto Proxy za pomocą 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 że ułożyła z wielu źródeł, w tym ww bibliotece UsernameToken - ustawia certyfikat klienta dla (wzajemne?) Uwierzytelniania w warstwie transportowej , certyfikat usługi/podpisu do podpisania treści wiadomości i UsernameToken z hasłem Digest 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!

2

Przeszedłem twój kod i wygląda on poprawnie. Istnieje niewielka różnica w komunikatach mydlanych GPW i WCF, ale różnica dotyczy tylko tego, w jaki sposób odwołuje się certyfikat używany do podpisywania wiadomości.

Myślę, że kluczową kwestią jest niewłaściwe wykorzystanie certyfikatów. Korzystasz z wzajemnego bezpieczeństwa zarówno transportu, jak i wiadomości. Teoretycznie wymaga to czterech certyfikatów. Potrzebujesz:

  • Certyfikat usługi dla bezpieczeństwa transportu - ten certyfikat jest używany przez serwer do budowania połączenia SSL. Aby pomyślnie zbudować połączenie, klient musi ufać certyfikatowi (musisz albo zaufać organowi, który wystawił certyfikat, albo certyfikat serwera musi zostać umieszczony w twoim magazynie zaufanych osób).
  • Certyfikat klienta dla bezpieczeństwa transportu - ten certyfikat służy do uwierzytelnienia klienta na serwerze na poziomie transportu - musisz mieć certyfikat i jego prywatny klucz w swoim osobistym sklepie
  • Certyfikat usługi dla zabezpieczenia wiadomości - ten certyfikat służy do szyfrowania żądanie i odpowiedź podpisu (gdy WS-Security 1.0) jest używany. Musisz mieć ten certyfikat gdzieś na swoim komputerze (od Ciebie zależy jaka lokalizacja zostanie użyta do załadowania certyfikatu).
  • Certyfikat klienta dla zabezpieczenia wiadomości - ten certyfikat służy do szyfrowania odpowiedzi i żądania podpisania (gdy używany jest WS-Security 1.0). Musisz mieć ten certyfikat i jego prywatny klucz gdzieś na swoim komputerze (od ciebie zależy, jaka lokalizacja zostanie użyta do załadowania certyfikatu).

Wygląda na to, że masz tylko dwa certyfikaty - jeden klient i jeden serwer. W takim przypadku powinny one prawdopodobnie zostać użyte zarówno do zabezpieczenia transportu, jak i wiadomości. Ale tutaj pojawia się ciekawy problem - twój certyfikat "podpisu" po stronie klienta na przykładzie GPW jest faktycznie certyfikatem usługi. Jeśli tak rzeczywiście jest, oznacza to, że klient musi mieć dostęp do prywatnego klucza serwera - to nigdy nie powinno się zdarzyć. To jest najgorsze naruszenie infrastruktury PKI. Infrastruktura PKI opiera się na zaufaniu do urzędów certyfikacji i zabezpieczaniu kluczy prywatnych, w których każdy uczestnik ma swój prywatny klucz niedostępny dla nikogo innego. Udostępnianie kluczy prywatnych zmniejsza bezpieczeństwo. W najgorszym przypadku może to oznaczać brak zabezpieczeń, ponieważ każdy, kto ma dostęp do klucza prywatnego, może przechwycić komunikację lub fałszywy podpis w wiadomości.

Jeśli mam rację, powinieneś używać WSE 3.0 i być z tego zadowolony. Zmuszanie 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 protokołu HTTPS i zabezpieczenia wiadomości. Wymaga to tworzenia własnych ClientCredentials z dwóch właściwości i niestandardowe SecurityTokenManager powrót odpowiedniego dostawcę certyfikatu (poprzez wdrożenie dla każdego użycia (to jest teoria. - Nigdy nie próbowałem)

Btw problem z EndpointIdentity jest oparta na faktach. że twoja usługa jest narażona na niektóre DNS i jeśli podmiot w certyfikacie usługi (który jest w twoim przypadku również podpisywanie certyfikatu) jest inny, musisz utworzyć nową tożsamość DNS dla swojego punktu końcowego.W przeciwnym razie WCF nie będzie ufał certyfikatowi. zostać wydane z podmiotem pasującą do nazwy DNS używanej do uzyskania dostępu do serwera:

+0

Dzięki za opinie i pomoc w tym Ladislav. Mój problem został rozwiązany. Nie jestem pewien, co to rozwiązało. Znalazłem poniższe informacje na stronie IBM i przedstawiłem je w naszej grupie infrastruktury bramy IT. Mówią, że nie wprowadzono żadnych zmian w DataPower, ale zaczęło działać dziś rano !!! Zamierzam odpowiedzieć na pytanie z roboczym kodem wraz z klasą CustomCredential, którą przygotowałem. Dzięki! – Jawad

+0

IBM: https://publib.boulder.ibm.com/infocenter/ieduasst/v1r1m0/index.jsp?topic=/com.ibm.iea.wdatapower/wdatapower/1.0/xa35/380DataPowerWCFIntegration/player.html Kiedy Używanie BasicHttpBinding z protokołem SSL: Możesz wyłączyć parametr sprawdzania szyfrowania dla wszystkich potwierdzeń TransportBinding. Podstawowy nagłówek uwierzytelniania nie jest domyślnie obsługiwany w proxy usług WWW. Niestandardowa konfiguracja reguły błędu do wstrzykiwania nagłówka WWW-Authenticate jest wymagana do współdziałania z WCF. – Jawad