Web Service Authentication + MD5 [from]

Sample Image - WebServiceAuthentication.gif

Introduction

This is a simple mechanism to authenticate users to a Web Service, using a Time Token and MD5 Hashing to encrypt password.

Background

In CodeProject, you can find at least two others' mechanism to authenticate users to a Web Service. Dan_P wrote Authentication for Web Services as a Simple authentication for web services using SOAP headers. But the username and password are send in clear text and there is no encryption for the data. HENDRIK R. is the author of An introduction to Web Service Security using WSE, that is really a complete solution, but too much complicated for my purposes. The username is send in clear text, but it is possible to use Password Digest to encrypt the password. The data are encrypted using XML Encryption specification to encrypt portions of the SOAP messages.

My solution is something in the middle of the above two. The username is send in clear text, but I use MD5 to encrypt the password. I do not need to send sensitive data, so the data returned by the Web Service is not encrypted.

Using the code

The basic idea is to send UserName and Password from the client to the Web Service using MD5 Hash Code as encryption system. In this way, the password never travels in clear over the network. The Web Service retrieves the user password from a DB or anything else and uses the same MD5 algorithm to test if the password is correct. To be sure that if someone intercepts the Hash, this can be used to authenticate in a later time, I added a timestamp before hashing the Key string. Last, as we are not always on the same server and/or the client clock may be in a different Time Zone or simply not synchronized, I added the possibility to request a Token containing the time mark to the server.

I provided a sample in ASP.NET C# for the client side, but it is possible to use any language: ASP classical JScript or VBScript, PHP, Python, etc. Anyway, on the client side we need to build up the Key using UserName, Password and the hashed timestamp Token previously got from the same Web Service. We can then call the Service and we will get the answer (or an authentication failure warning) that is displayed on the web page.

 
  
None.gif private   void  ButtonUseToken_Click( object  sender, System.EventArgs e)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
string ret;
InBlock.gif    
string UserName, Password, ServiceName, Token;
InBlock.gif    
string Key, ToHash;
InBlock.gif
InBlock.gif    UserName
=this.TextBoxUserName.Text;
InBlock.gif    Password
=this.TextBoxPwd.Text;
InBlock.gif    ServiceName
=this.TextBoxService.Text;
InBlock.gif    Token
=this.TextBoxToken.Text;
InBlock.gif    ToHash
=UserName.ToUpper()+"|"+Password+"|"+Token;
InBlock.gif    Key
=Hash(ToHash)+"|"+UserName;
InBlock.gif
InBlock.gif    ServicePointReference.ServicePoint Authenticate 
= 
InBlock.gif                             
new ServicePointReference.ServicePoint();
InBlock.gif    ret
=Authenticate.UseService(Key, ServiceName);
InBlock.gif    
this.ServResponse.Text=ret;
ExpandedBlockEnd.gif}

The MD5 Hash procedure is very simple in C#; this one was written by Vasudevan Deepak Kumar in Securing Web Accounts.

 
  
None.gif private   string  Hash( string  ToHash)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
// First we need to convert the string into bytes,
InBlock.gif    
// which means using a text encoder.
InBlock.gif
    Encoder enc = System.Text.Encoding.ASCII.GetEncoder();
InBlock.gif
InBlock.gif    
// Create a buffer large enough to hold the string
InBlock.gif
    byte[] data = new byte[ToHash.Length];
InBlock.gif    enc.GetBytes(ToHash.ToCharArray(), 
0, ToHash.Length, data, 0true);
InBlock.gif
InBlock.gif    
// This is one implementation of the abstract class MD5.
InBlock.gif
    MD5 md5 = new MD5CryptoServiceProvider();
InBlock.gif    
byte[] result = md5.ComputeHash(data);
InBlock.gif
InBlock.gif    
return BitConverter.ToString(result).Replace("-""").ToLower();
ExpandedBlockEnd.gif}

On Web Service server side I implemented just three Web Methods:

GetToken is used to get the Time-marked token. The token you get this way, is intended to be used in the basic Authenticate method, or in the UseService that can also verify the access rights for the users authenticated to the requested service. The core of the system is implemented by TestHash. Here the password is hard-coded, but in the sample provided you have also the code to get it from a database:

