强制HttpWebRequest发送客户端证书

本文关键字:客户端 证书 HttpWebRequest 强制 | 更新日期: 2023-09-27 18:07:17

我有一个p12证书,我以这种方式加载它:

X509Certificate2 certificate = new X509Certificate2(certName, password,
        X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet |
        X509KeyStorageFlags.Exportable);

它是正确加载的,事实上,如果我执行certificate.PrivateKey.ToXmlString(true);,它会返回一个完整的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();

服务器表示证书未发送/无效。

我的问题是:

  • 即使链构建为false,我如何发送证书
  • 是否有另一个类发布证书,但在发送证书之前不检查证书验证

非常感谢。Antonino

强制HttpWebRequest发送客户端证书

我解决了这个问题,重点是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);
}

我使用您的代码的修改版本创建了一个命令行程序,其中包含从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);
}

问题是将私钥安装到机器存储中,对于不在本地系统帐户下运行或具有明确私钥权限的进程,通常不允许将私钥用于客户端身份验证。您需要在当前用户存储中安装密钥:

X509Certificate2 certificate = new X509Certificate2(certName, password,
        X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet |
        X509KeyStorageFlags.Exportable);

我试图使用一个自签名证书,其唯一目的是";服务器证书";使用以下代码作为客户端证书:

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

有两种解决方案:

  1. 切换到.NET Framework
  2. 生成一个新的自签名证书,该证书具有用途/增强型密钥用法=客户端证书。然后代码也将在Core中工作