Click here to Skip to main content
Click here to Skip to main content
 
Add your own
alternative version

Secure Web Services via TCP/IP

, 30 May 2003
Describes an approach for delivery of Soap Messages serialised using ASP.NET Web Client Services over TCP/IP
wsaltroute2.zip
ArgosAPIExtensions
Bin
Client
ArgosAPIExtensions.dll
WSAltRouteTest.exe
WSIPTransports.dll
Server
WebServices
WSAltRouteArgos
bin
ArgosAPIExtensions.dll
ArgosAPIExtensions.pdb
WSAltRouteArgos.dll
WSAltRouteArgos.pdb
Global.asax
WSAltRouteArgos.csproj.webinfo
WSAltRouteArgos.vsdisco
WSAltRouteBEFake.exe
WSAltRouteTest
App.ico
Web References
ArgosTrinketService
Reference.map
Service1.disco
Service1.wsdl
WSAltRouteBEFake
App.ico
WSAltTransports2
WSQTransports.dll
AsyncHelper.dll
// --------------------------------------------------------------------------------
// Module:      ProxyCommonWSE.cs
// Author:      Simon G
// Date:        24 February 2003
// Description: Common WSE-oriented functions for use by the Web Service caller
// --------------------------------------------------------------------------------
// $Archive: <VSS Path To File>/ProxyCommonWSE.cs $
// $Revision: 1 $ changed on $Date: 24 February 2003 18:37 $
// Last changed by $Author: Simon G $
// --------------------------------------------------------------------------------
using System;
using System.IO;
using System.Text;
using System.Diagnostics;					// For Event Log entries
using System.Security;
using System.Security.Cryptography;			// Managed Crypto functions
using System.Security.Cryptography.Xml;		// KeyInfo
using Microsoft.Web.Services;
using Microsoft.Web.Services.Dime;			// For WS-Attachment support
using Microsoft.Web.Services.Security;		// For WS-Security support


namespace WSAltRouteProxy
{
	/// <summary>
	/// Summary description for ProxyCommonWSE.
	/// </summary>
	public class ProxyCommonWSE
	{
		// WSE specific stuff


		// Setup methods

		/// <author>SGregory</author>
		/// <date>18 February 2002</date>
		/// <summary>SetupSOAPRequestContext</summary>
		/// <remarks>Sets up the WSDL Context from which the SOAP header is populated before calling the Web Service</remarks>
		/// <param name="vobjSoapContext" type="SoapContext">SOAP Context into which we can feed the user credentials and expiry information</param>
		///	<preconditions>Valid SoapContext</preconditions>
		///	<postconditions>SoapContext augmented with time-to-live, username etc.</postconditions>
		static public void SetupSOAPRequestContext(
			SoapContext vobjSoapContext)
		{
			// Delegate...
			SetupSOAPRequestContext(
				vobjSoapContext,
				null);
		}


		/// <author>SGregory</author>
		/// <date>18 February 2002</date>
		/// <summary>SetupSOAPRequestContext</summary>
		/// <remarks>Sets up the WSDL Context from which the SOAP header is populated before calling the Web Service</remarks>
		/// <param name="vobjSoapContext" type="SoapContext">SOAP Context into which we can feed the user credentials and expiry information</param>
		/// <param name="vstImage" type="Stream">Image as bytes</param>
		///	<preconditions>Valid SoapContext</preconditions>
		///	<postconditions>SoapContext augmented with time-to-live, username etc.</postconditions>
		static public void SetupSOAPRequestContext(
			SoapContext vobjSoapContext,
			Stream vstImage)
		{
			// Need to explicitly set a routing path action
			SetupSOAPRequestContextWithRoutePathAction(vobjSoapContext);

			// Get a symmetric encryption key to encrypt the body with
			EncryptionKey encKey = GetEncryptionKey();

			// Get the ms since machine switch on as string - we will use this as a User Name
			// since it changes every call, and we do not care about a real user of the service
			// per se.
			string strUserName = Environment.TickCount.ToString();

			// Create a User Token from a tick count and static User Name
			UsernameToken userToken = new UsernameToken(strUserName,
				CalculatePasswordForProxyUser(strUserName),
				PasswordOption.SendHashed);

			// Stick it in the SOAP Context
			vobjSoapContext.Security.Tokens.Add(userToken);

			// Request a signature for the message (based on the User Token)
			vobjSoapContext.Security.Elements.Add(new Microsoft.Web.Services.Security.Signature(userToken));

			// Request the SOAP body be encrypted (with a different symmetric key)
			vobjSoapContext.Security.Elements.Add(new Microsoft.Web.Services.Security.EncryptedData(encKey));

			// Set an expiry on this SOAP message too
			vobjSoapContext.Timestamp.Ttl = SOAP_MSG_EXPIRY_TIME;

			// Add any attachments as required
			if (vstImage != null)
			{
				vstImage.Position = 0;	// Assume we can seek

				DimeAttachment objBin = new DimeAttachment(
					"uuid:9F21A65F-E0FA-43c4-828D-1B5D9FEDC30E",
					"application/octet-stream", 
					TypeFormatEnum.MediaType,
					vstImage);
				vobjSoapContext.Attachments.Add(objBin);
			}
		}


