locked
clientCredentialType UserName auch ohne Zertifikat möglich? RRS feed

  • Frage

  • Hallo,

    ich versuche seit 3 Tagen eine einfache WCF Anwendung mit clientCredentialType="UserName" auf die Beine zu stellen.
    Ich hätte gedacht das ist nicht soo schwer, aber weit gefehlt.
    Immerhin habe ich es nach einigen Schwierigkeiten geschafft die WCF-Kommunikation ohne "UserName" zum spielen zu bringen.
    Jetzt wollte ich "nur noch" eine Autorisierung über username und passwort einbauen.
    Das habe ich auch soweit geschafft, es hapert jetzt am Zertifikat.

    Ich habe die Anleitung befolgt - bis zum Punkt "Step 4". Das Tool FindPrivateKey endet bei mir immer mit der Meldung "FindPrivateKey failed for the following reason: Unable to obtain private key file name".
    Dann habe ich in meiner Verzweiflung die Private Key Files selbst gesucht, unter "C:\Users\All Users\Microsoft\Crypto\RSA\MachineKeys" gefunden
    und als Admin jedem File dort für alle User alle Rechte gegeben - trotzdem verabschiedet sich mein WCF-Service beim Starten mit den Worten "Das Zertifikat \"CN=TempCA\" muss einen privaten Schlüssel aufweisen, der den Schlüsselaustausch unterstützt. Der Prozess muss über Zugriffsrechte für den privaten Schlüssel verfügen."
    Dann wollte ich den gefundenen Private Keys mit dem cacls.exe Tool die passenden Rechte verleihen:
    cacls.exe b1d9260a6cee3508a4ea0c985b33d498_03e073d5-1aaf-41c6-8a7e-44acf83fec4a /E /G "NETWORK SERVICE":R

    Ergebnis:
    "Zuordnungen von Kontennamen und Sicherheitskennungen wurden nicht durchgeführt." - Arrgh - was kann ich denn noch tun?
    Googeln nach obiger Fehlermeldung brachte auch nichts passenden.

    <Frust>Hätte ich doch das alte Windows-SDK bemüht, dann wäre ich schon lange fertig</Frust>

    Kann mir da jemand noch einen Tipp geben, was ich noch probieren kann?

    Oder ist es evtl. doch möglich bei Autentifizierung mit Benutzername+PW ganz auf Zertifikate zu verzichten?


    Vielen Dank im Voraus,
    glibber


    VS2008 SP1
    .NET 3.5
    C#
    Windows 7 RC
    Dienstag, 10. November 2009 15:02

Antworten


  • Hallo,

    jetzt hab' ich endlich geschafft - war auch eine schwierige Geburt.

    Also:
    makecert -sr LocalMachine -ss My -pe -sky exchange -n "CN=TestCert1" TestCert1.cer -a sha1

    dann nichts weiter, der Key ist dann schon im Store und braucht nicht manuell importiert zu werden.

    Service-Konfig:
              <serviceCredentials>
                <serviceCertificate findValue="TestCert1" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
                <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyFirstWcfService.CustomUserNameValidator, MyFirstWcfService" />
                <clientCertificate>
                  <authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" />
                </clientCertificate>
              </serviceCredentials>


    Client-Konfig:
              <clientCredentials>
                <serviceCertificate>
                  <authentication certificateValidationMode="Custom" customCertificateValidatorType="MyFirstWcfClient.MyCertValidator, MyFirstWcfClient"/>
                </serviceCertificate>
              </clientCredentials>


    Client-Code:
    namespace MyFirstWcfClient
    {
        class MyCertValidator : X509CertificateValidator
        {
            public override void Validate(X509Certificate2 certificate)
            {
                // Check that there is a certificate.
                if (certificate == null)
                {
                    throw new ArgumentNullException("certificate");
                }

                // TODO: do all the checks
            }
        }
    }

    So funktioniert es. :-))


    enlightened by dotnetpro (Ausgabe 9.2009)


    (Da zeigt sich wieder, dass man nicht den ganzen Tag vor dem Bildschirm hocken, sondern ab- und an mal ein Buch oder eine Zeitschrift zur Hand nehmen sollte!)

    Schönen Abend.

    glibber
    • Als Antwort markiert glibber Mittwoch, 11. November 2009 18:48
    Mittwoch, 11. November 2009 18:47

