read client certificate from httprequest C#

23,007

Solution 1

I wrote a identification web page a while back that looked for a client certificate and if found would display the certificate information. I believe that is what you are looking for... Here is the page:

<%@ 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>

Solution 2

I'm not sure what you need the client certificate for, but if you're using it for your own custom authentication or authorization, you may want to consider using the web server's security infrastructure instead of implementing your own. For example, you can configure IIS to require client certificates, map the certs to user accounts, and use Windows-based authentication. Of course, this doesn't necessarily work for your problem domain.

Solution 3

Ok it isn't completely clear but you have a website which requires the clients to authenticate themselves using certificates? Because thats what the Request.ClientCertificate property is for.

I say this because there's something odd about your question.

"I can get the certificate information from the page sending the request."

Pages in general do not really send requests the clients do.

To get the server cert you could open the X509Store and sift through the certs to find the one with the CN you need.

Solution 4

You have to configure your local IIS to accept (or require) client certificates.

Share:
23,007
John Som
Author by

John Som

Updated on March 15, 2020

Comments

  • John Som
    John Som about 4 years

    I am trying to read an X509 certificate using Request.ClientCertificate but nothing is returned. The certificate is definitely being attached to the request because I can get the certificate information from the page sending the request.

    I have tried reading the certificate from several different places but cannot seem to get it to work.

    I started with code from this KB Article. In the requested page I tried to print out some information about the certificate but nothing was returned in the response.

    This is running on IIS 5.1 and the communication is over SSL. This must be done using version 2 of the .Net framework

    Why does the certificate seem to dissappear?

    • DaveN59
      DaveN59 almost 15 years
      I didn't see this before asking essentially the same question just a few minutes ago. It's a little disheartening you don't have an answer yet...
    • CodeRot
      CodeRot almost 15 years
      What point in the lifecycle are you attempting to retreive it? Page OnInit, OnLoad, Master Page, Global.asax, etc?
    • John Som
      John Som almost 15 years
      I try to retrieve it in the Page_Load method
  • John Som
    John Som almost 15 years
    the "page sending the request" is the client in this case. It attaches an X509 certificate to the request for the page that I try to use the Request.ClientCertificate property in
  • jtate
    jtate about 7 years
    How is this the top answer? The OP said he is already using Page.Request.ClientCertificate.Certificate but it is returning null. This does nothing to help solve the issue.