		/// <author>SGregory</author>
		/// <date>18 February 2002</date>
		/// <summary>SetupSOAPRequestContextWithRoutePathAction</summary>
		/// <remarks>Sets up the Soap Context with JUST the Route Path before calling the Web Service</remarks>
		/// <param name="vobjSoapContext" type="SoapContext">SOAP Context into which we can feed the user credentials and expiry information</param>
		///	<preconditions>Valid SoapContext</preconditions>
		///	<postconditions>SoapContext augmented with path action only.</postconditions>
		static public void SetupSOAPRequestContextWithRoutePathAction(
			SoapContext vobjSoapContext)
		{
			// Need to explicitely set a routing path action
			vobjSoapContext.Path.Action = string.Empty;
		}


		/// <author>SGregory</author>
		/// <date>18 February 2002</date>
		/// <summary>CalculatePasswordForProxyUser</summary>
		/// <remarks>Takes the User Name, and tick count</remarks>
		/// <param name="vstrUserName" type="string">User Name to pass in SOAP message</param>
		///	<postconditions>
		///	String representing a unique 'password' - it may not look secure but it's about
		///	to be hashed to hell before being put in the SOAP message
		///	</postconditions>
		static private string CalculatePasswordForProxyUser(
			string vstrUserName)
		{
			StringBuilder objSB = new StringBuilder(128);
			objSB.Append(vstrUserName);
			objSB.Append(PROXY_USER_NAME);
			return objSB.ToString();
		}


		/// <summary>RegDotNetAccess.Wrapper.GetEncryptionKey</summary>
		/// <author>Simon G</author>
		/// <date>16 October 2002</date>
		/// <remarks>
		/// Returns a Symmtric encryption key for use when encrypting
		/// the outbound SOAP body.  The key is used both by this Client
		/// Proxy Wrapper (RegDotNetWS) and the target Registration.NET
		/// Web Service.
		/// </remarks>
		/// <returns type="Microsoft.Web.Services.Security.EncryptionKey">Symmetric key for encrypting data</returns>
		/// <postconditions>Symmetric key generated using TripleDESCryptoServiceProvider</postconditions>
		static private EncryptionKey GetEncryptionKey()
		{            
			// Creates an instance of a class deriving from
			// SymmetricAlgorithm. The TripleDESCryptoServiceProvider provides
			// an implenentation of the Triple DES algorithm.
			SymmetricAlgorithm algo = new TripleDESCryptoServiceProvider();

			// Defines the 128-bit secret key shared by the XML Web service
			// and this client.
			byte[] keyBytes = { 30, 8, 67, 16, 12, 68, 26, 12, 
								  98, 27, 5, 20, 01, 15, 2, 69 };
			// Defines the initialization vector for the algorithm.
			byte[] ivBytes =  { 9, 8, 1, 6, 2, 2, 9 };

			// Sets the 128-bit secret key for the Triple DES algorithm.
			algo.Key = keyBytes;

			// Sets the initialization vector for the Triple DES algoritm.
			algo.IV = ivBytes;

			// Creates a key that will be used by the WSE to encrypt the
			// outbound SOAP message.
			EncryptionKey key = new SymmetricEncryptionKey(algo);

			//
			// Finally, we must build the KeyInfo for the key. For this sample, we use
			// a simple name for the key.
			KeyInfoName keyName = new KeyInfoName();
			keyName.Value  = "WSAltRouteWSE KeyXfer";
			key.KeyInfo.AddClause(keyName);

			// Give back to WSE builder
			return key;
		}



		// Verification methods

