本文將說明如何使用 Microsoft Visual C# NET 中的
HttpWebRequest 與
HttpWebResponse 類別傳送用戶端憑證。
當 Web 伺服器需要用戶端憑證時,您可以使用
HttpWebRequest 和
HttpWebResponse 類別來傳送。如果要取得能夠使用
HttpWebRequest 類別傳送用戶端憑證的憑證,請使用下列其中一種方法:
方法 1
使用
X509Certificate 類別從 .cer 檔案讀取憑證,然後設定
ClientCertificates 屬性。
方法 2
使用 CryptoAPI 呼叫從憑證存放區取得憑證,然後將
X509Certificate 類別設定為您從憑證存放區收到的憑證,接著就可以設定
ClientCertificates 屬性。
傳送用戶端憑證的需求條件
當您使用 ASP.NET 應用程式時,請確定下列需求已經完成:
- 用戶端憑證必須安裝在 LOCAL_MACHINE 登錄區中,而不能在 CURRENT_USER
登錄區中。如果要確認已安裝用戶端憑證,請執行下列步驟:
- 按一下 [開始],再按 [執行],輸入
mmc,再按一下 [確定]。
- 按一下 [檔案] 功能表上的
[新增/移除嵌入式管理單元]。
- 在 [新增/移除嵌入式管理單元] 對話方塊中,按一下
[新增]。
- 在 [新增獨立嵌入式管理單元] 對話方塊中,按一下
[憑證],再按一下 [新增]。
- 在 [憑證嵌入式管理單元] 對話方塊中,按一下
[電腦帳戶],然後按 [下一步]
- 在 [選擇電腦] 對話方塊中,按一下
[完成]。
- 在 [新增獨立嵌入式管理單元] 對話方塊中,按一下
[關閉],再按一下 [確定]。
- 展開 [憑證 (本機電腦)],展開
[個人],然後按一下 [憑證]。
在右側窗格中應該會列出用戶端憑證。 - 您必須授予用戶端憑證的私密金鑰 ASP.NET 使用者帳戶權限。如果要授予用戶端憑證的私密金鑰 ASP.NET
使用者帳戶權限,請使用 WinHttpCertCfg.exe 工具。
如需詳細資訊,請按一下下面的文件編號,檢視「Microsoft 知識庫」中的文件:
823193?
(http://support.microsoft.com/kb/823193/
)
INFO: How To Get Windows HTTP 5.1 Certificate And Trace Tools
如需有關如何使用這項工具的詳細資訊,請造訪下列 Microsoft Developer Network
(MSDN) 網站:
使用 .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) 網站: