从C#的httprequest中读取客户端证书

11

我正在尝试使用Request.ClientCertificate读取X509证书,但没有返回任何内容。该证书肯定已经附加到请求中,因为我可以从发送请求的页面获得证书信息。

我已经尝试从几个不同的位置读取证书,但似乎无法使其工作。

我从这篇KB文章开始编写代码。在请求的页面中,我尝试打印一些有关证书的信息,但响应中未返回任何内容。

这在运行IIS 5.1上,并且通信是通过SSL进行的。必须使用.NET框架的2.0版本来完成此操作。

为什么证书似乎会消失?


我在几分钟前问了基本相同的问题之前没有看到这个。你还没有答案有点令人沮丧... - DaveN59
1
你正在尝试获取生命周期的哪个点? Page OnInit,OnLoad,Master Page,Global.asax等吗? - CodeRot
我尝试在Page_Load方法中检索它。 - Sean
4个回答

12

我一段时间前写了一个身份验证网页,它寻找客户端证书,如果找到,就会显示证书信息。我相信这就是您要找的... 这是该页面:

<%@ Page Language="C#" Trace="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ Import Namespace="System.Security.Cryptography.X509Certificates" %>
<%@ Import Namespace="System.Security.Cryptography" %>

<script runat="server">
    //protected void Page_Load(object sender, EventArgs e)
    //{ }

    void LoadCertInfo()
    {
        string para = "<div style='margin: 10px 0 0 0; font-weight: bold'>{0}</div>";
        string subpara = "<div style='margin-left: 15px; font-size: 90%'>{0}</div>";

        if (Page.Request.ClientCertificate.IsPresent)
        {
            Response.Write("<hr /><div style='width: 500px; margin: 20px auto'>");
            Response.Write("<h3 style='width: 500px; margin: 20px auto'>Client Certificate Information</h3>");
            try
            {
                X509Certificate2 x509Cert2 = new X509Certificate2(Page.Request.ClientCertificate.Certificate);

                Response.Write(string.Format(para, "Issued To:"));
                Response.Write(string.Format(subpara, x509Cert2.Subject));

                Response.Write(string.Format(para, "Issued By:"));
                Response.Write(string.Format(subpara, x509Cert2.Issuer));

                Response.Write(string.Format(para, "Friendly Name:"));
                Response.Write(string.Format(subpara, string.IsNullOrEmpty(x509Cert2.FriendlyName) ? "(None Specified)" : x509Cert2.FriendlyName));

                Response.Write(string.Format(para, "Valid Dates:"));
                Response.Write(string.Format(subpara, "From: " + x509Cert2.GetEffectiveDateString()));
                Response.Write(string.Format(subpara, "To: " + x509Cert2.GetExpirationDateString()));

                Response.Write(string.Format(para, "Thumbprint:"));
                Response.Write(string.Format(subpara, x509Cert2.Thumbprint));

                //Response.Write(string.Format(para, "Public Key:"));
                //Response.Write(string.Format(subpara, x509Cert2.GetPublicKeyString()));

                #region EKU Section - Retrieve EKU info and write out each OID
                X509EnhancedKeyUsageExtension ekuExtension = (X509EnhancedKeyUsageExtension)x509Cert2.Extensions["Enhanced Key Usage"];
                if (ekuExtension != null)
                {
                    Response.Write(string.Format(para, "Enhanced Key Usages (" + ekuExtension.EnhancedKeyUsages.Count.ToString() + " found)"));

                    OidCollection ekuOids = ekuExtension.EnhancedKeyUsages;
                    foreach (Oid ekuOid in ekuOids)
                        Response.Write(string.Format(subpara, ekuOid.FriendlyName + " (OID: " + ekuOid.Value + ")"));
                }
                else
                {
                    Response.Write(string.Format(para, "No EKU Section Data"));
                }
                #endregion // EKU Section

                #region Subject Alternative Name Section
                X509Extension sanExtension = (X509Extension)x509Cert2.Extensions["Subject Alternative Name"];
                if (sanExtension != null)
                {
                    Response.Write(string.Format(para, "Subject Alternative Name:"));
                    Response.Write(string.Format(subpara, sanExtension.Format(true)));
                }
                else
                {
                    Response.Write(string.Format(para, "No Subject Alternative Name Data"));
                }

                #endregion // Subject Alternative Name Section

                #region Certificate Policies Section
                X509Extension policyExtension = (X509Extension)x509Cert2.Extensions["Certificate Policies"];
                if (policyExtension != null)
                        {
                            Response.Write(string.Format(para, "Certificate Policies:"));
                            Response.Write(string.Format(subpara, policyExtension.Format(true)));
                        }
                        else
                        {
                            Response.Write(string.Format(para, "No Certificate Policies Data"));
                        }
                #endregion //Certificate Policies Section


                // Example on how to enumerate all extensions
                //foreach (X509Extension extension in x509Cert2.Extensions)
                //    Response.Write(string.Format(para, extension.Oid.FriendlyName + "(" + extension.Oid.Value + ")"));
            }
            catch (Exception ex)
            {
                Response.Write(string.Format(para, "An error occured:"));
                Response.Write(string.Format(subpara, ex.Message));
                Response.Write(string.Format(subpara, ex.StackTrace));
            }
            finally
            {
                Response.Write("</div>");
            }
        }
    }
</script>
<html>
  <head runat="server">
    <title><% Page.Response.Write(System.Environment.MachineName); %></title>
  </head>
  <body>
      <% LoadCertInfo();  %>
  </body>
</html>

1
这怎么成为最佳答案了?原帖中说他已经在使用Page.Request.ClientCertificate.Certificate,但是它返回的是null。这对解决问题没有任何帮助。 - jtate

2
好的,这不是完全清楚,但您有一个需要客户使用证书进行身份验证的网站?因为这就是Request.ClientCertificate属性的作用。
我这样说是因为您的问题有些奇怪。
“我可以从发送请求的页面获取证书信息。”
通常情况下,页面并不真正发送请求,而是客户端执行此操作。
要获取服务器证书,您可以打开X509Store并筛选证书,找到需要的CN。

在这种情况下,“发送请求的页面”是客户端。它会将X509证书附加到请求中,以便我尝试使用Request.ClientCertificate属性来访问该页面。 - Sean

2

我不确定你需要客户端证书的用途,但如果您想将其用于自定义身份验证或授权,建议考虑使用Web服务器的安全基础结构而不是实现自己的。例如,您可以配置IIS要求客户端证书,将证书映射到用户帐户,并使用基于Windows的身份验证。当然,这并不一定适用于您的问题域。


1

您需要配置本地IIS以接受(或要求)客户端证书。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接