Click here to Skip to main content
Click here to Skip to main content

WS-Security: Web services the secure way

By , 28 Sep 2003
 

Web services security

What is WS-Security

Although web services are generally a good idea, they lack some practical features. For example, systemized security and authentication support.

If developers would like to have their web services authenticated, they must write and implement it themselves. Also confidentiality of communication is not guaranteed, if SSL is not in use.

Because none of them is too enjoyable, good folks from Microsoft, IBM and VeriSign created the WS-Security standard, which solves three basic problems:

  • Authentication and authorization of users – along with web service call, you can now pass authentication information, login and password. These values are automatically processed at server side.
  • Message integrity – messages can be digitally signed, which eliminates the possibility to modify them on the way.
  • Message encryption – SOAP messages can be encrypted, even when sent via non-encrypted channel such as HTTP, to ensure privacy.

How WS-Security works

WS communication between client and server is made by exchange of SOAP messages, which are in fact normal XML documents. These are transported via HTTP or HTTPS in most cases.

Use of XML allows almost unlimited extension of these messages – and that’s exactly what WS-Security does. It adds further elements, which can contain various security related information.

Web Service enhancements

The WS-Security protocol is not included in current version of the .NET framework. But there is a package named “Web Service Enhancements” (WSE), which can be downloaded for free from the Microsoft’s web site. This extension implements some advanced standards, besides the WS-Security mentioned here, also WS-Routing, WS-Attachments and DIME.

The current stable version is 1.0 SP1 for .NET framework 1.0 or 1.1 (version without SP1 is intended for .NET framework 1.0 only). Newly available is also “technology preview” of version 2.0, which offers even more features, like role-based authorization and protocols WS-Policy, WS-SecurityPolicy, WS-Trust, WS-SecureConversation and WS-Addressing.

After installing WSE, you would be ale to use classes contained in namespace Microsoft.Web.Services.

All following code has been developed in VS.NET 2003 using WSE 1.0 SP1 and tested on IIS 6.0. Custom classes are intended to be in namespace AltairCommunications.Artex.

Authentication

Server

To use authentication, you must create a class, implementing the Microsoft.Web.Services.Security.IPasswordProvider interface. It must contain method GetPassword, which would return plaintext password for specified user.

The following is a trivial implementation, which returns the string “password”. In real world you would probably search some kind of user database.

Public Class MyPasswordProvider _
 Implements Microsoft.Web.Services.Security.IPasswordProvider

 Public Function GetPassword(ByVal token As _
 Microsoft.Web.Services.Security.UsernameToken) As String _
 Implements _
 Microsoft.Web.Services.Security.IPasswordProvider.GetPassword

     Return "password"
 End Function
End Class

A second thing you need to do is to setup Web.Config. It must tell your application that it should use your class to check passwords. WARNING: The example has added line breaks for readability. Attribute values must be on single line.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <configSections>
     <section name="microsoft.web.services" 
       type="Microsoft.Web.Services.Configuration.WebServicesConfiguration, 
         Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, 
         PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    <system.web>
        <webServices>
            <soapExtensionTypes>
              <add type="Microsoft.Web.Services.WebServicesExtension, 
                  Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, 
                  PublicKeyToken=31bf3856ad364e35"
                priority="1" group="0" />
            </soapExtensionTypes>
        </webServices>
    </system.web>
    <microsoft.web.services>
        <security>
            <passwordProvider 
                type="AltairCommunications.Artex.MyPasswordProvider, 
                      AltairCommunications.Artex" />
        </security>
    </microsoft.web.services>
</configuration>

At this moment, all web service requests containing authentication information would be checked for their validity and in case of error refused automagically.

The web services methods itself can be written the usual way. Only it is recommended to add at beginning a test, a check if user is authenticated (to allow publishing of anonymous methods too, WSE will let go through, request without any authentication information). At this time, you would probably want also to check, if the web service is called via the SOAP protocol, not by simply using HTTP GET or POST, which cannot implement WS-Security.

Secured (and personalized) version of well-known HelloWorld may then look like the following:

<WebMethod()> Public Function SecureHello() As String 
    '-- Check if I am called via SOAP
    Dim RC As Microsoft.Web.Services.SoapContext = _
      Microsoft.Web.Services.HttpSoapContext.RequestContext
    If RC Is Nothing Then Throw New _
        System.Web.Services.Protocols.SoapException_
        ("Only SOAP requests are accepted", _
        New System.Xml.XmlQualifiedName("NotSoapCall"))

    '-- Load user authentication data
    Dim U As Microsoft.Web.Services.Security.UsernameToken = GetUser(RC)
    If U Is Nothing Then Throw New _
      System.Web.Services.Protocols.SoapException_
        ("You are not authenticated", _
        New System.Xml.XmlQualifiedName("NotAuthenticated"))

    '-- Return personalized message
    Return "Hello, " & U.UserName
