Udostępnij za pośrednictwem


Moduł weryfikacji certyfikatów X.509

W tym przykładzie pokazano, jak zaimplementować niestandardowy moduł sprawdzania poprawności certyfikatów X.509. Jest to przydatne w przypadkach, gdy żaden z wbudowanych trybów weryfikacji certyfikatu X.509 nie jest odpowiedni dla wymagań aplikacji. W tym przykładzie przedstawiono usługę, która ma niestandardowy moduł sprawdzania poprawności, który akceptuje certyfikaty wystawione samodzielnie. Klient używa takiego certyfikatu do uwierzytelniania w usłudze.

Uwaga: każdy może utworzyć certyfikat wystawiony samodzielnie, niestandardowy moduł sprawdzania poprawności używany przez usługę jest mniej bezpieczny niż domyślne zachowanie udostępniane przez parametr ChainTrust X509CertificateValidationMode. Implikacje bezpieczeństwa należy dokładnie rozważyć przed użyciem tej logiki weryfikacji w kodzie produkcyjnym.

Podsumowując, w tym przykładzie pokazano, jak:

  • Klienta można uwierzytelnić przy użyciu certyfikatu X.509.

  • Serwer weryfikuje poświadczenia klienta względem niestandardowego klasy X509CertificateValidator.

  • Serwer jest uwierzytelniany przy użyciu certyfikatu X.509 serwera.

Usługa uwidacznia pojedynczy punkt końcowy do komunikowania się z usługą, zdefiniowany przy użyciu pliku konfiguracji App.config. Punkt końcowy składa się z adresu, powiązania i kontraktu. Powiązanie jest konfigurowane przy użyciu standardu wsHttpBinding , który domyślnie używa uwierzytelniania certyfikatu WSSecurity klienta i . Zachowanie usługi określa tryb niestandardowy sprawdzania poprawności certyfikatów X.509 klienta wraz z typem klasy modułu sprawdzania poprawności. Zachowanie określa również certyfikat serwera przy użyciu elementu serviceCertificate. Certyfikat serwera musi zawierać tę samą wartość dla SubjectName elementu w pliku serviceCertificate><.findValue

  <system.serviceModel>
    <services>
      <service name="Microsoft.ServiceModel.Samples.CalculatorService"
               behaviorConfiguration="CalculatorServiceBehavior">
        <!-- use host/baseAddresses to configure base address -->
        <!-- provided by host -->
        <host>
          <baseAddresses>
            <add baseAddress =
                "http://localhost:8001/servicemodelsamples/service" />
          </baseAddresses>
        </host>
        <!-- use base address specified above, provide one endpoint -->
        <endpoint address="certificate"
               binding="wsHttpBinding"
               bindingConfiguration="Binding"
               contract="Microsoft.ServiceModel.Samples.ICalculator" />
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <!-- X509 certificate binding -->
        <binding name="Binding">
          <security mode="Message">
            <message clientCredentialType="Certificate" />
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="CalculatorServiceBehavior">
          <serviceDebug includeExceptionDetailInFaults ="true"/>
          <serviceCredentials>
            <!-- The serviceCredentials behavior allows one -->
            <!-- to specify authentication constraints on -->
            <!-- client certificates. -->
            <clientCertificate>
              <!-- Setting the certificateValidationMode to -->
              <!-- Custom means that if the custom -->
              <!-- X509CertificateValidator does NOT throw -->
              <!-- an exception, then the provided certificate -->
              <!-- will be trusted without performing any -->
              <!-- validation beyond that performed by the custom -->
              <!-- validator. The security implications of this -->
              <!-- setting should be carefully considered before -->
              <!-- using Custom in production code. -->
              <authentication
                 certificateValidationMode="Custom"
                 customCertificateValidatorType =
"Microsoft.ServiceModel.Samples.CustomX509CertificateValidator, service" />
            </clientCertificate>
            <!-- The serviceCredentials behavior allows one to -->
            <!-- define a service certificate. -->
            <!-- A service certificate is used by a client to -->
            <!-- authenticate the service and provide message -->
            <!-- protection. This configuration references the -->
            <!-- "localhost" certificate installed during the setup -->
            <!-- instructions. -->
            <serviceCertificate findValue="localhost"
                 storeLocation="LocalMachine"
                 storeName="My" x509FindType="FindBySubjectName" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
      </system.serviceModel>

Konfiguracja punktu końcowego klienta składa się z nazwy konfiguracji, bezwzględnego adresu punktu końcowego usługi, powiązania i kontraktu. Powiązanie klienta jest konfigurowane przy użyciu odpowiedniego trybu i komunikatu clientCredentialType.

