Exchange UserCredential between client and webservice in.NET

The purpose of this article is to exchange the user credentials between client and web service using soap header. Nowadays security is of the most concern in any kind of system.

Content

1.Introduction
2.Web service
3.Soap Header
4.Web service Implementation
5.SoapHeader Implementation
6.Build token using Cryptography
7.Invoke Web service in client and pass the user credential
8.Conclusion

Introduction

   The purpose this article is to explore  the  exchange of  user credentials using the soap header between client and webs service with  Cryptography.Nowadays web service is most popular platform independent services.so most of the organization they would have to all data integration using web services. for example CRM.So this point most of developer they ask question about how about security strength.SOAP has solution for that it. When we implement web service you can use the SOAP Header and explicit you override to exchange  the user security token between client and server.

If you are a beginner for web service , To get more information about the web service just visit here
1.http://www.w3schools.com/webservices/default.asp
2.http://www.west-wind.com/presentations/dotnetwebservices/DotNetWebServices.asp


Web Services

   Web services are application components

  • Web services communicate using open protocols
  • Web services are self-contained and self-describing
  • Web services can be discovered using UDDI
  • Web services can be used by other applications
  • XML is the basis for Web services


Soap Header

  The optional SOAP Header element contains application-specific information (like authentication, payment, etc) about the SOAP message.If the Header element is present, it must be the first child element of the Envelope element.


Implementation

    Lets start web service implementation to this you need open visual studio , click File menu  then click new website you will get collection project temple window from that you need select ASP.NET web service (if you are using Visual Studio 2008) and supply website name as "http://localhost/UserCredentialWithSHInWebService" and click ok button. take a look following figure to clear understanding.



Now we created project next steps we need do two important work first you we need create SOAP Header class.

Soap header Implementation

   Right on the web project then add new class call as "SoapUserAuthenticationHeader" and click OK button.here you need inherit your SoapUserAuthenticationHeader class from the SoapHeader, then only you could add attribute to web service method.The SoapUserAuthenticationHeader class file like followings.


SoapUserAuthenticationHeader.cs

using System;
using System.Xml;
using System.Xml.Serialization;
using System.Web.Services.Protocols;


public class SoapUserAuthenticationHeader : SoapHeader
{
public SoapUserAuthenticationHeader()
{

}
public SoapUserAuthenticationHeader(string username, string token)
{
this.username = username;
this.token = token;
}
string username;

public string Username
{
get { return username; }
set { username = value; }
}
string token;

public string Token
{
get { return token; }
set { token = value; }
}

}

SoapUserAuthenticationHeader class is have two properties first for user name and second one for user taken(encrypted password).And important note here you should inherit from SoapHeader class.

Web service Implementation

To this I'm going to take a simple example, if you are have sent valid token to server then server return success message or else return failed message.

DataServices.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;

/// <summary>
/// Summary description for DataServices
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class DataServices : System.Web.Services.WebService
{

public SoapUserAuthenticationHeader UserCredential;


[WebMethod]
[SoapHeader("UserCredential", Direction = SoapHeaderDirection.In)]
public string Message(string[] data)
{
if (UserCredential != null)
{
if (SecurityManager.IsValidUser(UserCredential))
{
return "You are welcome";
}
}
return "You are token not valid, please try with new token";
}


}

Let's look [SoapHeader("UserCredential", Direction = SoapHeaderDirection.In)] in the above code.Just explicit tell to web service method you need a soap header and this direction as IN

One more class i were added to web service project to make easy

SecurityManager.cs


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// Summary description for SecurityManager
/// </summary>
public class SecurityManager
{
public static bool IsValidUser(SoapUserAuthenticationHeader usercredential)
{
string encryptedPass = usercredential.Token;
string plianPass = new CustomSecutiry().Decrypt(encryptedPass);
if (usercredential.Username.Equals("test"))
{
if (plianPass.Equals("pass"))
{
return true;
}
}
return false;
}
}

In this class i have a method to valid user name ans token .and look at this line string plianPass = new CustomSecutiry().Decrypt(encryptedPass);

When user pass password from the client just sent as encrypted token to server.server need decrypt that token and then check with password value.To this i have a common class call as "CutomSecutiy" this class have implemented methods to encrypt and decrypt using Cryptography.

