共用方式為


X.509 憑證驗證程式

此範例示範如何實作自定義 X.509 憑證驗證程式。 在沒有任何內建 X.509 憑證驗證模式適用於應用程式需求的情況下,這非常有用。 此範例示範的服務具有可接受自我發行憑證的自定義驗證程式。 用戶端會使用這類憑證向服務進行驗證。

注意:由於任何人都可以建構自我發行的憑證,服務所使用的自定義驗證程式比 ChainTrust X509CertificateValidationMode 所提供的預設行為不安全。 在實際執行程式代碼中使用此驗證邏輯之前,應該仔細考慮這一點的安全性影響。

總而言之,此範例示範如何:

  • 用戶端可以使用 X.509 憑證進行驗證。

  • 伺服器會使用自定義的 X509CertificateValidator 驗證用戶端憑證。

  • 伺服器是使用伺服器的 X.509 憑證進行驗證。

服務會公開單一端點,以便與服務通訊,並使用組態檔 App.config所定義。端點是由位址、系結和合約所組成。 繫結是以使用wsHttpBinding和用戶端憑證驗證的標準WSSecurity進行設定。 服務行為會指定自定義模式來驗證用戶端 X.509 憑證,以及驗證程式類別的類型。 行為也會使用 serviceCertificate 元素來指定伺服器證書。 伺服器證書必須包含與SubjectNamefindValue相同的<值。

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

用戶端端點組態包含組態名稱、服務端點的絕對位址、系結和合約。 用戶端系結是使用適當的模式和訊息 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>

用戶端實作會設定要使用的用戶端憑證。

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

此範例會使用自定義 X509CertificateValidator 來驗證憑證。 此範例會實作衍生自 X509CertificateValidator的 CustomX509CertificateValidator。 如需詳細資訊,請參閱相關 X509CertificateValidator 文件。 這個特定的自定義驗證程式範例會實作 Validate 方法,以接受任何自我發行的 X.509 憑證,如下列程式代碼所示。

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

在服務程式代碼中實作驗證程序之後,必須通知服務主機要使用的驗證程序實例。 這是使用下列程式代碼完成的。

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

或者,您可以在設定中執行相同的動作,如下所示。

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

當您執行範例時,作業要求和回應會顯示在用戶端控制台視窗中。 客戶端應該要成功呼叫所有方法。 在客戶端視窗中按 ENTER 鍵以關閉用戶端。

設定批次處理檔

此範例隨附的 Setup.bat 批處理檔可讓您設定具有相關憑證的伺服器,以執行需要伺服器證書式安全性的自我裝載應用程式。 必須修改此批處理檔,才能跨電腦使用,或在非托管的情況下操作。

下列提供批處理檔不同區段的簡短概觀,以便修改它們以在適當的組態中執行:

  • 建立伺服器憑證:

    Setup.bat 批處理檔中的下列幾行會建立要使用的伺服器證書。 %SERVER_NAME% 變數會指定伺服器名稱。 變更此變數以指定您自己的伺服器名稱。 預設值為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
    
  • 將伺服器憑證安裝到用戶端的受信任憑證儲存中:

    Setup.bat 批處理檔中的下列幾行會將伺服器證書複製到用戶端信任的人員存放區。 這是必要步驟,因為客戶端系統不會隱含信任 Makecert.exe 所產生的憑證。 如果您已經有根植於用戶端受信任的根證書的憑證,例如,Microsoft 發行的憑證,則不需要執行使用伺服器證書填入用戶端證書存儲的此步驟。

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • 建立客戶端憑證:

    Setup.bat 批處理檔中的下列幾行會建立要使用的用戶端憑證。 %USER_NAME% 變數會指定客戶端名稱。 此值會設定為 「test1」 因為這是用戶端程式代碼所尋找的名稱。 如果您變更 %USER_NAME% 的值,則必須變更Client.cs來源檔案中的對應值,然後重建用戶端。

    憑證會儲存在 CurrentUser 存放區位置下的 My (Personal) 存放區中。

    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
    
  • 將客戶端憑證安裝到伺服器信任的憑證儲存中:

    Setup.bat 批處理檔中的下列幾行會將客戶端憑證複製到受信任的人員存放區。 這是必要步驟,因為伺服器系統不會隱含信任 Makecert.exe 所產生的憑證。 如果您已經擁有一個包含在受信任的根憑證中的憑證,例如Microsoft核發的憑證,則不需要執行將用戶端憑證填入伺服器憑證存儲區的這個步驟。

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

