Validátor certifikátu X.509

Tato ukázka ukazuje, jak implementovat vlastní validátor certifikátu X.509. To je užitečné v případech, kdy žádný z integrovaných režimů ověřování certifikátů X.509 není vhodný pro požadavky aplikace. Tato ukázka ukazuje službu, která má vlastní validátor, který přijímá certifikáty vydané svým držitelem. Klient takový certifikát používá k ověření ve službě.

Poznámka: Vzhledem k tomu, že každý může vytvořit sebevydaný certifikát, uživatelský validátor používaný službou je méně zabezpečený než výchozí funkce ChainTrust pro režim ověřování X509CertificateValidationMode. Před použitím této ověřovací logiky v produkčním kódu byste měli pečlivě zvážit bezpečnostní důsledky.

V souhrnu tato ukázka ukazuje, jak:

  • Klient se dá ověřit pomocí certifikátu X.509.

  • Server ověří přihlašovací údaje klienta pomocí vlastního X509CertificateValidator.

  • Server se ověřuje pomocí certifikátu X.509 serveru.

Služba zveřejňuje jeden koncový bod pro komunikaci se službou definovanou pomocí konfiguračního souboru App.config. Koncový bod se skládá z adresy, vazby a kontraktu. Vazba je nakonfigurovaná se standardem wsHttpBinding, který ve výchozím nastavení používá WSSecurity a ověřování pomocí klientského certifikátu. Chování služby určuje vlastní režim pro ověřování klientských certifikátů X.509 spolu s typem třídy validátoru. Chování také určuje certifikát serveru pomocí elementu serviceCertificate. Certifikát serveru musí obsahovat stejnou hodnotu pro SubjectName jako findValue v <serviceCertificate>.

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

Konfigurace koncového bodu klienta se skládá z názvu konfigurace, absolutní adresy koncového bodu služby, vazby a kontraktu. Konfigurace vazby klienta odpovídá vhodnému režimu a zprávě 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>

Implementace klienta nastaví klientský certifikát, který se má použít.

// 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();
}

Tato ukázka používá k ověření certifikátů vlastní X509CertificateValidator. Ukázka implementuje CustomX509CertificateValidator, který je odvozen od X509CertificateValidator. Další informace najdete v X509CertificateValidator dokumentaci. Tato konkrétní ukázka vlastního validátoru implementuje metodu Validate tak, aby přijímala jakýkoli certifikát X.509, který je vystavený samostatně, jak je znázorněno v následujícím kódu.

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 implementaci validátoru v kódu služby musí být hostitel služby informován o instanci validátoru, která se má použít. To se provádí pomocí následujícího kódu.

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

Nebo můžete provést stejnou věc v konfiguraci následujícím způsobem.

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

Při spuštění ukázky se požadavky na operace a odpovědi zobrazí v okně konzoly klienta. Klient by měl úspěšně volat všechny metody. Stisknutím klávesy ENTER v okně klienta klienta ukončete klienta.

Nastavení dávkového souboru

Dávkový soubor Setup.bat, který je součástí této ukázky, umožňuje nakonfigurovat server s relevantními certifikáty pro spuštění aplikace v místním prostředí, která vyžaduje zabezpečení založené na certifikátech serveru. Tento dávkový soubor musí být upraven tak, aby fungoval na více počítačích nebo v nehostovaném prostředí.

Níže najdete stručný přehled různých částí dávkových souborů, aby je bylo možné upravit tak, aby běžely v příslušné konfiguraci:

  • Vytvoření certifikátu serveru:

    Následující řádky z dávkového souboru Setup.bat vytvoří certifikát serveru, který se má použít. Proměnná %SERVER_NAME% určuje název serveru. Změňte tuto proměnnou tak, aby byla zadána vlastní název serveru. Výchozí hodnota je 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
    
  • Instalace certifikátu serveru do důvěryhodného úložiště certifikátů klienta:

    Následující řádky v dávkovém souboru Setup.bat zkopírují certifikát serveru do úložiště důvěryhodných lidí klienta. Tento krok je povinný, protože certifikáty generované Makecert.exe nejsou implicitně důvěryhodné klientským systémem. Pokud už máte certifikát, který je kořenový v kořenovém certifikátu klienta ( například certifikát vydaný Microsoftem), tento krok naplnění úložiště klientských certifikátů certifikátem pomocí certifikátu serveru se nevyžaduje.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Vytvoření klientského certifikátu:

    Následující řádky z dávkového souboru Setup.bat vytvoří klientský certifikát, který se má použít. Proměnná %USER_NAME% určuje název klienta. Tato hodnota je nastavená na test1, protože se jedná o název, který klientský kód hledá. Pokud změníte hodnotu %USER_NAME% musíte změnit odpovídající hodnotu ve zdrojovém souboru Client.cs a znovu sestavit klienta.

    Certifikát je uložen v úložišti My (Osobní) pro aktuálního uživatele.

    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
    
  • Instalace klientského certifikátu do důvěryhodného úložiště certifikátů serveru:

    Následující řádky v dávkovém souboru Setup.bat zkopírují klientský certifikát do úložiště důvěryhodných osob. Tento krok je povinný, protože certifikáty generované Makecert.exe nejsou implicitně důvěryhodné systémem serveru. Pokud už máte certifikát, který je kořenový v důvěryhodném kořenovém certifikátu , například certifikát vydaný Microsoftem, tento krok naplnění úložiště certifikátů serveru klientským certifikátem se nevyžaduje.

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