<system.serviceModel>
    <client>
      <!-- X509 certificate based endpoint -->
      <endpoint name="Certificate"
        address=
        "http://localhost:8001/servicemodelsamples/service/certificate"
                binding="wsHttpBinding"
                bindingConfiguration="Binding"
                behaviorConfiguration="ClientCertificateBehavior"
                contract="Microsoft.ServiceModel.Samples.ICalculator">
      </endpoint>
    </client>
    <bindings>
        <wsHttpBinding>
            <!-- X509 certificate binding -->
            <binding name="Binding">
                <security mode="Message">
                    <message clientCredentialType="Certificate" />
               </security>
            </binding>
       </wsHttpBinding>
    </bindings>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ClientCertificateBehavior">
          <clientCredentials>
            <serviceCertificate>
              <!-- Setting the certificateValidationMode to -->
              <!-- PeerOrChainTrust means that if the certificate -->
              <!-- is in the user's Trusted People store, then it -->
              <!-- is trusted without performing a validation of -->
              <!-- the certificate's issuer chain. -->
              <!-- This setting is used here for convenience so -->
              <!-- that the sample can be run without having to -->
              <!-- have certificates issued by a certification -->
              <!-- authority (CA). This setting is less secure -->
              <!-- than the default, ChainTrust. The security -->
              <!-- implications of this setting should be -->
              <!-- carefully considered before using -->
              <!-- PeerOrChainTrust in production code.-->
              <authentication
                  certificateValidationMode="PeerOrChainTrust" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
  </system.serviceModel>

Implementacja klienta ustawia certyfikat klienta do użycia.

// Create a client with Certificate endpoint configuration
CalculatorClient client = new CalculatorClient("Certificate");
try
{
    client.ClientCredentials.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "test1");

    // Call the Add service operation.
    double value1 = 100.00D;
    double value2 = 15.99D;
    double result = client.Add(value1, value2);
    Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);

    // Call the Subtract service operation.
    value1 = 145.00D;
    value2 = 76.54D;
    result = client.Subtract(value1, value2);
    Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);

    // Call the Multiply service operation.
    value1 = 9.00D;
    value2 = 81.25D;
    result = client.Multiply(value1, value2);
    Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);

    // Call the Divide service operation.
    value1 = 22.00D;
    value2 = 7.00D;
    result = client.Divide(value1, value2);
    Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
    client.Close();
}
catch (TimeoutException e)
{
    Console.WriteLine("Call timed out : {0}", e.Message);
    client.Abort();
}
catch (CommunicationException e)
{
    Console.WriteLine("Call failed : {0}", e.Message);
    client.Abort();
}
catch (Exception e)
{
    Console.WriteLine("Call failed : {0}", e.Message);
    client.Abort();
}

W tym przykładzie użyto niestandardowego modułu X509CertificateValidator do sprawdzania poprawności certyfikatów. Przykład implementuje element CustomX509CertificateValidator pochodzący z elementu X509CertificateValidator. Aby uzyskać więcej informacji, zobacz dokumentację dotyczącą X509CertificateValidator tego tematu. Ten przykładowy niestandardowy moduł sprawdzania poprawności implementuje metodę Validate, aby zaakceptować dowolny certyfikat X.509 wystawiony samodzielnie, jak pokazano w poniższym kodzie.

public class CustomX509CertificateValidator : X509CertificateValidator
{
  public override void Validate ( X509Certificate2 certificate )
  {
   // Only accept self-issued certificates
   if (certificate.Subject != certificate.Issuer)
     throw new Exception("Certificate is not self-issued");
   }
}

Po zaimplementowaniu modułu sprawdzania poprawności w kodzie usługi host usługi musi zostać poinformowany o wystąpieniu modułu sprawdzania poprawności do użycia. Odbywa się to przy użyciu następującego kodu.

serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
serviceHost.Credentials.ClientCertificate.Authentication.CustomCertificateValidator = new CustomX509CertificateValidator();

Możesz też wykonać te same czynności w konfiguracji w następujący sposób.

<behaviors>
    <serviceBehaviors>
     <behavior name="CalculatorServiceBehavior">
       ...
   <serviceCredentials>
    <!--The serviceCredentials behavior allows one to specify -->
    <!--authentication constraints on client certificates.-->
    <clientCertificate>
    <!-- Setting the certificateValidationMode to Custom means -->
    <!--that if the custom X509CertificateValidator does NOT -->
    <!--throw an exception, then the provided certificate will -->
    <!--be trusted without performing any validation beyond that -->
    <!--performed by the custom validator. The security -->
    <!--implications of this setting should be carefully -->
    <!--considered before using Custom in production code. -->
    <authentication certificateValidationMode="Custom"
       customCertificateValidatorType =