End Function

Private Function GetUser(ByVal C As _
    Microsoft.Web.Services.SoapContext) _
    As Microsoft.Web.Services.Security.UsernameToken
    
    For Each T As Microsoft.Web.Services.Security.SecurityToken _
                                           In C.Security.Tokens
        If TypeOf T Is Microsoft.Web.Services.Security.UsernameToken _
                           Then Return T
    Next
    Return Nothing
End Function

Client

Calling of secured WS is similar to normal. WSE in VS.NET would generate a web service proxy, which would contain two versions of classes: standard (inherits System.Web.Services.Protocols.SoapHttpClientProtocol) and secure (inherits Microsoft.Web.Services.WebServicesClientProtocol and has added “Wse” to end of name).

Client must generate instance of Microsoft.Web.Services.Security.UsernameToken, which would contain authentication data, and send it along with call:

Const USER_NAME As String = "user"
Const USER_PASS As String = "password"

Dim T As New TestWSE.SecureWSWse

Dim U As New Microsoft.Web.Services.Security.UsernameToken( _
    USER_NAME, _
    USER_PASS, _
    Microsoft.Web.Services.Security.PasswordOption.SendHashed)
T.RequestSoapContext.Security.Tokens.Add(U)

Console.WriteLine T.SecureHello()

Digital signatures

The above solution can pass authentication, but does not ensure integrity of data – message can be changed during transport. To avoid this, you must add digital signature to the message – which is simple, as well:

Server

There is no need to configure anything at the server. Only when you want to check if message is signed or not, you can use the following function:

Private Function IsSigned(ByVal C As Microsoft.Web.Services.SoapContext) _
                                                       As Boolean
    For Each E As _
      Microsoft.Web.Services.Security.ISecurityElement In C.Security.Elements
        If TypeOf E Is _
           Microsoft.Web.Services.Security.Signature Then Return True
    Next
    Return False
End Function

Client

At client side, you must add only one line of code, which would add the signature:

Const USER_NAME As String = "user"
Const USER_PASS As String = "password"

Dim T As New TestWSE.SecureWSWse

Dim U As New Microsoft.Web.Services.Security.UsernameToken( _
    USER_NAME, _
    USER_PASS, _
    Microsoft.Web.Services.Security.PasswordOption.SendHashed)
T.RequestSoapContext.Security.Tokens.Add(U)

T.RequestSoapContext.Security.Elements.Add( _
    New Microsoft.Web.Services.Security.Signature(U))

Console.WriteLine T.SecureHello()

Data encryption

In this phase, all communication is authenticated and digitally signed, which secures it against a counterfeit. But all data are transported in clear form and everyone who understands the SOAP protocol can read them. There are two ways to change this situation.

First is to use secured transport channel for communication. You must setup web server to use HTTPS instead of HTTP. This solution is not always possible and wanted. It’s why you can use encryption of the SOAP messages itself, while sending them through an open communication channel.

Server

We will use the 3DES symmetric algorithm to encrypt message contents. Thus, both parties need to have the same encryption key. In the following example we would use 128 bit key, which means 16 bytes, represented as ASCII string MY_SECRET_KEY_16.

The technique is similar to password provider class above. Now we need to implement interface Microsoft.Web.Services.Security.IDecryptionKeyProvider. Example is here.

Public Class MyDecryptionKeyProvider
Implements Microsoft.Web.Services.Security.IDecryptionKeyProvider

Public Function GetDecryptionKey(ByVal algorithmUri As String, _
     ByVal keyInfo As System.Security.Cryptography.Xml.KeyInfo) _
     As Microsoft.Web.Services.Security.DecryptionKey _
     Implements _
     Microsoft.Web.Services.Security.IDecryptionKeyProvider.GetDecryptionKey
    
    Return New Microsoft.Web.Services.Security.SymmetricDecryptionKey( _
        System.Security.Cryptography.TripleDES.Create(), _
        System.Text.Encoding.ASCII.GetBytes("MY_SECRET_KEY_16"))
    End Function
End Class

Also the key provider must be registered in Web.Config. To section /configuration/microsoft.web.services/security add element passwordProvider:

<passwordProvider 
 type="AltairCommunications.Artex.MyPasswordProvider, 
 AltairCommunications.Artex" />

For comfort of a kind reader, here is function IsEncrypted, which can check if conversation was encrypted or not:

