Reputation: 4051
I have an asp.net application and I need to authenticate users using X509 certificates. That is, the user must install a certificate issued by me so that he can browse my website and I can identify which user is, by this certificate.
I have already configured SSL on IIS, but it's not what I'm looking for right now, and I don't know where to start.
How can I achieve this in asp.net c#?
Upvotes: 28
Views: 42433
Reputation: 27104
Assuming you have IIS 7.0 or higher, you can configure Client Certificate Mapping Authentication
Using Active Directory (Extremely easy, leaves the mapping work to the AD server)
<location path="Default Web Site">
<system.webServer>
<security>
<access sslFlags="Ssl, SslNegotiateCert" />
<authentication>
<windowsAuthentication enabled="false" />
<anonymousAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<basicAuthentication enabled="false" />
<clientCertificateMappingAuthentication enabled="true" />
</authentication>
</security>
</system.webServer>
</location>
Or using IIS (More configuration needed in IIS, needs access to the client certificate, but works standalone, no roundtrips to the AD). In this case, you specify (one or more) user credentials and
Configuration (many to one):
<location path="Default Web Site">
<system.webServer>
<security>
<authentication>
<windowsAuthentication enabled="false" />
<anonymousAuthentication enabled="false" />
<digestAuthentication enabled="false" />
<basicAuthentication enabled="false" />
<iisClientCertificateMappingAuthentication enabled="true"
manyToOneCertificateMappingsEnabled="true">
<manyToOneMappings>
<add name="Contoso Employees"
enabled="true"
permissionMode="Allow"
userName="Username"
password="[enc:AesProvider:57686f6120447564652c2049495320526f636b73:enc]">
<rules>
<add certificateField="Subject"
certificateSubField="O"
matchCriteria="Contoso"
compareCaseSensitive="true" />
</rules>
</add>
</manyToOneMappings>
</iisClientCertificateMappingAuthentication>
</authentication>
<access sslFlags="Ssl, SslNegotiateCert" />
</security>
</system.webServer>
</location>
(Sample configuration rather shamelessly copied from the samples on the iis.net documentation pages, which are quite elaborate.)
Or you can configure your application to use Claims-Based Authentication with a Security Token Service (STS) that authenticates clients based on client certificates. ADFS 2.0 can fullfil this role, or if it is not available, you could look at the Thinktecture Identity Server.
Upvotes: 13
Reputation: 21475
To create a secure authentication mechanism you would use both client certificates and username / password. The reason is that a certificate is something that can be stolen (copied) but a password is something that is only known by the person. An alternative could be a certificate on a smartcard, protected by a PIN.
To use client certificates in ASP.NET applications you need to do the following:
Step 1: In IIS Manager, open your application or web site, choose SSL Settings and choose both Require SSL and Require Client certificate.
Now when the user opens your web site, the browser will prompt him to select a client certificate that will be used in the communication.
Important At this point you have to make sure that the certificate is issued by someone you trust (since anyone can create their own self-signed certificates).
Step 2: Add a configuration item (either web.config, database etc.). In this list you would add the thumbprints of the whole CA (certificate authority) chain for your client certificates.
<add key="ClientCertificateIssuerThumbprints" value="4901f5b87d736cd88792bd5ef7caee91bf7d1a2b,0113e31aa85d7fb02740a1257f8bfa534fb8549e,c9321de6b5a82666cf6971a18a56f2d3a8675602"/>
Step 3: Create a classic username / password login page. Verify the username/password.
Step 4: Add the following code to your login page:
var x509 = new X509Certificate2(this.Request.ClientCertificate.Certificate);
var chain = new X509Chain(true);
chain.ChainPolicy.RevocationMode = X509RevocationMode.Offline;
chain.Build(x509);
var validThumbprints = new HashSet<string>(
System.Configuration.ConfigurationManager.AppSettings["ClientCertificateIssuerThumbprints"]
.Replace(" ", "").Split(',', ';'),
StringComparer.OrdinalIgnoreCase);
// if the certificate is self-signed, verify itself.
for (int i = chain.ChainElements.Count > 1 ? 1 : 0; i < chain.ChainElements.Count; i++)
{
if (!validThumbprints.Contains(chain.ChainElements[i].Certificate.Thumbprint))
throw new UnauthorizedAccessException("The client certificate selected is not authorized for this system. Please restart the browser and pick the certificate issued by XXXXX");
}
// certificate Subject would contain some identifier of the user (an ID number, SIN number or anything else unique). here it is assumed that it contains the login name and nothing else
if (!string.Equals("CN=" + login, x509.Subject, StringComparison.OrdinalIgnoreCase))
throw new UnauthorizedAccessException("The client certificate selected is authorized for another user. Please restart the browser and pick another certificate.");
Only when both the password and the certificate have been checked, the user should be allowed in the system.
Upvotes: 25