Dela via


X.509-certifikatverifierare

Det här exemplet visar hur du implementerar en anpassad X.509-certifikatverifierare. Detta är användbart i fall där inget av de inbyggda X.509-certifikatverifieringslägena är lämpliga för programmets krav. Det här exemplet visar en tjänst som har en anpassad validator som accepterar självutfärdade certifikat. Klienten använder ett sådant certifikat för att autentisera till tjänsten.

Obs! Eftersom vem som helst kan skapa ett självutfärdat certifikat är den anpassade valideraren som används av tjänsten mindre säker än standardbeteendet som tillhandahålls av ChainTrust X509CertificateValidationMode. Säkerhetskonsekvenserna av detta bör övervägas noggrant innan du använder den här valideringslogik i produktionskoden.

Sammanfattningsvis visar det här exemplet hur:

  • Klienten kan autentiseras med ett X.509-certifikat.

  • Servern validerar klientautentiseringsuppgifterna mot en anpassad X509CertificateValidator.

  • Servern autentiseras med hjälp av serverns X.509-certifikat.

Tjänsten exponerar en enda slutpunkt för kommunikation med tjänsten, definierad med hjälp av konfigurationsfilen App.config. Slutpunkten består av en adress, en bindning och ett kontrakt. Bindningen konfigureras med en standard som wsHttpBinding standard för användning WSSecurity och klientcertifikatautentisering. Tjänstbeteendet anger anpassat läge för validering av X.509-klientcertifikat tillsammans med typen av validatorklass. Beteendet anger även servercertifikatet med hjälp av elementet serviceCertificate. Servercertifikatet måste innehålla samma värde för som i serviceCertificate>.<findValueSubjectName

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

Klientslutpunktskonfigurationen består av ett konfigurationsnamn, en absolut adress för tjänstslutpunkten, bindningen och kontraktet. Klientbindningen konfigureras med lämpligt läge och meddelande 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>

Klientimplementeringen anger vilket klientcertifikat som ska användas.

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

Det här exemplet använder en anpassad X509CertificateValidator för att verifiera certifikat. Exemplet implementerar CustomX509CertificateValidator, härledd från X509CertificateValidator. Mer information finns i dokumentationen.X509CertificateValidator Det här anpassade valideringsexemplet implementerar metoden Validate för att acceptera alla X.509-certifikat som är självutfärdade enligt följande kod.

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

När validatorn har implementerats i tjänstkoden måste tjänstvärden informeras om den validatorinstans som ska användas. Detta görs med hjälp av följande kod.

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

Eller så kan du göra samma sak i konfigurationen på följande sätt.

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

När du kör exemplet visas åtgärdsbegäranden och svar i klientkonsolfönstret. Klienten bör anropa alla metoder. Tryck på RETUR i klientfönstret för att stänga av klienten.

Konfigurera Batch-fil

Med Setup.bat batchfil som ingår i det här exemplet kan du konfigurera servern med relevanta certifikat för att köra ett program med egen värd som kräver servercertifikatbaserad säkerhet. Den här batchfilen måste ändras för att fungera mellan datorer eller för att fungera i ett fall som inte är värdbaserat.

Följande ger en kort översikt över de olika avsnitten i batchfilerna så att de kan ändras så att de körs i rätt konfiguration:

  • Skapa servercertifikatet:

    Följande rader från Setup.bat batchfil skapar det servercertifikat som ska användas. Variabeln %SERVER_NAME% anger servernamnet. Ändra den här variabeln för att ange ditt eget servernamn. Standardvärdet är 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
    
  • Installera servercertifikatet i klientens betrodda certifikatarkiv:

    Följande rader i Setup.bat batchfil kopierar servercertifikatet till klientarkivet för betrodda personer. Det här steget krävs eftersom certifikat som genereras av Makecert.exe inte är implicit betrodda av klientsystemet. Om du redan har ett certifikat som är rotat i ett klientbetrott rotcertifikat, till exempel ett Microsoft-utfärdat certifikat, krävs inte det här steget för att fylla i klientcertifikatarkivet med servercertifikatet.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Skapa klientcertifikatet:

    Följande rader från Setup.bat batchfil skapar det klientcertifikat som ska användas. Variabeln %USER_NAME% anger klientnamnet. Det här värdet är inställt på "test1" eftersom det här är namnet som klientkoden söker efter. Om du ändrar värdet för %USER_NAME% måste du ändra motsvarande värde i Client.cs källfilen och återskapa klienten.

    Certifikatet lagras i arkivet My (Personal) under arkivplatsen 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
    
  • Installera klientcertifikatet i serverns betrodda certifikatarkiv:

    Följande rader i Setup.bat batchfil kopierar klientcertifikatet till arkivet betrodda personer. Det här steget krävs eftersom certifikat som genereras av Makecert.exe inte är implicit betrodda av serversystemet. Om du redan har ett certifikat som är rotat i ett betrott rotcertifikat, till exempel ett Microsoft-utfärdat certifikat, krävs inte det här steget för att fylla i servercertifikatarkivet med klientcertifikatet.

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

Så här konfigurerar och skapar du exemplet

  1. Skapa lösningen genom att följa anvisningarna i Skapa Windows Communication Foundation-exempel.

  2. Om du vill köra exemplet i en konfiguration med en eller flera datorer använder du följande instruktioner.

Så här kör du exemplet på samma dator

  1. Kör Setup.bat från exempelinstallationsmappen i en Visual Studio-kommandotolk som öppnas med administratörsbehörighet. Detta installerar alla certifikat som krävs för att köra exemplet.

    Viktigt!

    Den Setup.bat batchfilen är utformad för att köras från en Visual Studio-kommandotolk. Variabeln PATH-miljö som angetts i Visual Studio-kommandotolken pekar på katalogen som innehåller körbara filer som krävs av Setup.bat skriptet.

  2. Starta Service.exe från service\bin.

  3. Starta Client.exe från \client\bin. Klientaktiviteten visas i klientkonsolprogrammet.

  4. Om klienten och tjänsten inte kan kommunicera kan du läsa Felsökningstips för WCF-exempel.

Så här kör du exemplet mellan datorer

  1. Skapa en katalog på tjänstdatorn.

  2. Kopiera tjänstprogramfilerna från \service\bin till den virtuella katalogen på tjänstdatorn. Kopiera även filerna Setup.bat, Cleanup.bat, GetComputerName.vbs och ImportClientCert.bat till tjänstdatorn.

  3. Skapa en katalog på klientdatorn för klient binärfilerna.

  4. Kopiera klientprogramfilerna till klientkatalogen på klientdatorn. Kopiera även filerna Setup.bat, Cleanup.bat och ImportServiceCert.bat till klienten.

  5. På servern kör du setup.bat service i en kommandotolk för utvecklare för Visual Studio som öppnats med administratörsbehörighet. När du service kör setup.bat med argumentet skapas ett tjänstcertifikat med datorns fullständigt kvalificerade domännamn och tjänstcertifikatet exporteras till en fil med namnet Service.cer.

  6. Redigera Service.exe.config för att återspegla det nya certifikatnamnet (i findValue attributet i <serviceCertificate>) som är samma som datorns fullständigt kvalificerade domännamn. Ändra även datornamnet i <>service/<baseAddresses-elementet> från localhost till det fullständigt kvalificerade namnet på tjänstdatorn.

  7. Kopiera Service.cer-filen från tjänstkatalogen till klientkatalogen på klientdatorn.

  8. På klienten kör du setup.bat client i en kommandotolk för utvecklare för Visual Studio som öppnats med administratörsbehörighet. När du kör setup.bat med client argumentet skapas ett klientcertifikat med namnet client.com och klientcertifikatet exporteras till en fil med namnet Client.cer.

  9. I filen Client.exe.config på klientdatorn ändrar du slutpunktens adressvärde så att det matchar tjänstens nya adress. Gör detta genom att ersätta localhost med serverns fullständigt kvalificerade domännamn.

  10. Kopiera Client.cer-filen från klientkatalogen till tjänstkatalogen på servern.

  11. På klienten kör du ImportServiceCert.bat i en kommandotolk för utvecklare för Visual Studio som öppnas med administratörsbehörighet. Detta importerar tjänstcertifikatet från filen Service.cer till arkivet CurrentUser – Trusted Personer.

  12. På servern kör du ImportClientCert.bat i en kommandotolk för utvecklare för Visual Studio som öppnats med administratörsbehörighet. Detta importerar klientcertifikatet från filen Client.cer till arkivet LocalMachine – Trusted Personer.

  13. Starta Service.exe från kommandotolken på serverdatorn.

  14. Starta Client.exe från kommandotolken på klientdatorn. Om klienten och tjänsten inte kan kommunicera kan du läsa Felsökningstips för WCF-exempel.

Rensa efter exemplet

  1. Kör Cleanup.bat i exempelmappen när du har kört exemplet. Detta tar bort server- och klientcertifikaten från certifikatarkivet.

Kommentar

Det här skriptet tar inte bort tjänstcertifikat på en klient när du kör det här exemplet på flera datorer. Om du har kört WCF-exempel (Windows Communication Foundation) som använder certifikat mellan datorer måste du rensa de tjänstcertifikat som har installerats i arkivet CurrentUser – Trusted Personer. Det gör du genom att använda följande kommando: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> Till exempel: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.