Private Function IsEncrypted(ByVal C As _
          Microsoft.Web.Services.SoapContext) As Boolean
    For Each E As Microsoft.Web.Services.Security.ISecurityElement _
                                             In C.Security.Elements
        If TypeOf E Is _
           Microsoft.Web.Services.Security.EncryptedData Then 
              Return True
    Next
    Return False
End Function

Client

To allow data encryption, also the client part must have proper encryption key. Possible implementation can look like this:

Const USER_NAME As String = "user"
Const USER_PASS As String = "password"

Dim T As New TestWSE.SecureWSWse

Dim U As New Microsoft.Web.Services.Security.UsernameToken( _
    USER_NAME, _
    USER_PASS, _
    Microsoft.Web.Services.Security.PasswordOption.SendHashed)
T.RequestSoapContext.Security.Tokens.Add(U)

T.RequestSoapContext.Security.Elements.Add( _
    New Microsoft.Web.Services.Security.Signature(U))

Dim Key As New Microsoft.Web.Services.Security.SymmetricEncryptionKey( _
    System.Security.Cryptography.TripleDES.Create(), _
    System.Text.Encoding.ASCII.GetBytes(Me.txtKey.Text))
Key.KeyInfo.AddClause(New System.Security.Cryptography.Xml.KeyInfoName)
T.RequestSoapContext.Security.Elements.Add( _
    New Microsoft.Web.Services.Security.EncryptedData(Key))

Console.WriteLine T.SecureHello()

Instead of happy end

Excellent tool to debugging is possibility to save all exchanged messages to files. Simply add the following to the microsoft.web.services section of Web.Config:

<microsoft.web.services>
    <diagnostics>
        <trace enabled="true" input="input.xml" output="output.xml" />
    </diagnostics>
</microsoft.web.services>

But be warned: do not forget to switch the tracing before deploying application to production server. Like all other XML based protocols, the SOAP is pretty gossipy and trace files can grow to incredible sizes.

Links

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

About the Author

Michal Altair Valášek
Software Developer Altairis
Czech Republic Czech Republic
Member

Software architect and developer in Altairis, dev shop in Czech republic. Microsoft Most Valuable Professional (MVP) since 2004.

See my open source project at Codeplex.


Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 1memberMurat DOGANCAY26 Dec '09 - 9:20 
wse is the bad way of the securing web services."with WSE" must be in the article subject!
GeneralWebservice securitymemberlamxung4 Jan '07 - 22:46 
I'm studying about Web service security. What must i learn about and i want to write some small program to test, what program shoud i write( i used C#).Merci alots!!!Smile | :)
Questionhow can i do this ?memberanhvuphamx6 Oct '06 - 18:33 
Calling of secured WS is similar to normal. WSE in VS.NET would generate a web service proxy, which would contain two versions of classes: standard (inherits System.Web.Services.Protocols.SoapHttpClientProtocol) and secure (inherits Microsoft.Web.Services.WebServicesClientProtocol and has added “Wse” to end of name).
 
I was trying to do this but i was not able to to do this.Would anyone tell me how to do this please !Sigh | :sigh:
 
Thank you !
Regard
 

GeneralRequestSoapContext in client application not workingmembersrikanth_raghupathy20 Nov '05 - 20:47 
Hi
The article was really helpfull as an understanding to WSE, but i am facing a few problems when trying to run the sample
I have compiled the server app successfully but when i call an instance of the webservice on the consumer app, I am not able to get an handle to the RequestSoapContext where you are passing the Microsoft.Web.Services.Security.UsernameToken and 2 other places as below which I hasve underlined.
Pls do let me know how do i go about solving this problem.
 
