Content by Category
.NET 1.x
.NET 2.0
.NET 3.0
.NET 3.5
.NET 4.0
.NET Assemblies
.NET Framework
.NET Getting Started
Accessibility
ADO.NET
Advertorials
Agile Development
AJAX
Architecture
ASP.NET
ASP.NET MVC
ASP.NET WebForms
Azure
B2B (Business Integration)
Bing
BizTalk
Book Excerpts
Build and Deploy
C#
C++
ClickOnce
Cloud Computing
Code Contracts
CODE on the Road!
COM+
Community
Conferences
Continuous Integration
Crystal Reports
CSLA.NET
CSS
Data
Design Patterns
Development Process
Display Technologies
Distributed Computing
DotNetNuke
DSL
Dynamic Programming
Editorials
Enterprise Services ("COM+")
Entity Framework
Events
Expression Blend
F#
Fox to Fox
Frameworks
Functional Programming
Git
Graphics
Internet Explorer 8.0
Interviews
iPhone
Iron Ruby
Java
Java Script
jQuery
LINQ
Linux
Mac OS X
MDX
Microsoft Application Blocks
Microsoft Business Rules Framework
Microsoft Dynamics
Microsoft Expression
Microsoft Office
Mobile Development
Mobile PC
Mono
MsBuild
Network
NHibernate
Object Oriented Development
Open Source
Opinion
Opinions
Oracle
ORM
Other Languages
Parallel Programming
Patterns
Podcasts
Post Mortem
PowerPoint
Print/Output
Product News
Product Reviews
Project Management
Python
Q&A
Rails
Rake
Reporting Services
REST
RIA Services
Ruby
Ruby on Rails
Search
Security
Services
SharePoint
Silverlight
SOA
Social Networks
Software & Law
Software Business
Source Control
Speech-Enabled Applications
SQL Server
SQL Server 2000
SQL Server 2005
SQL Server 2008
SQL Server CE/AnyWhere/Mobile/Compact
Subversion
Sync Framework
Tablet PC
TDD
Team System
Techniques
Testing and Quality Control
Tips
UI Design
UML
User Groups
VB Script
VB.NET
Version Control
VFP and .NET
VFP and SQL Server
Virtual Earth
Vista
Visual Basic
Visual Basic 6 (and older)
Visual FoxPro
Visual Studio .NET
Visual Studio 2005
Visual Studio 2008
Visual Studio 2010
Visual Studio Tools for Office
VSX
WCF
Web Development (general)
Web Services
WF
Whitepapers
Windows 7
Windows Azure
Windows Live
Windows Server
Windows Vista
WinForms
Workflow
WPF
XAML
XML
XNA
XSLT



Hacker Halted


 


DevReach

Reader rating:
Article source: CoDe (2004 - May/June)


Article Pages: < Previous - 1 2  3  4 - Next >


Behold WSE 2.0: Removing Another Layer of WS-Pain (Cont.)

Passing User Credentials

To properly pass the user token, the client must create an instance of the UsernameToken type, populate it with the correct username and password, and somehow indicate to the client-side run time that it must be serialized to the outgoing message. You can manually add the token to the proxy's RequestSoapContext or configure a client-side policy to enforce behavior through the pipeline. The latter requires that you add the token to a global cache where the WSE run time looks to collect all tokens and signatures for serializing messages to fit policy.

The same policy configuration interface shown in Figure 1 is used to define a policy cache for sending messages from the client. An application configuration file (app.config) is created by the tool to configure send and receive policy files for the client. Policy configuration files are also similar to Listing 1; client policy and service policy need only be complimentary, not identical.

After setting up a send policy for the client, specifying the requirement for outgoing messages to support UsernameToken signatures, the WSE run time looks for an appropriate token in the global cache during serialization. This global cache is accessible through the SecurityTokenCache type in the Microsoft.Web.Services.Security.Tokens namespace. Its purpose is to store tokens for client applications that apply policies to outgoing messages.

Using the global cache is an alternative to adding tokens directly to the Web service proxy class, which I'll use later when I need to deal with a more complicated security scenario. The following code creates a UsernameToken credential and adds it to the global cache:

UsernameToken userToken = new UsernameToken(
this.txtUsername.Text, 
this.txtPassword.Text,
PasswordOption.SendHashed);
SecurityTokenCache.
GlobalCache.Add(userToken);

UsernameToken is an object representation of the wsse:UsernameToken type. A username token per the WS-Security specification may provide a plain-text password, a hashed password, or no password at all. The PasswordOption enumeration I used requests that the password be sent hashed, but values exist for the other two options as well, though not recommended for obvious reasons. Passing a username token without a password implies that the user has previously been authenticated within the trust domain of the service. This only works if all applications are part of the same domain, or if a single sign-on solution that handles username tokens is introduced. Sending a password in clear text, of course, has implications if the message is not encrypted.

At this point, with two lines of code on the client, a single authentication method on the server, and some skillful pointing and clicking to configure policy and security, you have user authentication in place.

Extensibility for Hashed Passwords

Allowing the run time to validate the database-provided password against the information passed in the message has a flaw in its logic. Most secure databases actually store one-way hash versions of passwords that cannot be decoded for comparison. That means that either the client must pass a hash of their passwords using the same hashing algorithm, or you need a way to override the way WSE compares the password with the database hash.

It's neither reasonable to require clients to hash passwords with a consistent algorithm, nor is it appropriate, as that publicly exposes the hashed value. So we must rely on the future extensibility of WSE to help us out in this department. Today it is not possible to override how the run time validates passwords.