		/// <summary>Eqos.RegistrationService.Presentation.WebServiceFacade.WSCommon.ValidateCredentials</summary>
		/// <author>Simon G</author>
		/// <date>16 October 2002</date>
		/// <remarks>Checks that there is a valid Security token</remarks>
		/// <param name="vRequestContext" type="Microsoft.WSE.SoapContext">SOAP Request Context</param>
		/// <postconditions>Return OK, or an exception will be thrown to the caller</postconditions>
		static public void ValidateCredentials(
			SoapContext vRequestContext)
		{
			StringBuilder objSBDiagnostics = new StringBuilder(512);
			objSBDiagnostics.Append("SecCtx Info: ");

			try
			{
				// Valid context?
				if (vRequestContext == null)
				{
					objSBDiagnostics.Append("null");
					throw new ApplicationException(
						SEC_INFO_FAIL_MSG + " [Bad.Config].");
				}

				// If so, let's examine the Request Context
				// Build a diagnostic block first...
				objSBDiagnostics.Append("\nSecTokens: ");
				objSBDiagnostics.Append(vRequestContext.Security.Tokens.Count);
				objSBDiagnostics.Append("\n");
				foreach (SecurityToken secT in vRequestContext.Security.Tokens)
				{
					objSBDiagnostics.Append("  Token[");
					objSBDiagnostics.Append(secT.Id);
					objSBDiagnostics.Append("] = ");
					objSBDiagnostics.Append(secT.ToString());
					objSBDiagnostics.Append("\n");
				}

				objSBDiagnostics.Append("Elements: ");
				objSBDiagnostics.Append(vRequestContext.Security.Elements.Count);
				objSBDiagnostics.Append("\n");
				foreach (object elT in vRequestContext.Security.Elements)
				{
					if (elT is Microsoft.Web.Services.Security.Signature)
					{
						objSBDiagnostics.Append("  Token (Signature)[");
						objSBDiagnostics.Append(((Microsoft.Web.Services.Security.Signature)elT).SecurityToken.Id);
						objSBDiagnostics.Append("] = ");
						objSBDiagnostics.Append(((Microsoft.Web.Services.Security.Signature)elT).SecurityToken.ToString());
						objSBDiagnostics.Append("  [");
						objSBDiagnostics.Append(((Microsoft.Web.Services.Security.Signature)elT).SignatureOptions.ToString());
						objSBDiagnostics.Append("]\n");
					}
					else
						if (elT is EncryptedData)
					{
						objSBDiagnostics.Append("  Token (EncryptedData)[");
						objSBDiagnostics.Append(((EncryptedData)elT).ToString());
						objSBDiagnostics.Append("]\n");
					}
				}

				objSBDiagnostics.Append("Attachments: ");
				objSBDiagnostics.Append(vRequestContext.Attachments.Count);
				objSBDiagnostics.Append("\nExtendedSec: ");
				objSBDiagnostics.Append(vRequestContext.ExtendedSecurity.Count);

				// Log information about SoapContext
				using (EventLog objEvLog = new EventLog())
				{
					objEvLog.Source = "WSAltRouteTest";
					objEvLog.WriteEntry(
						objSBDiagnostics.ToString(),
						EventLogEntryType.Information);
				}	// objEvLog.Dispose() is called automatically when using scope exited.

				// No tokens means that the message can be rejected quickly
				if (vRequestContext.Security.Tokens.Count == 0)
					throw new ApplicationException(
						SEC_INFO_FAIL_MSG + " [Bad.Tokens].");

				// Check for a Signature that signed the soap Body and uses a token that we accept.
				bool blnValid = false;
				for (int i = 0; blnValid == false && i < vRequestContext.Security.Elements.Count; i++)
				{
					Microsoft.Web.Services.Security.Signature signature = vRequestContext.Security.Elements[i] as Microsoft.Web.Services.Security.Signature;

					// We only care about signatures that signed the soap Body
					if (signature != null && 
						(signature.SignatureOptions & SignatureOptions.IncludeSoapBody) != 0 )
					{
						// Found it - fish it out
						UsernameToken usernameToken = signature.SecurityToken as UsernameToken;

						// Check we have one
						if (usernameToken != null)
						{
							// Let's check it's numeric - as we know our User Name should be
							string strUserName = usernameToken.Username;
							try
							{
								int intDummy = Convert.ToInt32(strUserName);
							}
							catch
							{
								throw new ApplicationException(
									SEC_INFO_FAIL_MSG + " [Bad.UserCreds].");
							}

							// Add more checks as we require them
							// .....
						}
					}
				}
			}
			catch(Exception e)
			{
				// All WSE-based validation issues get logged here
				using (EventLog objEvLog = new EventLog())
				{
					objEvLog.Source = "WSAltRouteTest";
					objEvLog.WriteEntry(
						"An exception was caught while processing Security Context information on return from a WS call : " + objSBDiagnostics.ToString(),
						EventLogEntryType.Error);
				}	// objEvLog.Dispose() is called automatically when using scope exited.

				// Throw up to our caller
				throw(e);
			}
		}


		// Constants
		private const string SEC_INFO_FAIL_MSG = "The security information supplied on the return from call was not valid";

		// WSE specific support
		public const string PROXY_USER_NAME = "SecureWSAltRoute";	// User Credentials
		private const int SOAP_MSG_EXPIRY_TIME = 60000;				// 60s
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

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

Share

About the Author

Simon Gregory
Web Developer
United Kingdom United Kingdom
I am the Technical Director for Myphones.com, which specialises in developing integrated VoIP products and services for the consumer and SME markets. Technology-wise, we are heavily into the 2nd generation WS stack atop .NET, and basically most things Microsoft, as well as C++ on Linux.

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141216.1 | Last Updated 31 May 2003
Article Copyright 2003 by Simon Gregory
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid