Web Services Enhancements (WSE) is a webservice standard which provides cross-platform security for webservices. WSE provides various security specifications like WS-Security, WS-Policy, WS-Trust, WS-Privacy, WS-SecureConversation, WS-Federation, WS-Authorization which can be used to provide authentication, authorization and message level security using digital signing and encryption based on particular requirements on hand. More information on WSE and the areas it addresses can be seen here (This document is a old but provides a an overview of WSE security-model). Another good article on MSDN Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication explain different security scenarios and how WSE can be used in each one of them.
In the following I will provide a step-by-step detail on how to use WSE 3.0 for authenticating webservices with your custom user database. The custom database can be an ASP.NET Membership database or other custom user database. (You must have Visual Studio 2005 installed to proceed with the following. Please click on the images to enlarge)
1. Download WSE 3.0 from http://www.microsoft.com/downloads/details.aspx?FamilyID=018A09FD-3A74-43C5-8EC1-8D789091255D&displaylang=en and install it on your machine.
2. Create a new ASP.NET Webservice website project. Right Click the project select the “WSE Settings 3.0” option as shown below.
3. This will open the following WSE settings dialog. Select the “Enable this project for WSE” and “Enable Microsoft WSE Soap Protocol Factory” from the settings dialog.
4. Go to the Policy Tab and select “Enable Policy”. Add a new Policy with the name “CustomServicePolicy” (or any name).
5. After you add a new Policy, the following Policy settings wizard will open and you will be required to define your policy.
6. Select the Authentication method. The Authentication method can be Anonymous, Username, Certificate and Windows. In this sample I am going to select the username as we care going to pass username and password for the authentication. You can use other methods like windows if required. Also select that you are seting this policy for a service. (In case you are creating a policy for a client to access WSE webservice you can select the “Secure Client Application” option). Click next to proceed.
7. In the following screen you can select “Enable WS-Security 1.1 Extensions” and select Protection Order as “None”. You can select other protection methods if you want to sign and encrypt your messages using a X.25 certificate.
8. Click next to proceed, you will be shown the following screen with the informaiton about the Policy that you have defined.
9. After defining the Policy, go to you service class and add this Policy to the class using the Policy Attribute as shown in the following screenshot. You will be required to add Microsoft.Web.Services3.dll into your project to reference Policy attribute and other WSE classes. The WSE documentation says that this reference will be automatically added to your project if you enable WSE for your project as we did but I have observed that you have to manually add this reference and it is not added automcatically. You can get this dll from the following path C:\Program Files\Microsoft WSE\v3.0\ (The path may be different based on your installation location).
Note that after configuring WSE settings, there is an extra configuration file “wse3policyCache.config” which has been added to the project.
10. Now we need to write a Custom Token Manager which will authenticate the username and password sent by the user. Add a new Class “CustomTokenManager” in the project and inherit it from Microsoft.Web.Services3.Security.Tokens.UsernameTokenManager. Override the AuthenticateToken method, WSE soap filter will call this method to obtain the clear text password for the user. You can write your custom code to access your ASP.NET membership database or other user database to obtain the password. You can get the username from the UsernameToken passed as a parameter to the AuthenticateToken method.
11. After you have you CustomTokenManager ready, you need to set this in web.config in the WSE security settings so WSE can use this class when user authentication is required.
Now your service is ready to use and it will authenticate the incoming user with your custom database usign the CustomTokenManager class that you have created.
To write a client for a WSE service, you need to have WSE 3.0 installed on the client machine. Moreover, you will have to enable WSE for your project (step 3) before adding the webreference to the server. If WSE is installed on the client machine and is enabled for your project, the WSDL.exe toll will generate an additional proxy class ending with Wse if the service supports WSE. In our case there will be two proxy classes that will be generated on the client. Service and ServiceWse. Use ServiceWse for accessing the service.
The following code shows how to pass username and password as UserNameToken in WS-Security header.
Dim oService As New WSETestService.ServiceWse
Dim U As New Microsoft.Web.Services3.Security.Tokens.UsernameToken(“<User_Name>”, “<Password>”, Security.Tokens.PasswordOption.SendHashed)
oService.SetClientCredential(U)
Dim oPolicy As New Policy()
oPolicy.Assertions.Add(New UsernameOverTransportAssertion())
oService.SetPolicy(oPolicy)
oService.HelloWorld()
Following is the SOAP request with the UserNameToken in the WSE header with username and password of the user. The password is passed as SHA-1 hash of the actual password. (Note that you selected Security.Tokens.PasswordOption.SendHashed inUserNameToken constructor from the client when creating the token, you can choose to send clear text password as well)
<inputMessage utc=”9/18/2007 1:10:30 PM” messageId=”urn:uuid:8c890d37-28a0-423f-8aaa-1b35e7f6a021″>
<processingStep description=”Unprocessed message”>
<soap:Envelope xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:wsa=”http://schemas.xmlsoap.org/ws/2004/08/addressing” xmlns:wsse=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd” xmlns:wsu=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd”>
<soap:Header>
<wsa:Action>http://tempuri.org/HelloWorld</wsa:Action>
<wsa:MessageID>urn:uuid:8c890d37-28a0-423f-8aaa-1b35e7f6a021</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://localhost:1841/WebServiceAuthentication/Service.asmx</wsa:To>
<wsse:Security soap:mustUnderstand=”1″>
<wsu:Timestamp wsu:Id=”Timestamp-dd031f35-cc14-4426-b34f-bfff66d03991″>
<wsu:Created>2007-09-18T13:10:30Z</wsu:Created>
<wsu:Expires>2007-09-18T13:15:30Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken xmlns:wsu=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd” wsu:Id=”SecurityToken-2ee318dd-56ef-4f26-877d-2a199ff5b4e3″>
<wsse:Username>aleem</wsse:Username>
<wsse:Password Type=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest”>CyI4oSXQgRMdYF416fHcD0IDpIE=</wsse:Password>
<wsse:Nonce>NVU8Adj9tEMZQpBPoPfJMw==</wsse:Nonce>
<wsu:Created>2007-09-18T13:10:30Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<HelloWorld xmlns=”http://tempuri.org/” />
</soap:Body>
</soap:Envelope>
</processingStep>
<processingStep description=”Entering SOAP filter Microsoft.Web.Services3.Design.RequireSoapHeaderAssertion+RequireSoapHeaderFilter” />
<processingStep description=”Exited SOAP filter Microsoft.Web.Services3.Design.RequireSoapHeaderAssertion+RequireSoapHeaderFilter” />
<processingStep description=”Entering SOAP filter Microsoft.Web.Services3.Design.UsernameOverTransportAssertion+ServiceInputFilter” />
<processingStep description=”Exited SOAP filter Microsoft.Web.Services3.Design.UsernameOverTransportAssertion+ServiceInputFilter” />
<processingStep description=”Processed message”>
<soap:Envelope xmlns:soap=”http://schemas.xmlsoap.org/soap/envelope/” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” xmlns:wsa=”http://schemas.xmlsoap.org/ws/2004/08/addressing” xmlns:wsse=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd” xmlns:wsu=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd”>
<soap:Header />
<soap:Body>
<HelloWorld xmlns=”http://tempuri.org/” />
</soap:Body>
</soap:Envelope>
</processingStep>
</inputMessage>