minus.gif Collapse
 
  
None.gif private   bool  TestHash ( string  HashStr, 
None.gif             
string  UserName,  int  minutes,  string  ServiceName)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
string Pwd, ToHash;
InBlock.gif    
string sResult, sResultT, sResultToken;
InBlock.gif    
try
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
// JUST FOR TEST: the password is hard-coded:
InBlock.gif
        Pwd="SeCrEt";
InBlock.gif
InBlock.gif        DateTime dt 
= DateTime.Now;
InBlock.gif        System.TimeSpan minute 
= new System.TimeSpan(0,0,minutes,0,0);
InBlock.gif        dt 
= dt-minute;
InBlock.gif        
//before hashing we have:
InBlock.gif        
//USERNAME|PassWord|YYYYMMDD|HHMM
InBlock.gif
        ToHash=UserName.ToUpper()+"|"+Pwd+"|"+dt.ToString("yyyyMMdd")+
InBlock.gif                                             
"|"+dt.ToString("HHmm");
InBlock.gif        sResult 
= Hash(ToHash);
InBlock.gif        
//TokenWeGotBefore
InBlock.gif
        ToHash=dt.ToString("yyyyMMdd")+"|"+dt.ToString("HHmm");
InBlock.gif        sResultToken 
= Hash(ToHash);
InBlock.gif        
//USERNAME|PassWord|TokenWeGotBefore
InBlock.gif
        ToHash=UserName.ToUpper()+"|"+Pwd+"|"+sResultToken;
InBlock.gif        sResultT 
= Hash(ToHash);
InBlock.gif    
InBlock.gif        
if ((sResult==HashStr) || (sResultT==HashStr)) 
InBlock.gif            
return true;
InBlock.gif        
else
InBlock.gif            
if (minutes==0// allowed max 2 minutes - 1
InBlock.gif                            
// second to call web service
InBlock.gif
            return TestHash (HashStr, UserName, 1, ServiceName);
InBlock.gif        
else
InBlock.gif            
return false;
ExpandedSubBlockEnd.gif    }

InBlock.gif    
catch
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
return false;
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

To request a hashed time-stamped Token to the Web Service the method is:

 
  
None.gif [WebMethod]
None.gif
public   string  GetToken ()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
string ToHash, sResult;
InBlock.gif    DateTime dt 
= DateTime.Now;
InBlock.gif    ToHash
=dt.ToString("yyyyMMdd")+"|"+dt.ToString("HHmm");
InBlock.gif    sResult 
= Hash(ToHash);
InBlock.gif    
return sResult;
ExpandedBlockEnd.gif}

The method that checks the user authentication is also kept very simple; in a real application you normally need to access a database to check the authentication level and may need to return some data to the caller:

minus.gif Collapse
 
  
None.gif [WebMethod]
None.gif
public   string  UseService ( string  Key,  string  ServiceName)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
string [] HashArray;
InBlock.gif    
string UserName, level;
InBlock.gif
InBlock.gif    
// Key string: HASH|User|OptionalData
InBlock.gif
    HashArray=Key.Split('|');
InBlock.gif    level 
= "-1";    //defaul level
InBlock.gif

InBlock.gif    
if (TestHash(HashArray[0], HashArray[1], 0, ServiceName))
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
try
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            UserName
=HashArray[1];
InBlock.gif            
// JUST FOR TEST: the User authentication level is hard-coded
InBlock.gif            
// but may/shuold be retrieved from a DataBase
InBlock.gif
            switch (UserName)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
case "MyUserName":
InBlock.gif                    level
="1";
InBlock.gif                    
break;
InBlock.gif                
case "OtherUser":
InBlock.gif                    level
="2";
InBlock.gif                    
break;
InBlock.gif                
default:
InBlock.gif                    level
="-1";
InBlock.gif                    
break;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
if (level=="1"return "YOU ARE AUTHORIZED";
ExpandedSubBlockEnd.gif        }

InBlock.gif        
catch (Exception exc)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
return "Authentication failure: " + exc.ToString();
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif    
return "Authentication failure";
ExpandedBlockEnd.gif}

Points of Interest

TestHash checks to see if the Hash contains a timestamp or an already-hashed token, and calls itself once again in case of failure: if someone is calling the service, let's say, at 11:34:58 the Key is valid from 11:34:00 until 11:35:00, that is during two minutes.

The client side may be implemented in any language: ASP classical, JScript or VBScript, PHP, Python, etc. I have intention to post this code too next time...

转载于:https://www.cnblogs.com/BoKeRen/archive/2007/05/24/758426.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值