Build token using Cryptography

CutomSecutiy.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;
using System.IO;

public class CustomSecutiry
{
public CustomSecutiry()
{
// Default symmetric algorithm
mCryptoService = new RijndaelManaged();
mCryptoService.Mode = CipherMode.CBC;
mAlgorithm = ServiceProviderEnum.Rijndael;
}

public CustomSecutiry(ServiceProviderEnum serviceProvider)
{
// Select symmetric algorithm
switch (serviceProvider)
{
case ServiceProviderEnum.Rijndael:
mCryptoService = new RijndaelManaged();
mAlgorithm = ServiceProviderEnum.Rijndael;
break;
case ServiceProviderEnum.RC2:
mCryptoService = new RC2CryptoServiceProvider();
mAlgorithm = ServiceProviderEnum.RC2;
break;
case ServiceProviderEnum.DES:
mCryptoService = new DESCryptoServiceProvider();
mAlgorithm = ServiceProviderEnum.DES;
break;
case ServiceProviderEnum.TripleDES:
mCryptoService = new TripleDESCryptoServiceProvider();
mAlgorithm = ServiceProviderEnum.TripleDES;
break;
}
mCryptoService.Mode = CipherMode.CBC;
}

public CustomSecutiry(string serviceProviderName)
{
try
{
// Select symmetric algorithm
switch (serviceProviderName.ToLower())
{
case "rijndael":
serviceProviderName = "Rijndael";
mAlgorithm = ServiceProviderEnum.Rijndael;
break;
case "rc2":
serviceProviderName = "RC2";
mAlgorithm = ServiceProviderEnum.RC2;
break;
case "des":
serviceProviderName = "DES";
mAlgorithm = ServiceProviderEnum.DES;
break;
case "tripledes":
serviceProviderName = "TripleDES";
mAlgorithm = ServiceProviderEnum.TripleDES;
break;
}

// Set symmetric algorithm
mCryptoService = (SymmetricAlgorithm)CryptoConfig.CreateFromName(serviceProviderName);
mCryptoService.Mode = CipherMode.CBC;
}
catch
{
throw;
}
}
#region Symmetric cryptography class...

#region Private members...
//private string mKey = "wqdj~yriu!@*k0_^fa7431%p$#=@hd+&";
private string mKey = "wqdj~yriu!@*k0_^fa7431%p$#=@hd+&*&NSGTHYT%$D&*JK(^%GFDE$%TYJ&^%R";
private string mSalt = "*&%^hr^";
private ServiceProviderEnum mAlgorithm;
private SymmetricAlgorithm mCryptoService;

private void SetLegalIV()
{
// Set symmetric algorithm
switch (mAlgorithm)
{
case ServiceProviderEnum.Rijndael:
mCryptoService.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9, 0x5, 0x46, 0x9c, 0xea, 0xa8, 0x4b, 0x73, 0xcc };
break;
default:
mCryptoService.IV = new byte[] { 0xf, 0x6f, 0x13, 0x2e, 0x35, 0xc2, 0xcd, 0xf9 };
break;
}
}

#endregion

#region Public interfaces...

public enum ServiceProviderEnum : int
{
// Supported service providers
Rijndael,
RC2,
DES,
TripleDES
}
public virtual byte[] GetLegalKey()
{
// Adjust key if necessary, and return a valid key
if (mCryptoService.LegalKeySizes.Length > 0)
{
// Key sizes in bits
int keySize = mKey.Length * 8;
int minSize = mCryptoService.LegalKeySizes[0].MinSize;
int maxSize = mCryptoService.LegalKeySizes[0].MaxSize;
int skipSize = mCryptoService.LegalKeySizes[0].SkipSize;

if (keySize > maxSize)
{
// Extract maximum size allowed
mKey = mKey.Substring(0, maxSize / 8);
}
else if (keySize < maxSize)
{
// Set valid size
int validSize = (keySize <= minSize) ? minSize : (keySize - keySize % skipSize) + skipSize;
if (keySize < validSize)
{
// Pad the key with asterisk to make up the size
mKey = mKey.PadRight(validSize / 8, '*');
}
}
}
PasswordDeriveBytes key = new PasswordDeriveBytes(mKey, ASCIIEncoding.ASCII.GetBytes(mSalt));
return key.GetBytes(mKey.Length);
}