若要設定和建置範例

  1. 若要建置解決方案,請遵循 建置 Windows Communication Foundation 範例中的指示。

  2. 若要在單一或跨計算機組態中執行範例,請使用下列指示。

在同一部電腦上執行這個範例

  1. 在以系統管理員許可權開啟的 Visual Studio 命令提示字元內,從範例安裝資料夾執行 Setup.bat。 這會安裝執行範例所需的所有憑證。

    這很重要

    Setup.bat 批處理文件的設計目的是要從 Visual Studio 命令提示字元執行。 Visual Studio 命令提示字元內設定的PATH環境變數會指向包含 Setup.bat 腳本所需可執行檔案的目錄。

  2. 從 service\bin 啟動 Service.exe。

  3. 從 \client\bin 啟動 Client.exe。 用戶端活動會顯示在用戶端主控台應用程式上。

  4. 如果客戶端和服務無法通訊,請參閱 WCF 範例 疑難解答秘訣。

在多台電腦上執行範例

  1. 在服務電腦上建立目錄。

  2. 將服務程式檔案從 \service\bin 複製到服務計算機上的虛擬目錄。 同時將 Setup.bat、Cleanup.bat、GetComputerName.vbs 和 ImportClientCert.bat 檔案複製到服務計算機。

  3. 在用戶端電腦上為用戶端二進位檔建立目錄。

  4. 將用戶端程式檔案複製到用戶端電腦上的客戶端目錄。 同時將 Setup.bat、Cleanup.bat和 ImportServiceCert.bat 檔案複製到用戶端。

  5. 在伺服器上,以系統管理員許可權開啟的 Visual Studio 開發人員命令提示字元中運行 setup.bat service。 使用 setup.bat 自變數執行 service 會建立具有電腦完整域名的服務憑證,並將服務憑證導出至名為 Service.cer 的檔案。

  6. 編輯 Service.exe.config,使其反映與電腦完整網域名稱相同的新憑證名稱(在 findValue<serviceCertificate> 的屬性中)。 此外,將 <service>/<baseAddresses> 元素中的電腦名稱從 localhost 變更為服務電腦的完整資格名稱。

  7. 將Service.cer檔案從服務目錄複製到用戶端電腦上的客戶端目錄。

  8. 在用戶端上,以系統管理員許可權開啟的 Visual Studio 開發人員命令提示字元中執行 setup.bat client。 使用 setup.bat 自變數執行 client 會建立名為 client.com 的用戶端憑證,並將客戶端憑證導出至名為 Client.cer 的檔案。

  9. 在用戶端電腦上的 Client.exe.config 檔案中,變更端點的位址值,以符合您服務的新位址。 將localhost替換為伺服器的完整網域名稱。

  10. 將Client.cer檔案從客戶端目錄複製到伺服器上的服務目錄。

  11. 在用戶端上,在以系統管理員許可權開啟的Visual Studio開發人員命令提示字元中執行 ImportServiceCert.bat。 這會將服務憑證從 Service.cer 檔案匯入 CurrentUser - TrustedPeople 存放區。

  12. 在伺服器上,在以系統管理員許可權開啟的Visual Studio開發人員命令提示字元中執行 ImportClientCert.bat。 這會將客戶端憑證從 Client.cer 檔案匯入 LocalMachine - TrustedPeople 存放區。

  13. 在伺服器電腦上,從命令提示字元窗口啟動 Service.exe。

  14. 在用戶端電腦上,從命令提示字元窗口啟動 Client.exe。 如果客戶端和服務無法通訊,請參閱 WCF 範例 疑難解答秘訣。

在範例之後清除

  1. 在您完成執行範例之後,請在samples資料夾中執行 Cleanup.bat。 這會從證書存儲中移除伺服器和客戶端憑證。

備註

在跨電腦執行此範例時,此腳本不會移除用戶端上的服務憑證。 如果您已執行跨計算機使用憑證的 Windows Communication Foundation (WCF) 範例,請務必清除已在 CurrentUser - TrustedPeople 存放區中安裝的服務憑證。 若要這樣做,請使用下列命令:certmgr -del -r CurrentUser -s TrustedPeople -c -n <Fully Qualified Server Machine Name> 例如:certmgr -del -r CurrentUser -s TrustedPeople -c -n server1.contoso.com