如何使用 Microsoft Visual C# .NET 中的 HttpWebRequest 與 HttpWebResponse 類別傳送用戶端憑證

文章翻譯 文章翻譯
文章編號: 895971 - 檢視此文章適用的產品。

需求

如果要從 Microsoft ASP.NET 應用程式傳送用戶端憑證,您必須安裝下列 Hotfix 或 Service Pack:

Microsoft .NET Framework 1.0

您必須安裝 .NET Framework 1.0 Service Pack 3 (SP3),或是 Hotfix 817854。 如需詳細資訊,請按一下下面的文件編號,檢視「Microsoft 知識庫」中的文件:
817854 FIX:ASP.NET Web 應用程式無法將用戶端認證傳送到安全網站上

.NET Framework 1.1

您必須安裝 .NET Framework 1.1 Service Pack 1 (SP1),或是安裝 Hotfix 831138。 如需詳細資訊,請按一下下面的文件編號,檢視「Microsoft 知識庫」中的文件:
831138 FIX: A .NET Framework application that uses the System.Net.WebRequest method very frequently experiences OutOfMemoryException errors
全部展開 | 全部摺疊

在此頁中

簡介

本文將說明如何使用 Microsoft Visual C# NET 中的 HttpWebRequestHttpWebResponse 類別傳送用戶端憑證。

其他相關資訊

當 Web 伺服器需要用戶端憑證時,您可以使用 HttpWebRequestHttpWebResponse 類別來傳送。如果要取得能夠使用 HttpWebRequest 類別傳送用戶端憑證的憑證,請使用下列其中一種方法:

方法 1

使用 X509Certificate 類別從 .cer 檔案讀取憑證,然後設定 ClientCertificates 屬性。

方法 2

使用 CryptoAPI 呼叫從憑證存放區取得憑證,然後將 X509Certificate 類別設定為您從憑證存放區收到的憑證,接著就可以設定 ClientCertificates 屬性。

傳送用戶端憑證的需求條件

當您使用 ASP.NET 應用程式時,請確定下列需求已經完成:
  • 用戶端憑證必須安裝在 LOCAL_MACHINE 登錄區中,而不能在 CURRENT_USER 登錄區中。如果要確認已安裝用戶端憑證,請執行下列步驟:
    1. 按一下 [開始],再按 [執行],輸入 mmc,再按一下 [確定]
    2. 按一下 [檔案] 功能表上的 [新增/移除嵌入式管理單元]
    3. [新增/移除嵌入式管理單元] 對話方塊中,按一下 [新增]
    4. [新增獨立嵌入式管理單元] 對話方塊中,按一下 [憑證],再按一下 [新增]
    5. [憑證嵌入式管理單元] 對話方塊中,按一下 [電腦帳戶],然後按 [下一步]
    6. [選擇電腦] 對話方塊中,按一下 [完成]
    7. [新增獨立嵌入式管理單元] 對話方塊中,按一下 [關閉],再按一下 [確定]
    8. 展開 [憑證 (本機電腦)],展開 [個人],然後按一下 [憑證]
    在右側窗格中應該會列出用戶端憑證。
  • 您必須授予用戶端憑證的私密金鑰 ASP.NET 使用者帳戶權限。如果要授予用戶端憑證的私密金鑰 ASP.NET 使用者帳戶權限,請使用 WinHttpCertCfg.exe 工具。 如需詳細資訊,請按一下下面的文件編號,檢視「Microsoft 知識庫」中的文件:
    823193 INFO: How To Get Windows HTTP 5.1 Certificate And Trace Tools
    如需有關如何使用這項工具的詳細資訊,請造訪下列 Microsoft Developer Network (MSDN) 網站:
    WinHttpCertCfg.exe 憑證組態工具 (英文) http://msdn2.microsoft.com/en-us/library/aa384088.aspx

使用 .cer 檔案

