2013-02-06 17 views
11

Zusammenfassung: ich auf einem .NET 4.0 WCF-Client arbeite einen Web-Service zu konsumieren (DataPower, Java-Dienst am anderen Ende) mit SOAP 1.1 und WS-Security 1.0. Der WCF-Client muss ein Clientzertifikat für die gegenseitige Authentifizierung auf der Transportschicht implementieren. Der Nachrichtentext muss mit einem separaten Dienst/Signaturzertifikat signiert werden. Der SOAP-Header muss außerdem einen Benutzernamen-Token mit Passwort-Digest enthalten und Nonce- und Created-Tags enthalten.WCF SOAP 1.1 und WS-Security 1.0, Client-Zertifikat Transport Auth, Service cert für Nachrichtentext Unterschrift, Username, Passwort Digest, Nonce

Ich bin in der Lage, diesen Web-Service mit WSE 3.0 mit BasicHTTPBinding zu konsumieren. Aber ich war bisher nicht erfolgreich bei der Implementierung der gleichen mit WCF mit WSHttpBinding oder CustomBinding. Ich habe alle Sicherheitsbindungselemente ausprobiert und bisher kein Glück gehabt.

Ich benutze auch die usernettoken Bibliothek von hier (http://blogs.msdn.com/b/aszego/archive/2010/06/24/usernametoken-profile-vs-wcf.aspx), so kann ich Passwort Digest/Nonce/erstellt in der UsernameToken in SOAP-Header.

Ich bin derzeit mit SecurityBindingElement.CreateMutualCertificateBindingElement ich auch mehr andere, wie AsymmetricSecurityBindingElement, TransportSecurityBindingElement usw. (in Code auf Kommentar unten) versucht habe

CERTS: Ich habe sowohl das Client-Zertifikat und Service-Zertifikat loaded in den Zertifikatsspeicher mit MMC (Ich bin unter Windows 7 btw.) Sowohl das Client-Zertifikat als auch das Service-Zertifikat haben private Schlüssel. Ich habe beide PFX-Dateien in LocalMachine/Personal, LocalMachine/Root und LocalMachine/TrustedPeople geladen. Ich habe auch den FindPrivateKey/ICACLS ausgeführt, um dem Konto "IIS App Pool/DefaultAppPool" die Berechtigung zu erteilen. Obwohl nichts davon von Bedeutung sein sollte, da ich den WSE 3.0-Code von meinem Computer ausführen kann, und es ohne Cert-Probleme funktioniert.

Befehle ausführen:

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 PROBLEM: ich zur Zeit eine „etablieren kann nicht sicheren Kanal für SSL/TLS mit Autorität 'x.x.com'“ erhält Nachricht zurück von dem Datapower-Gateway. Ich denke, dass dies möglicherweise daran liegt, dass das Gateway das Dienstzertifikat übernimmt und dieses für die Clientauthentifizierung verwendet, anstatt das Clientzertifikat zu verwenden, das ich sende. Ich sage das, weil ich, wenn ich die DNS-Identität für den Endpunkt nicht spezifiziere, eine Nachricht zurückbekomme, die besagt, dass das Gateway die DNS-Identität als "{Name des Dienstes/Signaturzertifikat}" erwartet.

Hier ist die SOAP-Anforderung, die von WCF generiert wird, die den obigen Fehler gibt. Die WCF-SOAP-Anforderung ähnelt der WSE-SOAP-Anforderung sehr. Der obige Fehler tritt wahrscheinlich aufgrund des Cert-Problems auf der SSL/Transport-Schicht auf.

WCF SOAP-Anforderung:

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

WSE 3.0 SOAP-Anforderung (das funktioniert):

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

Hier werden alle der Konfiguration ist, lassen Sie es mich wissen, was ich tue, falsch!

WCF web.config: Ich habe alles aus der web.config entfernt, wie ich den ganzen config in Code tue.

WCF config in Code:

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

Ich habe hinzugefügt ProtectionLevel.Sign auf der Servicecontract und OperationContracts da ich nur den Nachrichtentext zu unterzeichnen. Ich bin jedoch noch nicht soweit gekommen, um es zu überprüfen.

[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) { 

Ich habe die im Anschluss an die unten an web.config hinzugefügt Protokollierung der gesamten Seife einschließlich pii Daten

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

zu ermöglichen ============== =

WSE 3.0 (Arbeits config und Code): 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> 

... und WSE3 Code:

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

==========

Also, wie konvertiere ich die oben WSE 3.0-Code zu WCF?

+0

Warum möchten Sie in erster Linie konvertieren, wenn Sie Arbeitslösung in WSE 3.0 haben? Zu Ihrer Frage im WSE-Code: Kein Endpunkt, der Token unterstützt, ist ein völlig anderes Feature. Übrigens. Brauchen Sie auch WS-Adressierung (wird im WSE-Client verwendet). –

+1

Wir haben einen Kunden, der darauf besteht, dass wir ihnen die WCF-Version zur Verfügung stellen, da sie sagen, dass WSE 3.0 "alte Technologie" ist ... Ich glaube nicht Adressierung, aber sicher nur für Konformität, wir werden es behalten Soap11WSAddressing10 – Jawad

+0

werde ich Sehen Sie sich dieses Problem während des Wochenendes an - ich interessiere mich selbst, bin aber darauf vorbereitet, dass es für WCF keine Lösung gibt (außer Sicherheit manuell schreiben) - WCF enthält nicht alle Optionen, die zuvor in WSE 3.0 verfügbar waren. Aus Neugier: Haben Sie WS-SecurityPolicy (normalerweise Teil der WSDL- oder Java-Servicebereitstellung) oder eine Beispielanforderung und -antwort für den Service (nicht die von WSE, sondern vom Java-Client generierten)? –

Antwort

6

Ich konnte mein Problem beheben und eine Verbindung zum DataPower (IBM Xi50) -Webdienst-Gateway mithilfe des folgenden WCF-CustomB herstellen Indeed (CertificateOverTransport) und CustomCredentials (UsernameToken mit Password Digest, Client-Zertifikat für Transport-Authentifizierung und Service-Zertifikat für Signatur des Nachrichtentextes). Ich bin mir nicht sicher, was genau das Problem gelöst hat, aber hier ist mein funktionierender WCF-Code! Ich hoffe, das hilft anderen, die in einer ähnlichen Situation sind wie ich.

Überprüfen Sie, ob das DataPower Xi50-Gateway auch für WCF konfiguriert ist. Von IBM: "Wenn Sie BasicHttpBinding mit SSL verwenden: Sie können den Parameter disable-ssl-cipher-check verwenden, um Verschlüsselungsüberprüfungen für alle TransportBinding-Assertionen zu deaktivieren. Der Basic Auth-Header wird standardmäßig nicht im Web-Services-Proxy unterstützt Eine On-Error-Regel zum Eingeben des WWW-Authenticate-Headers ist erforderlich, um mit WCF zu interagieren. " Für Details, gehen Sie hier: https://publib.boulder.ibm.com/infocenter/ieduasst/v1r1m0/index.jsp?topic=/com.ibm.iea.wdatapower/wdatapower/1.0/xa35/380DataPowerWCFIntegration/player.html.

Stellen Sie sicher, ProtectionLevel.Sign auf Ihrem Servicevertrag festgelegt haben, wenn Sie Ihren Nachrichtentext nur unterzeichnet werden soll (und nicht verschlüsselt.)

Für DNS-Identität, die ich mit früheren Ausgaben hatte, war ich nun in der Lage zu setze meinen Client-Zertifikats-Subjektnamen - früher würde das nicht funktionieren.

Ich habe keine Konfiguration in meiner web.config.

Hier wird der Proxy mit Custom:

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

Hier ist meine CustomCredentials Klasse, die ich zusammen aus mehreren Quellen stellen, die die oben Usernamen Bibliothek erwähnt - setzt Client-Zertifikat für (gegenseitige?) Authentifizierung auf der Transportschicht , Service/Zertifikat Unterzeichnung der Nachrichtentext und Username mit Passwort Digest im SOAP-Header für die Anmeldung:

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 
} 

Viel Glück!

2

Ich ging Ihren Code durch und es sieht korrekt aus. Es gibt einen kleinen Unterschied in der WSE- und WCF-Soap-Nachricht, aber der Unterschied besteht nur in der Art und Weise, auf die auf das zum Signieren der Nachricht verwendete Zertifikat verwiesen wird.

Ich denke, das Kernproblem hier ist die falsche Verwendung von Zertifikaten. Sie verwenden sowohl die Transport- als auch die Nachrichtensicherheit. In der Theorie benötigt dies vier Zertifikate. Sie benötigen

  • Dienstzertifikat für Transportsicherheit - Dieses Zertifikat wird vom Server zum Erstellen einer SSL-Verbindung verwendet. Um die Verbindung erfolgreich aufzubauen, muss der Client dem Zertifikat vertrauen (entweder müssen Sie der Autorität vertrauen, die das Zertifikat ausgestellt hat, oder das Serverzertifikat muss in Ihrem vertrauenswürdigen People Store gespeichert sein).
  • Client-Zertifikat für Transportsicherheit - Dieses Zertifikat wird verwendet, um den Client auf dem Server auf Transportebene zu authentifizieren. Sie müssen das Zertifikat und seinen privaten Schlüssel in Ihrem persönlichen Speicher haben
  • Servicezertifikat für Nachrichtensicherheit - Dieses Zertifikat wird zum Verschlüsseln verwendet Anfrage und Signatur Antwort (wenn WS-Security 1.0) verwendet wird. Sie müssen dieses Zertifikat irgendwo auf Ihrem Rechner haben (es liegt an Ihnen, welcher Standort zum Laden des Zertifikats verwendet wird).
  • Client-Zertifikat für Nachrichtensicherheit - Dieses Zertifikat wird zum Verschlüsseln der Antwort- und Signaturanforderung (bei Verwendung von WS-Security 1.0) verwendet. Sie müssen dieses Zertifikat und seinen privaten Schlüssel irgendwo auf Ihrem Computer haben (es liegt an Ihnen, welcher Speicherort zum Laden des Zertifikats verwendet wird).

Es sieht so aus, als ob Sie nur zwei Zertifikate haben - einen Client und einen Server. In diesem Fall sollten sie wahrscheinlich sowohl für die Transport- als auch für die Nachrichtensicherheit verwendet werden. Aber hier kommt interessantes Problem - Ihr "Signing" -Zertifikat auf der Clientseite im WSE-Beispiel ist eigentlich ein Dienstzertifikat. Wenn dies wirklich der Fall ist, bedeutet dies, dass der Client Zugriff auf den privaten Schlüssel des Servers haben muss - das sollte niemals passieren. Das ist die schlimmste Verletzung der PKI-Infrastruktur. Die PKI-Infrastruktur basiert auf der Vertrauenswürdigkeit der Zertifizierungsstellen und auf der Sicherung privater Schlüssel, wobei jeder Teilnehmer seinen eigenen privaten Schlüssel hat, auf den sonst niemand Zugriff hat. Die Freigabe privater Schlüssel verringert die Sicherheit. Im schlimmsten Fall kann es überhaupt keine Sicherheit geben, da jeder mit Zugriff auf den privaten Schlüssel die Kommunikation oder gefälschte Signatur auf der Nachricht abfangen kann.

Wenn ich recht habe, sollten Sie WSE 3.0 verwenden und damit zufrieden sein. WCF nur zwingen, verschiedene Client-Zertifikate für HTTPS und Nachrichtensicherheit zu verwenden, kann ziemlich schwierig sein. Sie haben eine einzelne ClientCertificate-Eigenschaft, aber Sie müssen ein anderes Zertifikat für HTTPS und Nachrichtensicherheit laden. Es erfordert die Erstellung von eigenen ClientCredentials mit zwei Eigenschaften und benutzerdefinierten SecurityTokenManager richtigen Zertifikat Provider zurückzukehren (durch für jede Nutzung der Umsetzung (das ist eine Theorie. - Ich habe es nie versucht)

Btw Ihr Problem mit EndpointIdentity basiert auf der Tatsache. dass Ihr Dienst auf einem DNS verfügbar ist und der Betreff im Dienstzertifikat (in Ihrem Fall auch das Signaturzertifikat) anders ist, müssen Sie eine neue DNS-Identität für Ihren Endpunkt erstellen. Andernfalls wird WCF dem Zertifikat nicht vertrauen Der Name des Subjekts stimmt mit dem für den Zugriff auf den Server verwendeten DNS-Namen überein.

+0

Vielen Dank für Ihr Feedback und Hilfe bei diesem Ladislav. Mein Problem ist gelöst. Ich bin mir nicht sicher, was es gelöst hat. Ich habe das unten auf der Website von IBM gefunden und es mit unserer IT-Gateway-Infrastrukturgruppe zusammengeführt. Sie sagen, dass keine Änderungen an DataPower vorgenommen wurden, aber es hat heute Morgen angefangen zu arbeiten !!! Ich werde die Frage mit dem Arbeitscode zusammen mit der CustomCredential-Klasse beantworten, die ich zusammengestellt habe. Vielen Dank! – 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 Wann Verwenden von BasicHttpBinding mit SSL: Sie können den Parameter disable-ssl-cipher-check verwenden, um Verschlüsselungsüberprüfungen für alle TransportBinding-Assertionen zu deaktivieren. Der Basic Auth Header wird standardmäßig nicht im Web-Services-Proxy unterstützt. Eine benutzerdefinierte Konfiguration einer On-Error-Regel, um den WWW-Authenticate-Header zu injizieren, ist erforderlich, um mit WCF zu interagieren. – Jawad