您目前已離線,請等候您的網際網路重新連線

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

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

需求

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

Microsoft .NET Framework 1.0

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

.NET Framework 1.1

您必須安裝 .NET Framework 1.1 Service Pack 1 (SP1),或是安裝 Hotfix 831138。 如需詳細資訊,請按一下下面的文件編號,檢視「Microsoft 知識庫」中的文件:
831138FIX: A .NET Framework application that uses the System.Net.WebRequest method very frequently experiences OutOfMemoryException errors
其他相關資訊
當 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 知識庫」中的文件:
    823193INFO: 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) 網站:
Platform SDK:密碼編譯 (英文)
http://msdn2.microsoft.com/en-us/library/aa380255.aspx
內容

文章識別碼:895971 - 最後檢閱時間:01/16/2007 08:22:00 - 修訂: 1.4

  • Microsoft .NET Framework 1.1
  • Microsoft .NET Framework 1.0
  • kbhowto kbhowtomaster kbinfo kbprogramming kbwebclasses kbsample kbcode kbaspnet kbdigitalcertificates KB895971
意見反應