"Microsoft.ServiceModel.Samples. CustomX509CertificateValidator, service" />
   </clientCertificate>
   </serviceCredentials>
   ...
  </behavior>
 </serviceBehaviors>
</behaviors>

Po uruchomieniu przykładu żądania operacji i odpowiedzi są wyświetlane w oknie konsoli klienta. Klient powinien pomyślnie wywołać wszystkie metody. Naciśnij klawisz ENTER w oknie klienta, aby zamknąć klienta.

Konfigurowanie pliku wsadowego

Plik wsadowy Setup.bat dołączony do tego przykładu umożliwia skonfigurowanie serwera z odpowiednimi certyfikatami w celu uruchomienia aplikacji hostowanej samodzielnie, która wymaga zabezpieczeń opartych na certyfikatach serwera. Ten plik wsadowy należy zmodyfikować tak, aby działał na komputerach lub działać w przypadku innym niż hostowany.

Poniżej przedstawiono krótkie omówienie różnych sekcji plików wsadowych, dzięki czemu można je zmodyfikować w celu uruchomienia w odpowiedniej konfiguracji:

  • Tworzenie certyfikatu serwera:

    Następujące wiersze z pliku wsadowego Setup.bat tworzą certyfikat serwera do użycia. Zmienna %SERVER_NAME% określa nazwę serwera. Zmień tę zmienną, aby określić własną nazwę serwera. Wartość domyślna to localhost.

    echo ************
    echo Server cert setup starting
    echo %SERVER_NAME%
    echo ************
    echo making server cert
    echo ************
    makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
    
  • Instalowanie certyfikatu serwera w magazynie zaufanych certyfikatów klienta:

    Następujące wiersze w pliku wsadowym Setup.bat skopiuj certyfikat serwera do magazynu zaufanych osób klienta. Ten krok jest wymagany, ponieważ certyfikaty generowane przez Makecert.exe nie są niejawnie zaufane przez system kliencki. Jeśli masz już certyfikat, który jest root w zaufanym certyfikacie głównym klienta — na przykład certyfikat wystawiony przez firmę Microsoft — ten krok wypełniania magazynu certyfikatów klienta przy użyciu certyfikatu serwera nie jest wymagany.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Tworzenie certyfikatu klienta:

    Następujące wiersze z pliku wsadowego Setup.bat tworzą certyfikat klienta do użycia. Zmienna %USER_NAME% określa nazwę klienta. Ta wartość jest ustawiona na "test1", ponieważ jest to nazwa szukana przez kod klienta. Jeśli zmienisz wartość %USER_NAME%, musisz zmienić odpowiednią wartość w pliku źródłowym Client.cs i ponownie skompilować klienta.

    Certyfikat jest przechowywany w magazynie Mój (osobisty) w lokalizacji magazynu CurrentUser.

    echo ************
    echo Client cert setup starting
    echo %USER_NAME%
    echo ************
    echo making client cert
    echo ************
    makecert.exe -sr CurrentUser -ss MY -a sha1 -n CN=%USER_NAME% -sky exchange -pe
    
  • Instalowanie certyfikatu klienta w magazynie zaufanych certyfikatów serwera:

    Następujące wiersze w pliku wsadowym Setup.bat skopiuj certyfikat klienta do magazynu zaufanych osób. Ten krok jest wymagany, ponieważ certyfikaty generowane przez Makecert.exe nie są niejawnie zaufane przez system serwera. Jeśli masz już certyfikat, który jest zakorzeniony w zaufanym certyfikacie głównym — na przykład certyfikat wystawiony przez firmę Microsoft — ten krok wypełniania magazynu certyfikatów serwera przy użyciu certyfikatu klienta nie jest wymagany.

    certmgr.exe -add -r CurrentUser -s My -c -n %USER_NAME% -r LocalMachine -s TrustedPeople
    

Aby skonfigurować i skompilować przykład

  1. Aby skompilować rozwiązanie, postępuj zgodnie z instrukcjami w temacie Building the Windows Communication Foundation Samples (Tworzenie przykładów programu Windows Communication Foundation).

  2. Aby uruchomić przykład w konfiguracji pojedynczej lub między komputerami, skorzystaj z poniższych instrukcji.

