强制 HttpWebRequest 发送客户端证书
Force HttpWebRequest to send client certificate
我有一个 p12 证书,我以这种方式加载它:
X509Certificate2 certificate = new X509Certificate2(certName, password,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
加载正确,事实上如果我certificate.PrivateKey.ToXmlString(true);
它returns一个完整的xml没有错误。
但如果我这样做:
try
{
X509Chain chain = new X509Chain();
var chainBuilt = chain.Build(certificate);
Console.WriteLine("Chain building status: "+ chainBuilt);
if (chainBuilt == false)
foreach (X509ChainStatus chainStatus in chain.ChainStatus)
Console.WriteLine("Chain error: "+ chainStatus.Status);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
它写道:
Chain building status: False
Chain error: RevocationStatusUnknown
Chain error: OfflineRevocation
所以当我这样做时:
ServicePointManager.CheckCertificateRevocationList = false;
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
ServicePointManager.Expect100Continue = true;
Console.WriteLine("connessione a:" + host);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.PreAuthenticate = true;
req.AllowAutoRedirect = true;
req.ClientCertificates.Add(certificate);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
服务器说证书不是sent/valid。
我的问题是:
- 即使链构建错误,我如何发送证书?
- 还有另一个 class 到 post 的证书在发送之前不检查证书验证吗?
非常感谢。
安东尼奥
问题是您将私钥安装到机器存储,对于不在本地系统帐户下 运行 或具有显式私钥权限的进程,通常不允许将其用于客户端身份验证。您需要在当前用户存储中安装密钥:
X509Certificate2 certificate = new X509Certificate2(certName, password,
X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
我使用您的代码的修改版本创建了一个命令行程序,其中包含从 IE 导出的私钥的 pfx 证书,我能够对安全网站进行身份验证并检索受保护的页面:
string host = @"https://localhost/";
string certName = @"C:\temp\cert.pfx";
string password = @"password";
try
{
X509Certificate2 certificate = new X509Certificate2(certName, password);
ServicePointManager.CheckCertificateRevocationList = false;
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
ServicePointManager.Expect100Continue = true;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.PreAuthenticate = true;
req.AllowAutoRedirect = true;
req.ClientCertificates.Add(certificate);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
}
stream.Close();
}
catch(Exception e)
{
Console.WriteLine(e);
}
我解决了问题,重点是一个 P12 文件(作为 PFX)包含超过 1 个证书,因此必须以这种方式加载:
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
并以这种方式添加到 HttpWebRequest:request.ClientCertificates = certificates;
感谢大家的支持
完整的示例代码
string host = @"https://localhost/";
string certName = @"C:\temp\cert.pfx";
string password = @"password";
try
{
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.AllowAutoRedirect = true;
req.ClientCertificates = certificates;
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
}
stream.Close();
}
catch(Exception e)
{
Console.WriteLine(e);
}
我试图使用一个自签名证书,其唯一目的是“服务器证书”作为使用以下代码的客户端证书:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://serverurl.com/test/certauth");
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, "a909502dd82ae41433e6f83886b00d4277a32a7b", true)[0];
request.ClientCertificates.Add(certificate);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
我了解到 .NET Framework 会接受这样的证书作为客户端证书,但 .NET Core 不会(参见 https://github.com/dotnet/runtime/issues/26531)。
有两种解决方法:
- 切换到 .NET Framework
- 生成一个新的自签名证书,该证书具有用途/增强型密钥用法 = 客户端证书。然后代码也将在 Core 中工作。
我有一个 p12 证书,我以这种方式加载它:
X509Certificate2 certificate = new X509Certificate2(certName, password,
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
加载正确,事实上如果我certificate.PrivateKey.ToXmlString(true);
它returns一个完整的xml没有错误。
但如果我这样做:
try
{
X509Chain chain = new X509Chain();
var chainBuilt = chain.Build(certificate);
Console.WriteLine("Chain building status: "+ chainBuilt);
if (chainBuilt == false)
foreach (X509ChainStatus chainStatus in chain.ChainStatus)
Console.WriteLine("Chain error: "+ chainStatus.Status);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
它写道:
Chain building status: False
Chain error: RevocationStatusUnknown
Chain error: OfflineRevocation
所以当我这样做时:
ServicePointManager.CheckCertificateRevocationList = false;
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
ServicePointManager.Expect100Continue = true;
Console.WriteLine("connessione a:" + host);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.PreAuthenticate = true;
req.AllowAutoRedirect = true;
req.ClientCertificates.Add(certificate);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
服务器说证书不是sent/valid。
我的问题是:
- 即使链构建错误,我如何发送证书?
- 还有另一个 class 到 post 的证书在发送之前不检查证书验证吗?
非常感谢。 安东尼奥
问题是您将私钥安装到机器存储,对于不在本地系统帐户下 运行 或具有显式私钥权限的进程,通常不允许将其用于客户端身份验证。您需要在当前用户存储中安装密钥:
X509Certificate2 certificate = new X509Certificate2(certName, password,
X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet |
X509KeyStorageFlags.Exportable);
我使用您的代码的修改版本创建了一个命令行程序,其中包含从 IE 导出的私钥的 pfx 证书,我能够对安全网站进行身份验证并检索受保护的页面:
string host = @"https://localhost/";
string certName = @"C:\temp\cert.pfx";
string password = @"password";
try
{
X509Certificate2 certificate = new X509Certificate2(certName, password);
ServicePointManager.CheckCertificateRevocationList = false;
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
ServicePointManager.Expect100Continue = true;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.PreAuthenticate = true;
req.AllowAutoRedirect = true;
req.ClientCertificates.Add(certificate);
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
}
stream.Close();
}
catch(Exception e)
{
Console.WriteLine(e);
}
我解决了问题,重点是一个 P12 文件(作为 PFX)包含超过 1 个证书,因此必须以这种方式加载:
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
并以这种方式添加到 HttpWebRequest:request.ClientCertificates = certificates;
感谢大家的支持
完整的示例代码
string host = @"https://localhost/";
string certName = @"C:\temp\cert.pfx";
string password = @"password";
try
{
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);
ServicePointManager.ServerCertificateValidationCallback = (a, b, c, d) => true;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(host);
req.AllowAutoRedirect = true;
req.ClientCertificates = certificates;
req.Method = "POST";
req.ContentType = "application/x-www-form-urlencoded";
string postData = "login-form-type=cert";
byte[] postBytes = Encoding.UTF8.GetBytes(postData);
req.ContentLength = postBytes.Length;
Stream postStream = req.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
WebResponse resp = req.GetResponse();
Stream stream = resp.GetResponseStream();
using (StreamReader reader = new StreamReader(stream))
{
string line = reader.ReadLine();
while (line != null)
{
Console.WriteLine(line);
line = reader.ReadLine();
}
}
stream.Close();
}
catch(Exception e)
{
Console.WriteLine(e);
}
我试图使用一个自签名证书,其唯一目的是“服务器证书”作为使用以下代码的客户端证书:
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create("https://serverurl.com/test/certauth");
X509Store store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
var certificate = store.Certificates.Find(X509FindType.FindByThumbprint, "a909502dd82ae41433e6f83886b00d4277a32a7b", true)[0];
request.ClientCertificates.Add(certificate);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
我了解到 .NET Framework 会接受这样的证书作为客户端证书,但 .NET Core 不会(参见 https://github.com/dotnet/runtime/issues/26531)。
有两种解决方法:
- 切换到 .NET Framework
- 生成一个新的自签名证书,该证书具有用途/增强型密钥用法 = 客户端证书。然后代码也将在 Core 中工作。