Validator Sertifikat X.509

Sampel ini menunjukkan cara mengimplementasikan Validator Sertifikat X.509 kustom. Hal ini berguna jika tidak ada mode Validasi Sertifikat X.509 bawaan yang sesuai untuk persyaratan aplikasi. Sampel ini menunjukkan layanan yang memiliki validator kustom yang menerima sertifikat yang diterbitkan sendiri. Klien menggunakan sertifikat semacam itu untuk mengautentikasi ke layanan.

Catatan: Karena siapa pun dapat membuat sertifikat yang diterbitkan sendiri, validator kustom yang digunakan oleh layanan menjadi kurang aman daripada perilaku default yang disediakan oleh ChainTrust X509CertificateValidationMode. Implikasi keamanan dari ini harus dipertimbangkan dengan cermat sebelum menggunakan logika validasi ini pada kode produksi.

Singkatnya, sampel ini menunjukkan cara:

  • Klien dapat diautentikasi menggunakan sertifikat X.509.

  • Server memvalidasi kredensial klien terhadap X509CertificateValidator kustom.

  • Server diautentikasi menggunakan sertifikat X.509 server.

Layanan ini mengekspos satu titik akhir untuk berkomunikasi dengan layanan, yang ditentukan menggunakan file konfigurasi App.config. Titik akhir terdiri dari alamat, pengikatan, dan kontrak. Pengikatan dikonfigurasi dengan wsHttpBinding standar yang default menggunakan WSSecurity dan autentikasi sertifikat klien. Perilaku layanan menentukan mode Kustom untuk memvalidasi sertifikat X.509 klien beserta jenis kelas validator. Perilaku ini juga menentukan sertifikat server menggunakan elemen serviceCertificate. Sertifikat server harus berisi nilai yang sama untuk SubjectName sepertifindValue di <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>

Konfigurasi titik akhir klien terdiri dari nama konfigurasi, alamat absolut untuk titik akhir layanan, pengikatan, dan kontrak. Pengikatan klien dikonfigurasi dengan mode dan pesan clientCredentialType yang sesuai.

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

Implementasi klien menetapkan sertifikat klien yang akan digunakan.

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

Sampel ini menggunakan X509CertificateValidator kustom untuk memvalidasi sertifikat. Sampel mengimplementasikan CustomX509CertificateValidator, yang berasal dari X509CertificateValidator. Untuk informasi selengkapnya, lihat dokumentasi tentang X509CertificateValidator. Sampel validator kustom khusus ini mengimplementasikan metode Validasi untuk menerima sertifikat X.509 yang diterbitkan sendiri seperti yang ditunjukkan dalam kode berikut.

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

Setelah validator diimplementasikan dalam kode layanan, host layanan harus diberi tahu tentang instans validator yang akan digunakan. Hal ini dilakukan menggunakan kode berikut.

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

Atau, Anda dapat melakukan hal yang sama dalam konfigurasi sebagai berikut.

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

Saat Anda menjalankan sampel, permintaan dan respons operasi ditampilkan di jendela konsol klien. Klien harus berhasil memanggil semua metode. Tekan ENTER di jendela klien untuk mematikan komputer klien.

Menyiapkan File Batch

File batch Setup.bat yang disertakan dengan sampel ini memungkinkan Anda untuk mengonfigurasi server dengan sertifikat yang relevan untuk menjalankan aplikasi yang dihosting sendiri yang memerlukan keamanan berbasis sertifikat server. File batch ini harus dimodifikasi untuk bekerja di seluruh komputer atau untuk bekerja dalam kasus yang tidak dihosting.

Berikut ini memberikan gambaran singkat tentang berbagai bagian file batch sehingga dapat dimodifikasi untuk dijalankan dalam konfigurasi yang sesuai:

  • Membuat sertifikat server:

    Baris dari file batch Setup.bat berikut membuat sertifikat server yang akan digunakan. Variabel %SERVER_NAME% menentukan nama server. Ubah variabel ini untuk menentukan nama server Anda sendiri. Nilai defaultnya adalah 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
    
  • Menginstal sertifikat server ke penyimpanan sertifikat tepercaya klien:

    Baris berikut dalam file batch Setup.bat menyalin sertifikat server ke penyimpanan orang tepercaya klien. Langkah ini diperlukan karena sertifikat yang dihasilkan oleh Makecert.exe tidak dipercaya secara implisit oleh sistem klien. Jika Anda sudah memiliki sertifikat yang berakar pada sertifikat akar tepercaya klien—misalnya, sertifikat yang dikeluarkan Microsoft—langkah mengisi penyimpanan sertifikat klien dengan sertifikat server tidak diperlukan.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Membuat sertifikat klien:

    Baris berikut dari file batch Setup.bat membuat sertifikat klien yang akan digunakan. Variabel %USER_NAME% menentukan nama klien. Nilai ini ditetapkan ke "test1" karena ini merupakan nama yang dicari kode klien. Jika mengubah nilai %USER_NAME%, Anda harus mengubah nilai yang sesuai pada file sumber Client.cs dan membangun ulang klien.

    Sertifikat disimpan di penyimpanan Saya (Pribadi) di lokasi penyimpanan 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
    
  • Menginstal sertifikat klien ke penyimpanan sertifikat tepercaya server:

    Baris berikut dalam file batch Setup.bat menyalin sertifikat klien ke penyimpanan orang tepercaya. Langkah ini diperlukan karena sertifikat yang dihasilkan oleh Makecert.exe tidak dipercaya secara implisit oleh sistem server. Jika Anda sudah memiliki sertifikat yang berakar dalam sertifikat akar tepercaya—misalnya, sertifikat yang dikeluarkan Microsoft—langkah mengisi penyimpanan sertifikat server dengan sertifikat klien ini tidak diperlukan.

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

