You authenticate a user to learn his or her identity. The identity information might be used to make sure a person should have access to the Web Service. You may also use the identity to track the user's activities. When it comes to authenticating your users, you have several options:
Application level authenticationUsers identify themselves via credentials supplied in the SOAP message.
HTTP Basic authenticationThe username and password are sent as clear text. This is not useful for secure applications, but it can be useful in combination with other identification techniques.
HTTP Digest authenticationThis tool sends a hashed version of the basic authentication credentials so that only the server can decode them.
Client certificatesUsing a certificate provided by a certificate authority, the client can prove its identity during SSL authentication.
Windows authenticationThrough HTTP Basic/Digest authentication or client certificates, IIS can map a user identity to a real Windows user.
All of these options have different uses. We will look at each of them in turn. For all except application level authorization, ASP.NET will assist us. Each section will discuss when to set the options, but here is a quick overview. The web.config file has a section describing the authentication mode to use. Listing 6.1 shows an excerpt of the authentication section from that file.
Listing 6.1 Setting the Authentication Options Inside web.config.
<configuration> <system.web> <!-- AUTHENTICATION This section sets the authentication policies of the application. Possible modes are "Windows", "Forms", "Passport" and "None" --> <authentication mode="Windows" /> </system.web> </configuration>
The authentication mode can be any of the following values:
none No authentication is performed.
Windows Use Windows integrated authentication. This can be in the form of HTTP Basic or Digest authentication or NTLM.
Passport Uses Microsoft Passport to authenticate users. This is not a valid method of authentication for XML Web Services. A client with a user interface can use this data to identify the user via some custom methods.
Forms This form of authentication embeds the username and password in a cookie. If the cookie is not present, the user is redirected to a page where he or she can log in. Like Passport, this method will not work with XML Web Services. It can be used to identify the user and then use some other method to authenticate the user with the identity information.
Now, let's see how all these different methods can be used.
Application Level Authorization
At times, it may make sense to provide your own authentication mechanisms. Some of the reasons to do so include the following:
Your Web Service is running on a corporate intranet and you want to grant access to departments, not individuals. You also can assume that all users are legitimate.
Clients already are using credentials you manage for other items (for example, customer ID and password).
Integration with other authentication systems does not make sense for your application.
Whatever the reason, creating your own authentication mechanism is possible. All I present here is a recommended way to accomplish this task.
Credentials are useful for establishing ownership of data, rights to view or manipulate data, and for tracking Web Service usage. Regardless of how they are used, the credentials are out-of-band data. By "out-of-band," I mean that the information on the entity calling a particular Web Method is extra. In a typical programmatic API, you would get user identity by calling some operating system-specific functions and would never make this data part of the function signature. With a Web Service, you cannot always do this. However, you can keep the credentials out of the actual function signature. To do this, require that the client send the credentials in the SOAP Header.
The most common form of application level authentication involves the use of a username and password. Typically, this initial exchange should happen over a secure connection, such as SSL. This operation should return a token of some sort for the caller to use in subsequent calls. This token is the out-of-band data used to identify the caller. You should give the tokens as short a lifespan as what makes sense. For highly secure data, the token may live only a few seconds. For less secure data, the token may live for an hour or more.
As the provider of a Web Service, you should tell clients what to expect for an error when their tokens become invalid. When the token's lifespan comes to an end, the user will have to log in again to get a new, valid token so that he or she can continue to use the Web Service.
Listing 6.2 shows how you might accomplish this task. The sample uses a plain HTTP URL and does not go over SSL. If this were a production system, you would perform the extra steps to turn off anonymous access and enable SSL for IIS. To use SSL, you would need a certificate from a certificate authority that you and your users could trust. For internal applications, this could be a machine running Microsoft Certificate Server. Microsoft Certificate Server is available through the NT 4 Option Pack and ships as part of later versions of Windows Server as an optional Windows component. For external applications, you would have to acquire a certificate from a publicly trusted certificate authority, such as VeriSign.
The usage scenario for the example is simple:
Login and acquire a token.
Using the token, call HelloWorld.
The login method uses a hard-coded username and password.
Listing 6.2 Custom Authentication Login Method
<WebMethod()> Public Function Login(ByVal userName As String, _ ByVal password As String) As String If (userName = "Admin") And _ (password = "simplePW") Then Login = theToken Else Throw New System.Web.Services.Protocols.SoapException( _ "Invalid username/password combination", _ System.Web.Services.Protocols.SoapException. _ ClientFaultCode) End If End Function
The token is a constant string. Using that token, the user would call HelloWorld passing the token in the SOAP Header. Listing 6.3 shows how the service itself could check the token and make sure that the header matched the required credentials.
Listing 6.3 The Token Class and the HelloWorld Declaration Using That Token
Public Class TokenHeader Inherits System.Web.Services.Protocols.SoapHeader Public theToken As String End Class Public m_tokenHeader As TokenHeader Private Const theToken As String = "this_is_the_token" <WebMethod(), _ System.Web.Services.Protocols.SoapHeader("m_tokenHeader", _ Direction:=System.Web.Services.Protocols.SoapHeaderDirection.In, _ Required:=True)> Public Function HelloWorld() As String If (m_tokenHeader.theToken = theToken) Then HelloWorld = "Hello World!" Else Throw New System.Web.Services.Protocols.SoapException( _ "You must login first", _ System.Web.Services.Protocols.SoapException.ClientFaultCode) End If End Function
To see this in action, we can use a simple console application, as shown in Listing 6.4. This application will just log in and, with the returned token, call HelloWorld. It uses the special URI http://schemas.xmlsoap.org/soap/actor/next to indicate that the mustUnderstand refers to the recipient of the message.
Listing 6.4 A Simple Client Using Application Authentication
Sub Main() Dim svc As New localhost.Authenticate() Dim theToken As String Try Dim userName As String Dim password As String ' For this sample, we already know ' the username and password. userName = "Admin" password = "simplePW" theToken = svc.Login(username, password) If (theToken.Length > 0) Then ' We were authenticated. Call HelloWorld ' First, setup the token header Dim theHeader As New localhost.TokenHeader() theHeader.theToken = theToken theHeader.MustUnderstand = True ' Set the actor to say who must understand. ' Yes, it's the one who receives the message. theHeader.Actor = _ "http://schemas.xmlsoap.org/soap/actor/next" svc.TokenHeaderValue = theHeader System.Console.WriteLine(svc.HelloWorld()) End If Catch ex As System.Web.Services.Protocols.SoapException System.Console.WriteLine(ex.Detail) End Try System.Console.WriteLine("Press return to exit") System.Console.ReadLine() End Sub
If everything works correctly, the console should have the text "Hello World!" on it. To write a longer lived version of the client in Listing 6.4, you would store the token and only change it when a Web Service method indicated that the token was no longer valid. The downside to this approach is that someone with a packet sniffer and the ability to watch a piece of the network could capture the token and start using it. If this is a worry, you could run the entire communication over SSL so that the token would be encrypted. This all depends on how secure the data needs to be.
HTTP Basic and Digest Authentication
HTTP Basic and Digest Authentication are used to secure HTTP traffic. They are both defined in RFC 2617 and are variations on the same theme. With both forms of authentication, the user credentials are passed in the HTTP Authorization header field. User credentials always include a username and password and may include information regarding the domain to which the user belongs. When these credentials are passed using Basic authentication, the username and password travel as clear text. Well, not exactly. The text is base64 encoded. For a user named TestUser who has a password of password, the username and password will be represented in the HTTP headers as follows:
Authorization: Basic VGVzdFVzZXI6cGFzc3dvcmQ=
The header states that Basic authentication is being used and that the base64 encoded copy of the username and password is VGVzdFVzZXI6cGFzc3dvcmQ=. Decode this information, and you will discover that this says TestUser:password.
How does one go about decoding base64 encoded data? The XML classes support base64 encoding and decoding natively. One easy way to transform a value between base64 and its regular text representation is to make use of those XML classes. In particular, use the XmlTextReader to do the transformation. To see how to do this, take a look at how I decoded a string that was in the HTTP Authorization header. (You will see this username/password combination in use in the example for this section.)
The first thing I had to do was capture the HTTP conversation. This can be done by using the SOAP Toolkit and capturing an "Unformatted Trace." While a "Formatted Trace" displays only the XML exchanged between client and server, the "Unformatted Trace" shows every byte that goes back and forth. A base64 encoded string ends with a =. With the ability to locate the string easily, the following lines will decode and print the string to the console:
Dim str As String = "<ROOT>VGVzdFVzZXI6cGFzc3dvcmQ=</ROOT>" Dim reader As New _ System.Xml.XmlTextReader(New System.IO.StringReader(str)) reader.Read() Dim buffer(1) As Byte Dim AE As New Text.ASCIIEncoding() ' When done, will write out TestUser:password While reader.ReadBase64(buffer, 0, 1) <> 0 Console.Write(AE.GetChars(buffer)(0)) End While
Because the base64 representation comes back as a byte, you have to convert that byte into the correct representation. Because base64 encoding can be used on many different types of data (images, executables, spreadsheets, and so on), the reader of the data is expected to know what to do with the bytes. In our case, we know that the string contains encoded ASCII characters, so we use the ASCIIEncoding class to transform those bytes into characters.
Basic authentication works well over an SSL channel because the entire HTTP message exchange will be encrypted.
How do you set up the Web Service to use Basic Authentication? To demonstrate how, we will develop a simple Web Service. A variant on Hello World, this one uses Basic (and later, Digest) authentication to say Hello to the user calling the Web Service.
The first thing we need to do is create an XML Web Service. So, fire up Visual Studio and create a new Visual Basic Web Service named Ch6BasicAuth. Open up Service1.asmx.vb and look for the spot where the sample HelloWorld method is commented out. Uncomment that method and change the second line so that the whole method reads as shown in the following code:
<WebMethod()> Public Function HelloWorld() As String HelloWorld = "Hello " & Me.User.Identity.Name End Function
This function uses some of the built-in functionality of ASP.NET to figure out who the caller is. It takes the name tied to the calling identity and says "Hello" to it. The bulk of the work for making this work lies primarily in the land of Web Service deployment. You have to make sure that both the web.config file and the Web Application configuration in IIS are set up correctly. Here, make sure that the authentication type is set to Windows.
<authentication mode="Windows" />
To complete the necessary steps, you will need to go to the Internet Information Management Services console. You can get there by right-clicking My Computer and selecting Manage. After expanding a series of nodes, you should see things looking much like Figure 6.2.
Figure 6.2 Main IIS node.
After you locate this node, perform the following steps to turn on Basic HTTP authentication. While the steps shown are only for the Ch6BasicAuth application, they will work for any Web application.
Within the Internet Information Services node, open up Web Sites, Default Web Site, and locate the Ch6BasicAuth node (shown in Figure 6.3).
Figure 6.3 Locating the Ch6BasicAuth application in IIS.
Right-click the Ch6BasicAuth node and select Properties.
Click the Directory Security tab and click the Edit button.
Uncheck the Anonymous Access checkbox and check the Basic Authentication checkbox. If you want to only allow valid Windows Users, select the Integrated Windows Authentication checkbox too. This is shown in Figure 6.4.
Important: If you don't turn off Anonymous Access, many of the authentication samples in this chapter will not work.
Figure 6.4 Security setup for Ch6BasicAuth virtual directory.
Click OK twice and you should be returned to the management console. The application will now only accept authenticated users who have Windows accounts.
In the web.config file for the Web Service, make sure that the authentication mode is set to Windows. This section of the web.config file was described in this chapter in the "Authenticating Users" section.
Now that the Web Service is locked down and will only access authenticated users, how do you access it? To do this, you need to get on speaking terms with two classesCredentialCache and NetworkCredential. Both of these classes live in the System.Net namespace and they work together. For our purposes, CredentialCache is used to make associations between the servers we want to access and the username/password that we use to authenticate ourselves to the servers. You then tell the proxy to use the CredentialCache when it needs a username/password combination. Listing 6.5 shows a very simple client that uses HTTP Basic authentication to validate itself to use the Web Service.
Listing 6.5 A Simple Basic HTTP Authentication Client
Sub Main() Dim svc As New localhost.Service1() ' Tell the proxy to use the current user's credentials ' whenever it needs to authenticate this client. svc.Credentials = System.Net.CredentialCache.DefaultCredentials svc.AllowAutoRedirect = True svc.PreAuthenticate = True ' Show the results and exit ' The expected output is "Hello [user name]" System.Console.WriteLine(svc.HelloWorld()) System.Console.WriteLine("Press return to exit") System.Console.ReadLine() End Sub
To change this over to HTTP Digest Authentication, the client remains the same. Just go back through the steps outlined in this section and select Digest Authentication for Windows Domain Servers instead of Basic Authentication. The rest will just work. Digest authentication works by encrypting the HTTP Authentication header. The user identification information travels inside a MD5 hash. On a Windows network, this information can only be authenticated by a domain controller. Digest authentication is only available on Windows 2000 and later.
X509 Client Certificates
X509 client certificates present yet another way to authenticate users. Typically, a certificate is issued by an entity called a Certificate Authority (CA). An example of a public CA is Verisign. Many companies use Microsoft Certificate Server (part of Windows Server products) as an internal CA. Certificates make use of public key infrastructure (PKI) to encrypt data. They use private/public key pairs to secure the data. The sender of the data encrypts the data using a private key that only the sender knows. The data can be decrypted using his or her public key. This arrangement allows the receiver of the data to verify that the data was sent by a known party. If the private key is compromised, the owner of the certificate needs to invalidate the certificate and get a new one. How does this work with Web Services?
As you know, ASP.NET transmits SOAP requests using HTTP. SSL, a widely adopted HTTP technology, typically uses server certificates to guarantee that a client is talking to who he or she claims to be. Clients verify this by requesting the server certificate and making sure that data encrypted by the server can be decrypted with the public key. If needed, the server can request a client certificate that verifies the client's identity using the same techniques. A full discussion on SSL and certificates is beyond the scope of this book. For more information, Netscape has an excellent explanation at http://developer.netscape.com/tech/security/basics/index.html.
How do you set this up on your own server? Well, the first thing you will want to do is enable SSL on the server. Without SSL, the certificate exchange will not happen.
Setting up a Certificate Authority
This section contains supplemental information about setting up a certificate authority and has very little to do with Web Services. If you do not need to do this, feel free to skip ahead to the next section.
To run SSL on the server, you will need to have a certificate to prove the identity of the server. If you want to experiment with SSL without paying a certificate authority, such as Verisign for a certificate, you can set up your own certificate authority. Windows 2000 Server and later ships with a component called Certificate Server. You install this component through the Add/Remove Programs dialog by selecting "Add/Remove Windows Components." A certificate authority is an organization that provides public key infrastructure facilities. A certificate identifies the user and issuer of the certificate and provides keys that can be used in encrypting and decrypting data. A full discussion of certificates with respect to PKI, public key infrastructure, is beyond the scope of this book.
The following instructions explain how to set up SSL on a server using a local certificate authority.
Open up IIS administration console (inetmgr) and select the Web site on which you want to use SSL. On most machines, this will be the Web site named Default Web Site.
Right-click the Web site and select Properties.
Select the Directory Security tab and click the Server Certificate... button. This button will be enabled only if a certificate has not been applied to the Web site. Pressing the button brings up the Web Server Certificate wizard.
Select the Create a New Certificate radio button and click Next.
Select the Send the Request Immediately to an Online Certification Authority radio button and click Next again.
On the Name and Security Settings dialog, leave the defaults as is. This is shown in Figure 6.5. Click Next.
Figure 6.5 Name and Security Settings dialog.
On the Organization Information dialog, fill in some values for Organization and Organizational Unit. An example is shown in Figure 6.6.
Figure 6.6 Organization Settings Wizard dialog.
On the Your Site's Common Name dialog, leave the common name alone for testing. When accessing the site over SSL, make sure to use the computer name and not localhost. For a production site, you would want the common name to be the name of the Web site. For example, http://www.scottseely.com would use http://www.scottseely.com as the common name. Click Next.
On the Geographical Information dialog, fill in the information for the country, state/province, and city/locality that applies to your machine. Figure 6.7 shows what I filled in for my development machine. Click Next.
Figure 6.7 Geographical Information dialog.
Click a certification authority on the Choose a Certification Authority dialog. If you set up an in-house certification authority, choose that one. Click Next.
The request is now ready. Click Next to submit the request and install the certificate on the machine. When this is done, click Finish to exit the wizard.
After the certificate is installed, you can view the certificate by pressing the View Certificate button on the Directory Security tab of the Web site property sheet. Figure 6.8 shows the first tab of the certificate installed for my development computer.
Figure 6.8 General information about the installed certificate.
After SSL is set up, you are done with the server side of things. Things become a bit more difficult on the client side. Normally, when you are in a client, such as Internet Explorer, you will install a client certificate so that IE can send it at will. When developing a client application, you do not have access to the certificates IE knows. Instead, the certificate needs to be stored in a file (preferably a file secured to the owner only using the NT File System, NTFS). After this is done, you load the file at runtime and add it to the list of certificates being used by the proxy. The client presented in Listing 6.5 can be altered to use client certificates. The altered client is presented in Listing 6.6.
Listing 6.6 When Configured for SSL, Using the Ch6BasicAuth Service Using Client Certificates for Authentication
Sub Main() Dim svc As New localhost.Service1() ' Load the certificate from a file. Dim x509 As X509Certificate = _ X509Certificate.CreateFromCertFile("c:\example.cer") ' Add the certificate to the service cache svc.ClientCertificates.Add(x509) ' Show the results and exit Try System.Console.WriteLine(svc.HelloWorld()) Catch ex As Exception System.Console.WriteLine(ex.ToString()) End Try System.Console.WriteLine("Press return to exit") System.Console.ReadLine() End Sub
The requirements for this to work are simplea certificate authority that the server trusts must issue the client's certificate. This is the same requirement that the client places on the server when deciding to trust the server. Because the server side of the authentication is all handled by IIS, the Web Service itself does not change. The things that change are the client and the way it is deployed.
Setting up IIS for Client Certificates
Most developers have never set up IIS to accept a client certificate or requested a certificate for client authentication. This section assumes that you have a copy of Windows Server set up somewhere (a 120-day evaluation version will work) and that Certificate Server is installed on that computer. To make X509 certificates work, you have to set up the client and the IIS server correctly. Doing this consists of two larger stepsgetting a client certificate and mapping that certificate to a Windows account.
To get a client certificate, do the following:
Navigate to http://[cert server]/certsrv. Figure 6.9 shows the page on my network at home. Select Request a Certificate.
Figure 6.9 Web interface for requesting a client certificate.
Figure 6.10 shows the next screen. From here, select User Certificate because this is the certificate type we want to use.
Figure 6.10 Select certificate type.
On the next page, click Submit to get the certificate. Figure 6.11 shows the result of this submission. From here, select the Install This Certificate link. This will add the certificate to the client certificates available through Internet Explorer.
Figure 6.11 Result of requesting a client certificate.
With the certificate installed, from within Internet Explorer select the Tools, Internet Options menu option.
Select the Content tab. On that tab, select the Certificates button.
Select the just installed certificate on the Personal tab and click the Export... button. This tab is shown in Figure 6.12.
From within the Certificate Export Wizard, click the Next button. On the Export Private Key page, select No, Do Not Export the Private Key. As a rule you should not give someone your private key because that certificate can be used to impersonate you. The person would thus know the key you use to encrypt your data. Click Next.
On the Export File Format dialog, select DER Encoded Binary X.509 (.CER). The Base64-encoded option would work equally well, although it would result in a larger file to transmit during authentication. Base64 encoding typically introduces a 30 percent increase in size. Click Next.
For the filename, type in c:\example.cer as shown in Figure 6.13. This will allow you to duplicate the results in Listing 6.6. Click Next, and then click Finish. You now have exported the certificate.
Figure 6.13 Exporting the certificate to a file.
Figure 6.12 Personal certificates installed within Internet Explorer.
Now that the client certificate has been exported, you need to map that certificate to an individual or group of individuals in IIS. Let's look at how to map the certificate to an individual from within IIS.
Open up the IIS management consoleinetmgr.
Select the Web site you want to protect. In our case, select Local computer, Web Sites, Default Web Site, Ch6BasicAuth. Right-click the Ch6BasicAuth node and select Properties.
Select the Directory Security tab. In the Secure Communications group box, click the Edit... button.
On the Secure Communications dialog, select the Require Secure Channel (SSL) check box. Select the Require Client License radio button. Finally, select the Enable Client Certificate Mapping check box. The dialog should look as shown in Figure 6.14.
Figure 6.14 Configuring the virtual directory to use SSL and client certificates.
Click the Edit... button.
In the Account Mappings dialog, select the 1-to-1 tab. Click the Add... button to add a new mapping.
The Open common dialog will appear. Select the certificate you just saved from the first set of steps in this sidebar, c:\example.cer. Click Open.
The Map to Account dialog will be open. Make sure that the Enable This Mapping check box is selected. Pick an account from your machine for the Account text box. Then, enter that user's password. Figure 6.15 shows the dialog as filled out on my home machine. After the data is filled in correctly for your environment, click OK and then confirm the password.
Figure 6.15 Configuring the user mapped to a given certificate.
Click OK, until all property pages are closed and you are back at the IIS management console.
If you follow these directions, you should be able to use X509 certificates to authenticate clients.