Controlling Message Privacy: Who Can Read the Message?

If message parts are encrypted with a public key, only the holder of the (well guarded) private key can decrypt and process those parts. Once again using policy configuration, you can quickly add functionality to your Web services, leveraging the WSE run time to handle decryption.

To configure TellerServices to require encryption, check the Require Encryption box (as shown in Figure 1), and select X509SecurityToken from the Token Type list. This adds a <wssp:Confidentiality> element to the policy WS-Policy configuration for the application shown in Listing 1. The security token used for encryption, in this case, is wsse:X509v3. The run time uses this policy setting to verify that the message body is encrypted using this type of token. Subsequently, the run time looks to the configured certificate store for a private key to match the public key token indicated by the message. Assuming the key is found, decryption is handled by the pipeline prior to deserializing the message. The security configuration interface (Figure 2) has an X.509 Certificate Settings section that specifies the certificate store, and further rules for certificate processing.

Once again, a SoapHeaderException is raised if the message does not satisfy security policy, so you need to turn to the client to configure its policy and properly encrypt the message.

Clients use a public key to encrypt messages, and the destination Web service must have access to the matching private key to successfully decrypt. The same configuration tool is used on the client side to specify an encryption policy for outgoing messages, which means that the run time on the client side also looks for a valid encryption token in the global cache. During message serialization, this token is retrieved from the configured certificate store on the client computer to encrypt the message. The default policy configuration sets <wssp:MessageParts> to wsp:Body(), indicating that the message body is the target of the encryption policy. This setting should match whatever the policy is for decryption on the service side.

The following code creates an encryption token and adds it to the global cache:

X509SecurityToken encToken = 
CertificateStoreUtil.
GetCertificateToken( 
ConfigurationSettings.AppSettings
["serverpublickeyid"]);
            
SecurityTokenCache.GlobalCache.Add( encToken);

CertificateStoreUtil.GetCertificateToken() encapsulates code to retrieve the public key from the certificate store on the local computer, based on a configured application setting. This assumes that the client has already retrieved the public key for the Web service, and placed it in the certificate store of the client computer.

Encryption policy can be configured for inbound and outbound messages using the same policy configuration tool. In this case, a reverse set of keys are used. The service signs the response with the trusted source' public key and the client application uses its private key to decrypt the response. In situations where message data is highly sensitive, although this adds bloat to the message payload, it also guarantees that no intermediaries or non-trusted parties can read encrypted message parts.

Verifying Message Integrity: Who Really Sent the Message?

Even if a valid set of user credentials are supplied to invoke the WS, and the message is encrypted so that only the ultimate receiver can inspect messages, there still leaves the lack of certainty over the source of the message. Any client with the right public key can encrypt a message, and it is possible that a username and password combination could be hacked. If a limited set of trading partners or applications are considered to be valid Web service clients, it is possible to require each partner to use their private key to sign messages.

When a message is signed, it not only can serve as proof of the message source, but also proof of message integrity. Signing a message part implies that an encrypted version of the message part is sent along with the message so that the receiver can verify that the part was not tampered with. If a message part is signed with a private key, the receiver can decrypt the signed message part using the public key, to verify that the decrypted part matches the message sent.

Requiring Private Key Signatures

The policy configuration tool supports many signing options, including asymmetric X.509 digital signatures. This type of signature is the one sure way to guarantee the sender of the message, without having to maintain a shared secret between sender and receiver (as in a symmetric authentication pattern like passing username and password tokens). So long as the service has access to the public key for the sender, the run time can automatically verify that the required message parts are correctly signed by a trusted source.

On the client side, a similar policy is configured for sending encrypted messages, but in this case, the private key for the client application is retrieved and stored in the global cache:

X509SecurityToken sigToken = 
Certificates.CertificateStoreUtil. 
GetCertificateToken( 
ConfigurationSettings.AppSettings 
["clientprivatekeyid"]);

SecurityTokenCache.GlobalCache.Add(sigToken);

By default, the policy setting applies signatures to the body of the message only. The WS-Policy specification supports signing specific headers in addition to the body, but with WSE 2.0, this must be configured manually by editing the policy XML.

&


Why We Need WS-Security

SSL was never an adequate security solution for Web services communication. Although SSL may have insured that the message was not tampered with en-route, it did not guarantee the source of the message, nor prevent message replay, and it indiscriminately added bloat to the message payload by encrypting the entire message, attachments included.

SSL also fails to address the presence of Web service intermediaries that sit between the initial sender and ultimate receiver of the message. Intermediaries may not support SSL encryption, but worse, the sender doesn't likely have a trust relationship with intermediaries, yet may freely view message decrypted by an SSL exchange. In addition, the intermediary may not hold an encrypted exchange with the ultimate receiver and the message is exposed on the wire.



Article Pages: < Previous - 1 2  3  4 - Next Page: 'Signing Multiple Message Parts' >>

Page 1: Behold WSE 2.0: Removing Another Layer of WS-Pain
Page 2: Identifying Application Users: Who Is Using the Application?
Page 3: Passing User Credentials
Page 4: Signing Multiple Message Parts

How would you rate the quality of this article?
1 2 3 4 5
Poor      Outstanding

Tell us why you rated the content this way. (optional)

Average rating:
3.4 out of 5

87 people have rated this article.

      CODE TRAINING

 

DevLink