Sdílet prostřednictvím


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 certifikát vydaný vlastním držitelem, vlastní validátor používaný službou je méně zabezpečený než výchozí chování poskytované ChainTrust 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 pro vlastní 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ý se standardně používá WSSecurity a ověřování klientským certifikátem. 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 jako SubjectNamefindValue 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. Vazba klienta je nakonfigurována s odpovídajícím režimem a zprávou 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 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 v počítačích nebo aby fungoval v případě, že není hostovaný.

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 osob klienta. Tento krok je povinný, protože klientskému systému nejsou certifikáty generované Makecert.exe implicitně důvěryhodné. 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 (Personal) v umístění Úložiště 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
    
  • 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. Pokud chcete sestavit řešení, postupujte podle pokynů v části 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ů.

Spuštění ukázky 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 téma Řešení potíží Tipy pro ukázky WCF.

Spuštění ukázky napříč počítači

  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.bat a 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 <v 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 – Trusted Lidé.

  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 – Trusted Lidé.

  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 téma Řešení potíží Tipy pro ukázky WCF.

Vyčištění po ukázce

  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 počítačích neodebere certifikáty služby v 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 – Trusted Lidé. 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.