Aby uruchomić przykład na tym samym komputerze

  1. Uruchom Setup.bat z przykładowego folderu instalacji w wierszu polecenia programu Visual Studio otwartym z uprawnieniami administratora. Spowoduje to zainstalowanie wszystkich certyfikatów wymaganych do uruchomienia przykładu.

    Ważne

    Plik wsadowy Setup.bat jest przeznaczony do uruchamiania z poziomu wiersza polecenia programu Visual Studio. Zmienna środowiskowa PATH ustawiona w wierszu polecenia programu Visual Studio wskazuje katalog zawierający pliki wykonywalne wymagane przez skrypt Setup.bat.

  2. Uruchom Service.exe z pliku service\bin.

  3. Uruchom Client.exe z \client\bin. Działanie klienta jest wyświetlane w aplikacji konsolowej klienta.

  4. Jeśli klient i usługa nie mogą się komunikować, zobacz Rozwiązywanie problemów Wskazówki dla przykładów programu WCF.

Aby uruchomić przykład na komputerach

  1. Utwórz katalog na komputerze usługi.

  2. Skopiuj pliki programu usługi z \service\bin do katalogu wirtualnego na komputerze usługi. Skopiuj również pliki Setup.bat, Cleanup.bat, GetComputerName.vbs i ImportClientCert.bat na komputer usługi.

  3. Utwórz katalog na komputerze klienckim dla plików binarnych klienta.

  4. Skopiuj pliki programu klienckiego do katalogu klienta na komputerze klienckim. Skopiuj również pliki Setup.bat, Cleanup.bat i ImportServiceCert.bat do klienta.

  5. Na serwerze uruchom polecenie setup.bat service w wierszu polecenia dla deweloperów dla programu Visual Studio otwarte z uprawnieniami administratora. Uruchomienie setup.bat z argumentem service powoduje utworzenie certyfikatu usługi z w pełni kwalifikowaną nazwą domeny komputera i eksportuje certyfikat usługi do pliku o nazwie Service.cer.

  6. Edytuj plik Service.exe.config, aby odzwierciedlić nową nazwę certyfikatu (w atrybucie findValue serviceCertificate>), która jest taka sama jak w <pełni kwalifikowana nazwa domeny komputera. Zmień również nazwę komputera w elemecie <service>/<baseAddresses> z localhost na w pełni kwalifikowaną nazwę komputera usługi.

  7. Skopiuj plik Service.cer z katalogu usługi do katalogu klienta na komputerze klienckim.

  8. Na kliencie uruchom polecenie setup.bat client w wierszu polecenia dla deweloperów dla programu Visual Studio otwarte z uprawnieniami administratora. Uruchomienie setup.bat z argumentem client powoduje utworzenie certyfikatu klienta o nazwie client.com i wyeksportowanie certyfikatu klienta do pliku o nazwie Client.cer.

  9. W pliku Client.exe.config na komputerze klienckim zmień wartość adresu punktu końcowego, aby był zgodny z nowym adresem usługi. W tym celu należy zastąpić hosta lokalnego w pełni kwalifikowaną nazwą domeny serwera.

  10. Skopiuj plik Client.cer z katalogu klienta do katalogu usługi na serwerze.

  11. Na kliencie uruchom ImportServiceCert.bat w wierszu polecenia dewelopera dla programu Visual Studio otwartym z uprawnieniami administratora. Spowoduje to zaimportowanie certyfikatu usługi z pliku Service.cer do magazynu CurrentUser — Trusted Osoby.

  12. Na serwerze uruchom ImportClientCert.bat w wierszu polecenia dewelopera dla programu Visual Studio otwartym z uprawnieniami administratora. Spowoduje to zaimportowanie certyfikatu klienta z pliku Client.cer do magazynu LocalMachine — Trusted Osoby.

  13. Na komputerze serwera uruchom Service.exe w oknie wiersza polecenia.

  14. Na komputerze klienckim uruchom Client.exe w oknie wiersza polecenia. Jeśli klient i usługa nie mogą się komunikować, zobacz Rozwiązywanie problemów Wskazówki dla przykładów programu WCF.

Aby wyczyścić po próbce

  1. Uruchom Cleanup.bat w folderze samples po zakończeniu uruchamiania przykładu. Spowoduje to usunięcie certyfikatów serwera i klienta z magazynu certyfikatów.

Uwaga

Ten skrypt nie usuwa certyfikatów usługi na kliencie podczas uruchamiania tego przykładu na komputerach. Jeśli uruchomiono przykłady programu Windows Communication Foundation (WCF) korzystające z certyfikatów na komputerach, pamiętaj, aby wyczyścić certyfikaty usługi zainstalowane w magazynie CurrentUser — Trusted Osoby. W tym celu użyj następującego polecenia: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> na przykład: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.