Untuk menyiapkan dan membangun sampel

  1. Untuk membangun solusi, ikuti instruksi dalam Membangun Sampel Windows Communication Foundation.

  2. Untuk menjalankan sampel dalam konfigurasi tunggal atau lintas komputer, gunakan petunjuk berikut.

Untuk menjalankan sampel di komputer yang sama

  1. Jalankan Setup.bat dari folder instal sampel di dalam perintah Visual Studio yang dibuka dengan hak istimewa administrator. Ini memasang semua sertifikat yang diperlukan untuk menjalankan sampel.

    Penting

    File batch Setup.bat dirancang untuk dijalankan dari Prompt Perintah Visual Studio. Variabel lingkungan PATH yang diatur dalam Perintah Visual Studio menunjuk ke direktori yang berisi executable yang diperlukan oleh skrip Setup.bat.

  2. Luncurkan Service.exe dari service\bin.

  3. Luncurkan Client.exe dari \client\bin. Aktivitas klien ditampilkan di aplikasi konsol klien.

  4. Jika klien dan layanan tidak dapat berkomunikasi, lihat Tips Pemecahan Masalah untuk Sampel WCF.

Untuk menjalankan sampel di seluruh komputer

  1. Buat direktori di komputer layanan.

  2. Salin file program layanan \service\bin ke direktori virtual di komputer layanan. Salin juga file Setup.bat, Cleanup.bat, GetComputerName.vbs, dan ImportClientCert.bat ke komputer layanan.

  3. Buat direktori di komputer klien untuk binari klien.

  4. Salin file program klien ke direktori klien di komputer klien. Salin juga file Setup.bat, Cleanup.bat, dan ImportServiceCert.bat ke klien.

  5. Di server, jalankan setup.bat service di Perintah Pengembang untuk Visual Studio yang dibuka dengan hak istimewa administrator. Menjalankan setup.bat dengan argumen service akan membuat sertifikat layanan dengan nama domain komputer yang sepenuhnya memenuhi syarat dan mengekspor sertifikat layanan ke file yang dinamai Service.cer.

  6. Edit Service.exe.config untuk merefleksikan nama sertifikat baru (dalam atribut findValue di <serviceCertificate>) yang sama dengan nama domain komputer yang sepenuhnya memenuhi syarat. Ubah juga nama komputer pada elemen <service>/<baseAddresses> dari localhost menjadi nama komputer layanan yang sepenuhnya memenuhi syarat.

  7. Salin file Service.cer dari direktori layanan ke direktori klien di komputer klien.

  8. Di komputer klien, jalankan setup.bat client di Perintah Pengembang untuk Visual Studio yang dibuka dengan hak istimewa administrator. Menjalankan setup.bat dengan argumen client membuat sertifikat klien bernama client.com dan mengekspor sertifikat klien ke file bernama Client.cer.

  9. Di file Client.exe.config di komputer klien, ubah nilai alamat titik akhir agar sesuai dengan alamat baru layanan Anda. Lakukan ini dengan mengganti localhost dengan nama domain server yang sepenuhnya memenuhi syarat.

  10. Salin file Client.cer dari direktori klien ke direktori layanan di server.

  11. Di klien, jalankan ImportServiceCert.bat di Perintah Pengembang untuk Visual Studio yang dibuka dengan hak istimewa administrator. Langkah ini mengimpor sertifikat layanan dari file Service.cer ke penyimpanan CurrentUser - TrustedPeople.

  12. Di server, jalankan ImportClientCert.bat di Perintah Pengembang untuk Visual Studio yang dibuka dengan hak administrator. Langkah ini mengimpor sertifikat klien dari file Client.cer ke penyimpanan LocalMachine - TrustedPeople.

  13. Di komputer server, luncurkan Service.exe dari jendela perintah.

  14. Di komputer klien, luncurkan Client.exe dari jendela perintah. Jika klien dan layanan tidak dapat berkomunikasi, lihat Tips Pemecahan Masalah untuk Sampel WCF.

Untuk membersihkan sampel

  1. Jalankan Cleanup.bat di folder sampel setelah Anda selesai menjalankan sampel. Langkah ini menghapus sertifikat server dan klien dari penyimpanan sertifikat.

Catatan

Skrip ini tidak menghapus sertifikat layanan pada klien saat menjalankan sampel ini di seluruh komputer. Jika Anda telah menjalankan sampel Windows Communication Foundation (WCF) yang menggunakan sertifikat di seluruh komputer, pastikan untuk menghapus sertifikat layanan yang telah terpasang di penyimpanan CurrentUser - TrustedPeople. Untuk melakukannya, gunakan perintah berikut: certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> Misalnya: certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com.