方法 1 比較容易使用,不過您必須具有 .cer 檔案。如果沒有安裝 .cer 檔案,請使用 Microsoft Internet Explorer 匯出 .cer 檔案。

下列原始程式碼將會告訴您如何從 .cer 檔案取得憑證,以搭配 HttpWebRequest 類別使用。
//Uncomment the following code if you need a proxy. The boolean true is used to bypass the local address.
//WebProxy proxyObject = new WebProxy("Your Proxy value",true); 
//GlobalProxySelection.Select = proxyObject;

// Obtain the certificate. 
try
{
	//You must change the path to point to your .cer file location. 
	X509Certificate Cert = X509Certificate.CreateFromCertFile("C:\\mycert.cer");
	// Handle any certificate errors on the certificate from the server.
	ServicePointManager.CertificatePolicy = new CertPolicy();
	// You must change the URL to point to your Web server.
	HttpWebRequest Request = (HttpWebRequest)WebRequest.Create("https://YourServer/sample.asp");
	Request.ClientCertificates.Add(Cert);
	Request.UserAgent = "Client Cert Sample";
	Request.Method = "GET";
	HttpWebResponse Response = (HttpWebResponse)Request.GetResponse();
	// Print the repsonse headers.
	Console.WriteLine("{0}",Response.Headers);
	Console.WriteLine();
	// Get the certificate data.
	StreamReader sr = new StreamReader(Response.GetResponseStream(), Encoding.Default);
	int count;
	char [] ReadBuf = new char[1024];
	do
	{
		count = sr.Read(ReadBuf, 0, 1024);
		if (0 != count)
		{
			Console.WriteLine(new string(ReadBuf));
		}
						
	}while(count > 0);
}
catch(Exception e)
{
	Console.WriteLine(e.Message);
}
	

//Implement the ICertificatePolicy interface.
class CertPolicy: ICertificatePolicy
{
	public bool CheckValidationResult(ServicePoint srvPoint, 
X509Certificate certificate, WebRequest request, int certificateProblem)
	{
		// You can do your own certificate checking.
		// You can obtain the error values from WinError.h.

		// Return true so that any certificate will work with this sample.
		return true;
	}
}

使用 CryptoAPI 呼叫

如果必須從憑證存放區取得憑證,請使用 CryptoAPI 函式取得憑證,然後將憑證儲存在 X509Certificate 類別物件中。X509CertificateCollection 類別會列舉存放區中的所有憑證,然後將這些憑證放置在 X509CertificateCollection 類別物件中。

如果要取得特定的憑證,您必須變更類別程式碼以使用 CertFindCertificateInStore 函式取得特定的憑證。這個函式會在 Wincrypt.h 檔案中加以宣告。或者,您可以列舉 X509CertificateCollection 函式尋找所需的憑證。

下列範例程式碼會使用 CertEnumCertificatesInStore 函式傳回之集合中的第一個憑證。
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Runtime.InteropServices;

namespace SelectClientCert
{
	/// Sample that describes how how to select client cetificate and send it to the server.

	class MyCerts{

		private static int CERT_STORE_PROV_SYSTEM = 10;
		private static int CERT_SYSTEM_STORE_CURRENT_USER = (1 << 16);
		///private static int CERT_SYSTEM_STORE_LOCAL_MACHINE = (2 << 16);

		[DllImport("CRYPT32", EntryPoint="CertOpenStore", CharSet=CharSet.Unicode, SetLastError=true)]
		public static extern IntPtr CertOpenStore(
			int storeProvider, int encodingType,
			int hcryptProv, int flags, string pvPara);

		[DllImport("CRYPT32", EntryPoint="CertEnumCertificatesInStore", CharSet=CharSet.Unicode, SetLastError=true)]
		public static extern IntPtr CertEnumCertificatesInStore(
			IntPtr storeProvider,
			IntPtr prevCertContext);