public virtual string Encrypt(string plainText)
{
byte[] plainByte = ASCIIEncoding.ASCII.GetBytes(plainText);
byte[] keyByte = GetLegalKey();

// Set private key
mCryptoService.Key = keyByte;
SetLegalIV();

// Encryptor object
ICryptoTransform cryptoTransform = mCryptoService.CreateEncryptor();

// Memory stream object
MemoryStream ms = new MemoryStream();

// Crpto stream object
CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Write);

// Write encrypted byte to memory stream
cs.Write(plainByte, 0, plainByte.Length);
cs.FlushFinalBlock();

// Get the encrypted byte length
byte[] cryptoByte = ms.ToArray();

// Convert into base 64 to enable result to be used in Xml
return Convert.ToBase64String(cryptoByte, 0, cryptoByte.GetLength(0));
}

public virtual string Decrypt(string cryptoText)
{
// Convert from base 64 string to bytes
byte[] cryptoByte = Convert.FromBase64String(cryptoText);
byte[] keyByte = GetLegalKey();

// Set private key
mCryptoService.Key = keyByte;
SetLegalIV();

// Decryptor object
ICryptoTransform cryptoTransform = mCryptoService.CreateDecryptor();
try
{
// Memory stream object
MemoryStream ms = new MemoryStream(cryptoByte, 0, cryptoByte.Length);

// Crpto stream object
CryptoStream cs = new CryptoStream(ms, cryptoTransform, CryptoStreamMode.Read);

// Get the result from the Crypto stream
StreamReader sr = new StreamReader(cs);
return sr.ReadToEnd();
}
catch
{
return null;
}
}
#endregion

#endregion


}

Look like everything fine in server side , lets create client application to invoke web service do the testing.To this right click on the project solution and add a windows application to the solutions give project name as "UserCredentialDemo"

On this project first t you need a button to invoke web service and rename caption of the button as "Check Me". and then add Custom security class on this project also. Because of when we send password to server we need encrypt in the client side.

Then we need add web reference to client application, to this you need right on click the client project then select Add Web Reference  menu.after add web reference it like look like this.




Let's write few line of code to access web service method.

Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using UserCredentialWithServerDemo.DataServices;

namespace UserCredentialWithServerDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnCheckMe_Click(object sender, EventArgs e)
{
DataServices.DataServices service = new UserCredentialWithServerDemo.DataServices.DataServices();
service.Url = "http://localhost/UserCredentialWithSHInWebService/DataServices.asmx";
SoapUserAuthenticationHeader soapUserAuthenticationHeader=new UserCredentialWithServerDemo.DataServices.SoapUserAuthenticationHeader();
            soapUserAuthenticationHeader.Username="test";
            soapUserAuthenticationHeader.Token=new CustomSecutiry().Encrypt("pass");
service.SoapUserAuthenticationHeaderValue = soapUserAuthenticationHeader;
string [] data =new string []{"test1","test2"};
string message = service.Message(data);
MessageBox.Show(message, "Server message");

}
}
}

Just take yellow background code line before access web service method , we are setting user name and token value by SoapUserAuthenticationHeader .

Say if you are not pass user credential through the web service, you will get message (even you can throw like SoapHeadExeception As well) like you are not authorized person to access this web service.to test this just remove these lines in client code and build application and run.

when you click check me button you will get message like followings.


Conclusion

That's all , now we have secured web service server and windows client.I hope  this article will helpful and save time on your development progress.

Download client source code

Download Server source code

Please add comment about this article contents and mistake.

History

2009.05.12:Client source code link updated.

Thank you

RRaveen



By Ravenet Rasaiyah   Popularity  (3279 Views)
Biography - Ravenet Rasaiyah
My name is RRaveen, and my profession is software developing since 2006 (having more than 4 years experience) and holding MCTS,MCPD certifications and most of developments are based on MS technologies .Specifically ASP.NET ,Sharepoint and Widows Mobiles. I have worked on ERP systems also.