Nastavení a sestavení ukázky

  1. Pro sestavení řešení postupujte podle pokynů v Sestavení ukázek Windows Communication Foundation.

  2. Pokud chcete spustit ukázku v konfiguraci jednoho nebo více počítačů, postupujte podle následujících pokynů.

Spustit ukázku na stejném počítači

  1. Spusťte Setup.bat z ukázkové instalační složky v příkazovém řádku sady Visual Studio otevřeného s oprávněními správce. Tím se nainstalují všechny certifikáty potřebné pro spuštění ukázky.

    Důležité

    Dávkový soubor Setup.bat je navržený tak, aby běžel z příkazového řádku sady Visual Studio. Proměnná prostředí PATH nastavená v příkazovém řádku sady Visual Studio odkazuje na adresář, který obsahuje spustitelné soubory vyžadované skriptem Setup.bat.

  2. Spusťte Service.exe ze služby\bin.

  3. Spusťte Client.exe z \client\bin. Aktivita klienta se zobrazí v aplikaci konzoly klienta.

  4. Pokud klient a služba nemůžou komunikovat, přečtěte si Tipy pro řešení potíží v ukázkách WCF.

Spustit ukázku na více počítačích

  1. Vytvořte adresář na počítači služby.

  2. Zkopírujte soubory programu služby ze složky \service\bin do virtuálního adresáře v počítači služby. Zkopírujte také soubory Setup.bat, Cleanup.bat, GetComputerName.vbs a ImportClientCert.bat do počítače služby.

  3. Vytvořte adresář v klientském počítači pro binární soubory klienta.

  4. Zkopírujte soubory klientského programu do klientského adresáře v klientském počítači. Zkopírujte také soubory Setup.bat, Cleanup.bata ImportServiceCert.bat do klienta.

  5. Na serveru spusťte setup.bat service příkazový řádek pro vývojáře pro Visual Studio otevřený s oprávněními správce. Spuštění setup.bat s argumentem service vytvoří certifikát služby s plně kvalifikovaným názvem domény počítače a exportuje certifikát služby do souboru s názvem Service.cer.

  6. Upravte Service.exe.config tak, aby odrážel nový název certifikátu (v findValue atributu <serviceCertificate>), který je stejný jako plně kvalifikovaný název domény počítače. Změňte také název počítače v elementu <service>/<baseAddresses> z localhost na plně kvalifikovaný název počítače služby.

  7. Zkopírujte soubor Service.cer z adresáře služby do klientského adresáře v klientském počítači.

  8. Na klientovi spusťte setup.bat client příkazový řádek pro vývojáře pro Visual Studio otevřený s oprávněními správce. Spuštění setup.bat s argumentem client vytvoří klientský certifikát s názvem client.com a exportuje klientský certifikát do souboru s názvem Client.cer.

  9. V souboru Client.exe.config na klientském počítači změňte hodnotu adresy koncového bodu tak, aby odpovídala nové adrese vaší služby. Provedete to tak, že nahradíte localhost plně kvalifikovaným názvem domény serveru.

  10. Zkopírujte soubor Client.cer z klientského adresáře do adresáře služby na serveru.

  11. Na klientovi spusťte ImportServiceCert.bat v příkazovém řádku pro vývojáře pro Visual Studio otevřené s oprávněními správce. Tím se certifikát služby naimportuje ze souboru Service.cer do úložiště CurrentUser – TrustedPeople.

  12. Na serveru spusťte ImportClientCert.bat v příkazovém řádku pro vývojáře pro Visual Studio otevřené s oprávněními správce. Tím se klientský certifikát naimportuje ze souboru Client.cer do úložiště LocalMachine – TrustedPeople.

  13. Na serverovém počítači spusťte Service.exe z okna příkazového řádku.

  14. Na klientském počítači spusťte Client.exe z okna příkazového řádku. Pokud klient a služba nemůžou komunikovat, přečtěte si Tipy pro řešení potíží v ukázkách WCF.

Úklid po vzorku

  1. Po dokončení spuštění ukázky spusťte Cleanup.bat ve složce s ukázkami. Tím se odeberou certifikáty serveru a klienta z úložiště certifikátů.

Poznámka:

Tento skript při spuštění této ukázky na různých počítačích neodebere služební certifikáty na klientovi. Pokud jste spustili ukázky wcf (Windows Communication Foundation), které používají certifikáty napříč počítači, nezapomeňte vymazat certifikáty služby nainstalované v úložišti CurrentUser – TrustedPeople. K tomu použijte následující příkaz: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> Například: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.