		[DllImport("CRYPT32", EntryPoint="CertCloseStore", CharSet=CharSet.Unicode, SetLastError=true)]
		public static extern bool CertCloseStore(
			IntPtr storeProvider,
			int flags);
		
		X509CertificateCollection m_certs;

		public MyCerts(){
			m_certs = new X509CertificateCollection();
		}

		public int Init()
		{
			IntPtr storeHandle;
			storeHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, "MY");
			IntPtr currentCertContext;
			currentCertContext = CertEnumCertificatesInStore(storeHandle, (IntPtr)0);
			int i = 0;
			while (currentCertContext != (IntPtr)0) 
			{
				m_certs.Insert(i++, new X509Certificate(currentCertContext));
				currentCertContext = CertEnumCertificatesInStore(storeHandle, currentCertContext);
			}
			CertCloseStore(storeHandle, 0);

			return m_certs.Count;
		}
		
		public X509Certificate this [int index]
		{
			get 
			{
				// Check the index limits.
				if (index < 0 || index > m_certs.Count)
					return null;
				else
					return m_certs[index];
			}
		}
	};
	class MyHttpResource
	{
		String m_url;

		public MyHttpResource(string url){
			m_url = url;
		}

		public void GetFile(){

			HttpWebResponse  result = null;

			try{
			
				HttpWebRequest req = (HttpWebRequest)WebRequest.Create(m_url);
				req.Credentials  = CredentialCache.DefaultCredentials;

				///Method1
				//req.ClientCertificates.Add(X509Certificate.CreateFromCertFile("D:\\Temp\\cert\\c1.cer"));
		
				///Method2
				///Uses interop services
				MyCerts mycert = new MyCerts();
				if(mycert.Init() > 0)
					req.ClientCertificates.Add(mycert[0]);

				result = (HttpWebResponse)req.GetResponse();
				
				Stream ReceiveStream = result.GetResponseStream();
				Encoding encode = System.Text.Encoding.GetEncoding("utf-8");

				StreamReader sr = new StreamReader( ReceiveStream, encode );
				Console.WriteLine("\r\nResponse stream received");

				Char[] read = new Char[256];
				int count = sr.Read( read, 0, 256 );

				Console.WriteLine("HTTP Response...\r\n");
				while (count > 0) 
				{
					String str = new String(read, 0, count);
					Console.Write(str);
					count = sr.Read(read, 0, 256);
				}

			} 
			catch(WebException e) 
			{
            
				Console.WriteLine("\r\nError:");
				#if (DEBUG)
					Console.WriteLine(e.ToString());
				#else		
					Console.WriteLine(e.Message); 				
				#endif

			} 
			finally 
			{
				if ( result != null ) {
					result.Close();
				}
			}
				
		}
	
	}

	class CertSample
	{
		static void Main(string[] args)
		{
			try
			{
				if (args.Length < 1)
				{
					Console.WriteLine("No url is entered to download, returning.\n");
					Console.WriteLine("Usage: CertSample <urltoget>\n");
					Console.WriteLine("  e.g: CertSample https://servername \n"); 

					return;
				}

				MyHttpResource hr = new MyHttpResource(args[0]);
				hr.GetFile();
			}
			catch(Exception e)
			{
				Console.WriteLine(e.ToString());
			}
			return;
		}
	}
}

?考

如需詳細資訊,請造訪下列 Microsoft Developer Network (MSDN) 網站:
X509Certificate 類別 (英文)
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemsecuritycryptographyx509certificatesx509certificateclasstopic.asp
Platform SDK:密碼編譯 (英文)
http://msdn2.microsoft.com/en-us/library/aa380255.aspx

屬性

文章編號: 895971 - 上次校閱: 2007年1月16日 - 版次: 1.4
這篇文章中的資訊適用於:
  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
關鍵字:?
kbhowto kbhowtomaster kbinfo kbprogramming kbwebclasses kbsample kbcode kbaspnet kbdigitalcertificates KB895971
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。

提供意見

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com