Alle Antworten

  • hallo,
    jaja, dies ist herrlich einfach... bis irgendwo ein kleiner Fehler sich einschleicht und das ganze Kartenhaus zusammenbricht... Ich hatte auch meine liebe Not damit.
    Leider sind Fehlermeldungen auch nicht immer aussagekräfig. (Siehe meine Frage in diesem Forum)

    Aber eine leichte Antwort gibt es dazu leider nicht:
    tritt der Fehler auf der Client oder Serverseite auf?

    Ich kann Dir hier soviel raten:

    falls du es noch nicht benutzt, lade dir den Microsoft Service Trace Viewer (SvcTraceViewer.exe) runter und analysiere was passiert.

    Dazu eine Anleitung:
    http://msdn.microsoft.com/de-de/library/ms732023.aspx

    Schönen Abend noch.



    Dienstag, 10. November 2009 17:31
  • hallo Lucien81,

    danke für den Tipp mit dem SvcTraceViewer.exe. Das werde ich heute ausprobieren.

    Der Fehler "Das Zertifikat \"CN=TempCA\" muss einen privaten Schlüssel aufweisen, der den Schlüsselaustausch unterstützt. Der Prozess muss über Zugriffsrechte für den privaten Schlüssel verfügen. " tritt auf Serverseite auf - beim Starten des WCF Servers.

    Deshalb mein (erfolgloser) Versuch die Zertifikate mit Leserechten für alle und jeden zu versehen.

    In Deinem Thread schriebst Du dass Du die Codeplex-Anleitung nachvollziehen konntest.
    Du konntest also auch die Tools "FindPrivateKey" und "cacls" problemlos nutzen?
    Falls ja, unter welchen System entwickelst Du?
    Ich habe Windows 7 (RC) und da laufen diese Tools in Fehler.

    Grüße,
    glibber
    Mittwoch, 11. November 2009 08:22
  • Hallo,

    auch der SvcTraceViewer zeigt den gleichen Fehler: "Das Zertifikat "CN=tempCert" muss einen privaten Schlüssel aufweisen, der den Schlüsselaustausch unterstützt. Der Prozess muss über Zugriffsrechte für den privaten Schlüssel verfügen."

    Meine Zertifikate scheinen tatsächlich keinen private Key zu haben, weil auch das "FindPrivateKey" Tool einen gleichlautenden Fehler ausgibt (allerdings erst nachdem ich die Fehlerbehandlung im Fraglichen Code-Abschnitt etwas aufgepeppt habe).

    Frage: wie kann ich denn nun ein Zertifikat erzeugen, dass einen Privaten Schlüssel enthält?
    Mit
    makecert -n "CN=TempCA" -r -sv TempCA.pvk TempCA.cer
    und
    makecert -sk SignedByCA -iv TempCA.pvk -n "CN=SignedByCA" -ic TempCA.cer SignedByCA.cer -sr currentuser -ss My
    kann ich zwar Zertifikate Erzeugen und diese dann mit dem im Zertifikats-SnapIn der MMC importieren aber dann haben diese keinen privaten Schlüssel mehr. :-(

    Weiss da jemand Rat?

    glibber

    Mittwoch, 11. November 2009 12:26
  • Hallo,

    ich bin wieder ein Stück weiter.
    Was ich bis jetzt gemacht habe:

        1.) Stammzertifikat erzeugen:
            "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\makecert" -n "CN=tmpCA2" -r -sv tmpCA2.pvk tmpCA2.cer

        2.) Abhängiges Zertifikat erzeugen:
            "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\makecert" -sk SignedByCA2 -iv tmpCA2.pvk -n "CN=SignedByCA2" -ic tmpCA2.cer SignedByCA2.cer -sr currentuser -ss My

        3.) Speicherort herausfinden:
            "C:\Samples\WCFWFCardSpace\WCF\Tools\FindPrivateKey\CS\bin\Release\FindPrivateKey.exe" My localMachine -n "CN=SignedByCA2"

            Output:
            Private key directory:
            C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000
            Private key file name:
            c33e4456f7982cd9fb67f0b3307d2c55_03e073d5-1aaf-41c6-8a7e-44acf83fec4a


        4.) Berechtigung setzen:
            cacls.exe C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000 /E /G "NT-AUTORITÄT\NETZWERKDIENST":R

        5.) Berechtigung verifizieren:
            cacls.exe C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000
            C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000 NT-AUTORITÄT\SYSTEM:(OI)(CI)F
                                                                                                           VORDEFINIERT\Administratoren:(OI)(CI)F
                                                                                                           gz-PC\gz:(OI)(CI)F
                                                                                                           NT-AUTORITÄT\NETZWERKDIENST:(OI)(CI)R


    So weit so gut. Dann folgendes:

        6.) Zertifikat ansehen:
            certutil -store -v my f97cd3b086a05baa44b9960834346543
    my
    ================ Zertifikat 3 ================
    X.509-Zertifikat:
    Version: 3
    Seriennummer: f97cd3b086a05baa44b9960834346543
    Signaturalgorithmus:
        Algorithmus Objekt-ID: 1.2.840.113549.1.1.4 md5RSA
        Algorithmusparameter:
        05 00
    Aussteller:
        CN=tmpCA2

     Nicht vor: 11.11.2009 14:24
     Nicht nach: 01.01.2040 00:59

    Antragsteller:
        CN=SignedByCA2

    ...

      D:P(A;;GAGR;;;SY)(A;;GAGR;;;BA)(A;;GAGR;;;S-1-5-21-452922241-2776708937-1870220006-1000)

        Zulassen Vollzugriff        NT-AUTORITÄT\SYSTEM
        Zulassen Vollzugriff        VORDEFINIERT\Administratoren
        Zulassen Vollzugriff        gz-PC\gz

    Der private Schlüssel ist NICHT exportierbar
    Das Testen der Signature wurde erfolgreich abgeschlossen
    CertUtil: -store-Befehl wurde erfolgreich ausgeführt.



    Hier fehlt offensichtlich die "NT-AUTORITÄT\NETZWERKDIENST". Warum?
    Wenn ich jetzt den WCF-Service starte sehe ich immernoch den Fehler: "Das Zertifikat "CN=SignedByCA2" muss einen privaten Schlüssel aufweisen, der den Schlüsselaustausch unterstützt. Der Prozess muss über Zugriffsrechte für den privaten Schlüssel verfügen. "

    Jetzt gehen mir langsam die Ideen aus.

    Die App.config des WCF-Servers:

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.web>
        <compilation debug="true" />
      </system.web>
      <system.serviceModel>
        
        <services>
          <service name="MyFirstWcfService.MyWcfService" behaviorConfiguration="MyFirstWcfService.MyWcfServiceBehavior">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost:8731/Design_Time_Addresses/MyFirstWcfService/MyWcfService/"   />
              </baseAddresses>
            </host>
            <endpoint address=""  binding="wsHttpBinding" bindingConfiguration="MessageUserName" contract="MyFirstWcfService.IMyWcfService">
            <!--
            <endpoint address ="" binding="wsHttpBinding" contract="MyFirstWcfService.IMyWcfService">
            -->
              <identity>
                <dns value="localhost"/>
              </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
          </service>
        </services>
        
        <behaviors>
          <serviceBehaviors>
            <behavior name="MyFirstWcfService.MyWcfServiceBehavior">
              <serviceMetadata httpGetEnabled="true" />
              <serviceDebug includeExceptionDetailInFaults="false" />
              <serviceCredentials>
                <serviceCertificate findValue="SignedByCA2" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
                <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyFirstWcfService.CustomUserNameValidator, MyFirstWcfService" />
                <!-- new -->
                <clientCertificate>
                  <authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" />
                </clientCertificate>
                <!-- new -->
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
        <bindings>
          <wsHttpBinding>
            <binding name="MessageUserName">
              <security mode="Message">
                <message clientCredentialType="UserName"/>
              </security>
            </binding>
          </wsHttpBinding>
        </bindings>
    
      </system.serviceModel>
      
      <system.diagnostics>
        <trace autoflush="true" />
        <sources>
          <source name="System.ServiceModel" switchValue="Verbose, ActivityTracing" propagateActivity="true">
            <listeners>
              <add name="sdt" type="System.Diagnostics.XmlWriterTraceListener" initializeData= "SdrConfigExample.e2e" />
            </listeners>
          </source>
        </sources>
      </system.diagnostics>
    
    </configuration>
    
    Weiss jemand Rat?

    Vielen Dank im Voraus,
    glibber


    Mittwoch, 11. November 2009 15:10
  • Ok ich setz noch ein's drauf:

    "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\makecert" -n "CN=tmpCA4" -r -pe -sv tmpCA4.pvk tmpCA4.cer
     --> ok

    "C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\makecert" -sk SignedByCA4 -iv tmpCA4.pvk -n "CN=SignedByCA4" -pe -ic tmpCA4.cer SignedByCA4.cer -sr currentuser -ss My
    --> ok

    Zertifikate importieren
    --> ok

    "C:\Samples\WCFWFCardSpace\WCF\Tools\FindPrivateKey\CS\bin\Release\FindPrivateKey.exe" My localMachine -n "CN=SignedByCA4"
    Private key directory:
    C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000
    ...


    cacls.exe C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000 /E /G "NT-AUTORITÄT\NETZWERKDIENST":F
    --> ok

    cacls.exe C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000
            C:\Users\gz\AppData\Roaming\Microsoft\Crypto\RSA\S-1-5-21-452922241-2776708937-1870220006-1000 NT-AUTORITÄT\SYSTEM:(OI)(CI)F
                                                                                                           VORDEFINIERT\Administratoren:(OI)(CI)F
                                                                                                           gz-PC\gz:(OI)(CI)F
                                                                                                           NT-AUTORITÄT\NETZWERKDIENST:(OI)(CI)F


    certutil -store -v my ecbb835717e479be40e7d55bb46ae4a8
    my
    ================ Zertifikat 3 ================
    X.509-Zertifikat:
    Version: 3
    Seriennummer: ecbb835717e479be40e7d55bb46ae4a8
    Signaturalgorithmus:
        Algorithmus Objekt-ID: 1.2.840.113549.1.1.4 md5RSA
        Algorithmusparameter:
        05 00
    Aussteller:
        CN=tmpCA4

     Nicht vor: 11.11.2009 17:07
     Nicht nach: 01.01.2040 00:59

    Antragsteller:
        CN=SignedByCA4
    ...
      D:(A;ID;GAGR;;;SY)(A;ID;GAGR;;;BA)(A;ID;GAGR;;;S-1-5-21-452922241-2776708937-1870220006-1000)(A;ID;GAGR;;;NS)

        Zulassen Vollzugriff        NT-AUTORITÄT\SYSTEM
        Zulassen Vollzugriff        VORDEFINIERT\Administratoren
        Zulassen Vollzugriff        gz-PC\gz
        Zulassen Vollzugriff        NT-AUTORITÄT\NETZWERKDIENST


    Privater Schlüssel:
      PRIVATEKEYBLOB
      Version: 2
      aiKeyAlg: 0x2400
        CALG_RSA_SIGN
        Algorithmusklasse: 0x2000(1) ALG_CLASS_SIGNATURE
        Algorithmustyp: 0x400(2) ALG_TYPE_RSA
        Algorithmussub-ID: 0x0(0) ALG_SID_RSA_ANY
      0000  52 53 41 32                                        RSA2
      0000  ...
      024c
    Das Testen der Signature wurde erfolgreich abgeschlossen
    CertUtil: -store-Befehl wurde erfolgreich ausgeführt.


    Starten des WCF-Service --> Exception: "Das Zertifikat "CN=SignedByCA4 " muss einen privaten Schlüssel aufweisen, der den Schlüsselaustausch unterstützt. Der Prozess muss über Zugriffsrechte für den privaten Schlüssel verfügen. ".

    Was fehlt ihm denn jetzt noch, soll ich dem WCF-Dienst das Zertifikat vorlesen oder was???

    <<Verzweifel>> :-{

    Jede Hilfe wilkommen.



    Mittwoch, 11. November 2009 16:27

  • Hallo,

    jetzt hab' ich endlich geschafft - war auch eine schwierige Geburt.

    Also:
    makecert -sr LocalMachine -ss My -pe -sky exchange -n "CN=TestCert1" TestCert1.cer -a sha1

    dann nichts weiter, der Key ist dann schon im Store und braucht nicht manuell importiert zu werden.

    Service-Konfig:
              <serviceCredentials>
                <serviceCertificate findValue="TestCert1" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
                <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="MyFirstWcfService.CustomUserNameValidator, MyFirstWcfService" />
                <clientCertificate>
                  <authentication certificateValidationMode="PeerTrust" trustedStoreLocation="LocalMachine" />
                </clientCertificate>
              </serviceCredentials>


    Client-Konfig:
              <clientCredentials>
                <serviceCertificate>
                  <authentication certificateValidationMode="Custom" customCertificateValidatorType="MyFirstWcfClient.MyCertValidator, MyFirstWcfClient"/>
                </serviceCertificate>
              </clientCredentials>


    Client-Code:
    namespace MyFirstWcfClient
    {
        class MyCertValidator : X509CertificateValidator
        {
            public override void Validate(X509Certificate2 certificate)
            {
                // Check that there is a certificate.
                if (certificate == null)
                {
                    throw new ArgumentNullException("certificate");
                }

                // TODO: do all the checks
            }
        }
    }

    So funktioniert es. :-))


    enlightened by dotnetpro (Ausgabe 9.2009)


    (Da zeigt sich wieder, dass man nicht den ganzen Tag vor dem Bildschirm hocken, sondern ab- und an mal ein Buch oder eine Zeitschrift zur Hand nehmen sollte!)

    Schönen Abend.

    glibber
    • Als Antwort markiert glibber Mittwoch, 11. November 2009 18:48
    Mittwoch, 11. November 2009 18:47