Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / VB

WSSE Authentication for WebRequest/Response

4.60/5 (4 votes)
25 Jun 20074 min read 1   2.8K  
An implementation of WSSE authentication method for AtomAPI using IAuthenticationModule interface
Screenshot - WSSEClient.png

Introduction

WebRequest/Response class supports almost all of authentication methods used in the internet, such as Negotiate (SPNEGO), Kerberos, NTLM, Digest, and Basic, and there is no need to code authentication logics by ourselves, in the usual case. .NET also allows us to add custom authentication modules to the WebRequest/Response authentication layer.

This article describes the implementation of a custom authorization module of WSSE, which most of AtomAPI services use for authorization method. The module implements IAuthenticationModule interfaces and therefore it is compatible with WebRequest/Response authentication layer. You can support WSSE authentication without changing your HTTP client code.

WSSE Authentication

Originally, the WSSE authentication was made for SOAP web services. However, the Username Token algorithm of WSSE can be easily adapted to the HTTP authentication and now it is widely used in AtomAPI web services. The detail of WSSE authentication for AtomAPI is described in here.

Note: The AtomAPI has been updated to AtomPP and the draft mentions only Basic authentication. The WSSE authentication may be out of style in the near future.

Implementation

The below logics are necessary for WSSE authentication.

  • Base64 encoding
  • sha1 hash algorithm
  • ISO8601 format
  • Secure Nonce creation

Since .NET Framework has all these codes in the class library, the authentication can be written in a very simple way, as follows:

C#
//get credential
if (credentials == null) return null;
NetworkCredential credential = credentials.GetCredential(request.RequestUri, "WSSE");
if (credential == null) return null;
//check credential policy
//request.RequestURI is not actually challenged URI. but I don't know how I can get it...
ICredentialPolicy credentialpolicy = AuthenticationManager.CredentialPolicy;
if ((credentialpolicy != null) && (!credentialpolicy.ShouldSendCredential
	(request.RequestUri, request, credential, this))) return null;
//make secure nonce
byte[] nonce = CreateNonce();
//make iso8601 formatted UTC time
string createtime = DateTime.Now.ToUniversalTime().ToString
	(DateTimeFormatInfo.InvariantInfo.SortableDateTimePattern) + "Z";
//make buffer. createtime and password are encoded in UTF8
byte[] digestbuf = new byte[nonce.Length + Encoding.UTF8.GetByteCount
	(createtime + credential.Password)];
nonce.CopyTo(digestbuf, 0);
Encoding.UTF8.GetBytes(createtime + credential.Password).CopyTo(digestbuf, nonce.Length);
//create default SHA1
SHA1 sha1 = SHA1.Create();
//make digest string
string digest = Convert.ToBase64String(sha1.ComputeHash(digestbuf));
//add X-WSSE header to HTTPWebRequest
request.Headers.Add("X-WSSE", string.Join("", new string[] 
	{ "UsernameToken ", "Username=\"", credential.UserName, "\", ", 
	"PasswordDigest=\"", digest, "\", ", "Nonce=\"", 
	Convert.ToBase64String(nonce), "\", ", "Created=\"", createtime, "\"" }));

It should be noted that you should make the Nonce with great care. System.Random class is not secure enough for the Nonce. Use RNGCryptoServiceProvider class for making a random number.

Custom Authentication Module

In the .NET Framework, authentication processes of WebRequest/Response are managed by AuthenticationManager class and IAuthenticationModule interface. Each of the authentication logics are described in each authentication classes which implement IAuthenticationModule. For example, System.Net.NegotiateClient class corresponds to SPNEGO authentication and System.Net.BasicClient class corresponds to Basic authentication. The AuthenticationManager class holds IAuthenticationModule list, in which the default authentication modules are registered during the application startup.

When the server responses web request and requires the authentication, AuthenticationManager calls IAuthenticationModule.Authenticate function of the inner IAuthenticationModule list with the corresponding WebRequest, challenge string, and credentials. The function is called in the order in which they registered. If called IAuthenticateModule cannot perform authentication, it returns nothing and AuthenticationManager calls next module. If module can perform authentication, it returns an instance of Authorization class which contains the information of authentication. In the case of HTTP, the challenge string comes from the HTTP "WWW-Authenticate" header and the HTTP "Authorization" header is made by the returned Authorization object.

AuthenticationManager also supports Preauthentication. If the request URI has the same authority (schema, host, and port) as the URI requested before, AuthenticationManager calls IAuthenticationModule.Preauthenticate functions before the request is sent and determines the authentication string. The preauthenticate process prevents unnecessary round trip of the request and response.

The implementation of IAuthenticationModule is simple. You should implement only two properties and two functions.

Although the IAuthenticationModule members are called from not only one thread, you don't need to implements these members in the thread safe manner. The calls are already synchronized.

AuthenticationType Property

Name of authenticationtype. This time, it is "WSSE".

C#
public string AuthenticationType {
    get {
        return "WSSE";
    }
}

CanPreAuthenticate Property

True means that the module supports pre-authentication. WSSE authentication can support pre-authentication.

C#
public bool CanPreAuthenticate {
    get {
        return true;
    }
}

Authenticate / PreAuthenticate Function

These functions are called by AuthenticationManager when the WebRequest needs authentication information. The implementations of these functions are almost the same. The only difference is that PreAuthenticate function doesn't have challenge argument because it calls before sending a request.

You should implement these methods as below:

  • Confirm "challenge" argument contains corresponding signature ("WSSE"). It's not so simple because there may be quoted messages in the challenge string and it should be ignored.
    C#
    if (!ContainsSignatureInChallenge(challenge, Signature)) return null;
  • Get NetworkCredential corresponds to the request URI from "credentials" arguments.
    C#
    if (credentials == null) return null;
    NetworkCredential credential = 
    	credentials.GetCredential(request.RequestUri, AuthenticationType );
    if (credential == null) return null;
  • Call ShouldSendCredential.CredentialPolicy to check whether credential should be sent or not.
    C#
    ICredentialPolicy credentialpolicy = AuthenticationManager.CredentialPolicy;
    if ((credentialpolicy != null) && (!credentialpolicy.ShouldSendCredential
    	(request.RequestUri, request, credential, this))) return null;
  • Change WebRequest object if necessary. WSSE authentication needs "X-WSSE" header that contains Username, Nonce, Create Time, and Password Digest.
    C#
    request.Headers.Add("X-WSSE", ..... );
  • Return new Authorization object which contains authorization information for "Authorization" header. In the case of WSSE, "Authorization" header doesn't contain any secure information.
    C#
    return new Authorization("WSSE profile=\"UsernameToken\"", true);

How to Enable Custom Authentication Module

To use Custom Authentication Module, you should register it to AuthenticationManager. Usually, call AuthenticationManager.Register in the initialization code of application.

C#
System.Net.AuthenticationManager.Register(New WSSEClient)

You can use application configuration file also.

XML
<configuration>
  <system.net>
    <authenticationModules>
      <add type="Rei.Net.WSSEClient, WSSEClient" />
    </authenticationModules>
  </system.net>
</configuration>

History

  • 25th June, 2007: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here