Private Sub cmdInvoke_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdInvoke.Click
            Dim T As New SS.SecureWS
            Dim U As Microsoft.Web.Services.Security.UsernameToken
 
            '-- Authenticate request
            If Me.chkAuthenticate.Checked Then
                  U = New Microsoft.Web.Services.Security.UsernameToken( _
                        Me.txtUserName.Text, _
                        Me.txtPassword.Text, _
                        IIf(Me.chkSendHash.Checked, Microsoft.Web.Services.Security.PasswordOption.SendHashed, Microsoft.Web.Services.Security.PasswordOption.SendPlainText))
                  T.RequestSoapContext.Security.Tokens.Add(U)
                  ---------------------
 

                  '-- Sign request
                  If Me.chkSign.Checked Then _
                        T.RequestSoapContext.Security.Elements.Add( _
                        ---------------------
                        New Microsoft.Web.Services.Security.Signature(U))
            End If
 
            '-- Encrypt request
            If Me.chkEncrypt.Checked Then
                  Dim Key As New Microsoft.Web.Services.Security.SymmetricEncryptionKey( _
                        System.Security.Cryptography.TripleDES.Create(), _
                        System.Text.Encoding.ASCII.GetBytes(Me.txtKey.Text))
                  Key.KeyInfo.AddClause(New System.Security.Cryptography.Xml.KeyInfoName)
                  T.RequestSoapContext.Security.Elements.Add( _
                  ---------------------  
                        New Microsoft.Web.Services.Security.EncryptedData(Key))
            End If
 
            '-- Call WS
            Try
                  Me.lblResult.Text = "<div class=""status-ok"">Success</div><pre>" & Server.HtmlEncode(T.SecureHello()) & "</pre>"
            Catch ex As System.Web.Services.Protocols.SoapException
                  Me.lblResult.Text = "<div class=""status-fail"">SOAP exception '" & ex.Code.ToString() & "' occured</div>"
                  Me.lblResult.Text &= "<pre>" & Server.HtmlEncode(ex.ToString()) & "</pre>"
            Catch ex As Exception
                  Me.lblResult.Text = "<div class=""status-fail"">System exception occured</div>"
                  Me.lblResult.Text &= "<pre>" & Server.HtmlEncode(ex.ToString()) & "</pre>"
            End Try
      End Sub
GeneralAuthentication using more paramssussdengerous17 May '05 - 1:42 
How would I implement this if users have to send me 3 parameters in order to pass the authentication: accountname,username,password
GeneralNot workingmembermattiash8317 Jan '04 - 2:40 
Thank you for a very interesting article!
 
I just cant get it to work... I get this error-message:
"System.MissingMemberException: Public member 'RequestSoapContext' on type 'CZService' not found."
 
The error appears when the client is executing:
T.RequestSoapContext.Security.Tokens.Add(U)
 
I have taken the codes from your examples. I have tried with both WSE 1.1 AND WSE 2.0.
 
Thanks Smile | :)

GeneralRe: Not workingmemberMichal Altair Valasek17 Jan '04 - 8:42 

It seems that you don't have WSE correctly setup on client side, and that VS.NET does not correctly make the appropriate proxy. Please check it out.

It seems that people have not problem understanding the article, but rather setting up WSE. As it's a little tricky, I will try to write some article about that soon. Please, keep in touch and e-mail me if you would be unable to get it work.


Michal Altair Valasek - http://www.rider.cz
Doing my best for keeping freedom safe from democracy.

GeneralRe: Not workingmembermattiash8317 Jan '04 - 14:41 
Thanks for your fast reply Michal!
 
I have absolutely no idea how to fix this problem...
Yesterday I spend 13 hours and today 6 hours Confused | :confused: just trying and reading, and trying and reading to make it work.
I would appreciate your help so much!
 
What did you do with WSE to make it work with VS.NET?
How do I setup WSE on the clientside?
Did it work directly for you or did you have problems?
What code on the webservice is it that makes RequestSoapContext enable from the clientside?
 
Thank you for your time Michal!
 

GeneralRe: Not workingmemberMichal Altair Valasek17 Jan '04 - 15:59 

It's a little tricky, especially when you are running IIS on another computer than VS.NET.

At first, you need to install WSE to server, client and your VS.NET Machine. And the WSE plugin to VS.NET (it's named like "WSE Settings Tool") to your VS.NET machine.

At second, add reference to Microsoft.Web.Services namespace to both of them.

At third, right-click your project and choose Wse Settings and check "Enable this project for WSE". It would say that it cannot update web.config files residing or network drive. You must update it yourself (as written in article).

At server side it's simple -- you just need to add the reference and make changes in web.config.

The trick is that you must force VS.NET to generate not only standard web services proxy (which does not have the appropriate interface), but also WSE-enabled proxy (would be named as usual, but having Wse at end - ie Service1Wse. For that you need WSE plugin to VS.NET.

Hope it helps. I focused to technology and not mentioned development interface problems. I hope that new version of VS.NET would solve that.


 
Michal Altair Valasek - http://www.rider.cz
Doing my best for keeping freedom safe from democracy.

QuestionWhat does (AltairCommunications.Artex) suppose to represent?sussseoff16 Jan '04 - 6:00 
I was trying to setup your example project on my personal pc so I could see in use, and although you describe that you use (AltairCommunications.Artex). I’m confused what this class/namespace does or why I need it to make the WS work. Your article doesn’t explain what I would have to do to make my own similar class. I'm just a little confused, would you please help me?

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 29 Sep 2003
Article Copyright 2003 by Michal Altair Valášek
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid