The .NET Framework HttpWebRequest permits the developer to access resources on a server using the HTTP or HTTPS protocols. Some very secure systems, however, require a client X509 certificate as evidence to access resources. Setting this up in an ASP.NET application is not straightforward because the default ASP.NET service account has limited permissions and does not have the required access to the local certificate store.
The basic idea is to add the desired X509Certificate to the ClientCertificates collection on the HttpWebRequest object before calling the GetResponse method. Creating the X509Certificate object requires the private key of the certificate first be exported to a file in the file system which acts as the input to the X509Certificate::CreateFromCertFile static method. However, in order to successfully access the certificate in the certificate store, it is necessary to execute the code under the account used to install the certificate. We accomplish this by running the code as a COM+ serviced component, configuring the component to run using the credentials of the account under which the certificate was installed.
The example I give here is doing an HTTP POST to the server, attaching the X509 certificate before open the request stream, writing the POST data, and requesting the response.
First, here is the method used to attach the certificate to the HttpWebRequest:
/// <summary>
/// Attach an X509 client certificate to an existing Http request.
/// </summary>
/// <param name="request">The Http request to which to attach the client certificate.</param>
/// <param name="userName">The user account name (machinenameusername) under which the client certificate was installed.</param>
/// <param name="certPath">The path to the exported client certificate file.</param>
protected void AttachClientCertificate( HttpWebRequest request, string userName, string certPath )
{
TraceWriteLine( "Attaching X509 certificate to HttpWebRequest" );
X509Certificate certificate = ( X509Certificate) _certificates[ certPath ];
if ( certificate == null )
{
TraceWriteLine( "X509 certificate not in cache: Creating from file and caching" );
certificate = X509Certificate.CreateFromCertFile( certPath );
_certificates.Add( certPath, certificate );
}
request.ClientCertificates.Add( certificate );
TraceWriteLine( "X509 certificate successfully attached to HttpWebRequest" );
}
And here is the method used to do the HTTP POST, which makes use of the AttachClientCertificate method:
/// <summary>
/// Posts data to specified Uri using HTTP POST, optionally attaching an X509 client certificate in the process.
/// </summary>
/// <param name="requestUri">The Uri to post data.</param>
/// <param name="postData">The data to be posted.</param>
/// <param name="userName">The user account name (machinenameusername) under which the client certificate was installed.</param>
/// <param name="certPath">The path to the exported client certificate file.</param>
/// <returns>The response from the remote host.</returns>
/// <remarks>
/// A client certificate is attached only if the Uri scheme is https and the client certificate path is supplied.
/// It is assumed that if the certificate path is supplied, the certificate user account name is also supplied.
/// </remarks>
public string GetResponse( string requestUri, string postData, string userName, string certPath )
{
TraceWriteLine( "HttpWebRequestor.GetResponse(string,string,string,string): Entered" );
try
{
ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy( );
// Create the request
HttpWebRequest request = ( HttpWebRequest ) WebRequest.Create( requestUri );
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postData.Length;
// Attach the client certificate if https and certPath specified.
if ( new Uri( requestUri ).Scheme == Uri.UriSchemeHttps && certPath != "" )
{
AttachClientCertificate( request, userName, certPath );
}
// Write data to request
StreamWriter requestWriter = new StreamWriter( request.GetRequestStream( ) );
try
{
requestWriter.Write( postData );
}
finally
{
requestWriter.Close( );
}
// Send to the remote server and wait for the response
HttpWebResponse response = ( HttpWebResponse ) request.GetResponse( );
// Read the response
string responseString;
StreamReader responseReader = new StreamReader( response.GetResponseStream( ) );
try
{
responseString = responseReader.ReadToEnd( );
}
finally
{
responseReader.Close( );
}
// Return response
return responseString;
}
catch ( Exception e )
{
throw;
}
finally
{
TraceWriteLine( "HttpWebRequestor.GetResponse(string,string,string,string): Exiting" );
LogMessage( _trace.ToString( ) );
}
}