SOA23—MICROSOFT WINDOWS CARDSPACE
INTRODUCTORY LAB
This lab covers the core features of Windows CardSpace and shows how a developer can add CardSpace
support to browser applications and Web services-based applications. How to be a third-party identity
provider (creating security token services and user provisioning) is outside the scope of this lab. The first
part covers the rationale behind CardSpace, how it works and how users interact with it. The second
part covers how to add CardSpace support to Web services built using Windows Communication
Foundation. The third part covers how to add CardSpace support to Web sites running ASP.NET 2.0 and
shows the browser experience using Microsoft Internet Explorer 7.0.
Copyright and license
This document supports a preliminary release of a software product that may be changed substantially
prior to final commercial release. This document is provided for informational purposes only and
Microsoft makes no warranties, either express or implied, in this document. Information in this
document, including URL and other Internet Web site references, is subject to change without notice.
The entire risk of the use or the results from the use of this document remains with the user. Unless
otherwise noted, the companies, organizations, products, domain names, e-mail addresses, logos,
people, places, and events depicted in examples herein are fictitious. No association with any real
company, organization, product, domain name, e-mail address, logo, person, place, or event is intended
or should be inferred. Complying with all applicable copyright laws is the responsibility of the user.
Without limiting the rights under copyright, no part of this document may be reproduced, stored in or
introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical,
photocopying, recording, or otherwise), or for any purpose, without the express written permission of
Microsoft Corporation.
Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property
rights covering subject matter in this document. Except as expressly provided in any written license
agreement from Microsoft, the furnishing of this document does not give you any license to these
patents, trademarks, copyrights, or other intellectual property.
© 2007 Microsoft Corporation. All rights reserved.
Microsoft, MS-DOS, Windows, Windows Server, Windows Vista and Windows CardSpace are either
registered trademarks or trademarks of Microsoft Corporation in the United States and/or other
countries.
All other trademarks are property of their respective owners.
Fictitious Disclaimer
The example companies, organizations, products, domain names, e-mail addresses, logos, people,
places, and events depicted herein are fictitious. No association with any real company, organization,
product, domain name, email address, logo, person, places, or events is intended or should be inferred.
Abstract
This lab covers the core features of Windows CardSpace and shows how a developer can add CardSpace
support to browser applications and web services-based applications. How to be a third party identity
provider (creating security token services and user provisioning) is outside the scope of this lab.
The first part covers the rationale behind CardSpace, how it works and how users interact with it. The
second part covers how to add CardSpace support to web services built using Windows Communication
Foundation. The third part covers how to add CardSpace support to web sites running ASP.NET 2.0 and
shows the browser experience using Internet Explorer 7.0. You can choose to type the code, cut and
paste it from this document or simply look at the solutions in C:\Cardspace\solutions.
WCF and ASP.NET 2.0 are by no means the only ways to add information card support to an application,
just as IE7 is not the only browser that can use CardSpace but we will limit ourselves to those
technologies for this tutorial.
If you want to see an example of using information cards with the LAMP stack then look at Kim
Cameron’s tutorial at http://www.identityblog.com/?page_id=430. Similarly, on the web you can find
CardSpace add-ins for the Firefox browser and indeed alternatives to CardSpace that can be used on
Linux and the Apple Mac.
If you want to play with Windows CardSpace outside the scope of this lab—which you will do! - please
check out the tutorials at http://cardspace.netfx3.com/.
For further information on Windows CardSpace and Information Cards please refer to the articles
referenced in the ADDITIONAL READING section.
Prerequisites
The reader should be familiar with C#, ASP.NET, the .NET Framework and HTML to easily follow the
sample code. Some familiarity with WCF is advantageous for the web services part but is not required.
Introduction
It’s easy to take for granted just how much the Internet enriches our lives.
Today we can research and buy almost anything online: from music, film and literature to holidays and
houses. We pay our bills and taxes online and check our overdrafts too. We can meet our lifelong
partner or broadcast ourselves miming to a 80’s pop song—and have 7 million people watch it! Blogging
is a phenomenon, not just for geeks but for everyone.
The sheer diversity and scale of the web enables us to connect, collaborate and share with people in
ways undreamt of only a short time ago. Yet we are only scratching the surface of what is possible. Web
2.0, the second generation of services available on the Web, has given us rich new applications based on
Web Services, Atlas and RSS. What will Web 3.0, 4.0 and 5.0 bring us?
And yet despite all this, studies are showing that many people are cutting back on the amount of online
purchasing they do—or stopping altogether. Just as the usefulness of the Internet is dramatically
increasing, people’s confidence in it is decreasing.
Why? Because the Internet is a dangerous place! Online identity theft, fraud, and privacy concerns are
on the rise. It seems no one is safe from phishing. Users have to keep track of 10s or 100s of accounts
and passwords - or take the risk of reusing them.
At the root of these problems is a fundamental flaw in the Internet: it was designed without a system of
digital identity. There have been many attempts to solve this problem but all we’ve ended up with is an
inconsistent patchwork of ad hoc solutions. In Enterprises there are often hundreds of line-of-business
applications each using a different way to authenticate and authorize the user! It is precisely for this
reason that we’re now at a critical point in the evolution of the Internet.
What’s needed is a simple, consistent, secure system to represent digital identity; one that is
independent of—and accepted by—all vendors, one that is open and standards-based, covers all
possible scenarios, works with existing and future systems and places users where they belong: at the
centre, in control of their identity and protecting their privacy.
Sound impossible? Well, something similar has been done before. In the same way that the Internet
protocols abstract away the complex different underlying networking layers—such as Ethernet, Token
Ring and 802.11 (before it was invented)—we can form an identity metasystem, using standard, open
protocols to abstract away the details of underlying identity systems and address the core problem: to
safely, reliably identify sites to users and users to sites.
At the heart of this identity metasystem is the concept of representing digital identity as a set of claims
(e.g. “my name is Nigel Watling”, “I am British” and “my employer is Microsoft”) in a security token.
Security tokens can be implemented in many different ways in different systems (e.g. using SAML,
Kerberos, X.509) but how those secure tokens are used is always the same: an identity provider creates
the token and a relying party consumes the token. Identity Providers are employers providing employee
IDs, banks providing account details, a government providing an SSN or the user themselves providing
their name and address. Relying parties are any web site or web service that needs to consume those
claims.
Crucially, when we use this model to transform one type of security token into another we can achieve
widespread interoperability of identity across multiple systems.
Viewed in these terms, providing and using a user’s digital identity becomes very simple:
1. When a web site or service needs a user’s identity, it states what kind of security token it needs
- which claims and which token format (e.g. SAML 1.1).
2. The user chooses a suitable identity provider and asks for a token of the required form and
supplying any credentials required by the identity provider in the process. For example, if the
identity provider is my employer I might use my normal Windows logon credentials.
3. The token is then created and passed—under the control and consent of the user—to the
relying party.
4. The above steps provide us with a consistent context, enabling the mitigation of phishing
attacks.
It so happens that we have a ready-made set of standard, open protocols that enable us—or anyone—
to implement all of this: the web and web service protocols (WS-*).
We can express token requirements and capabilities either by using WS-SecurityPolicy or embedding
them in tags on an HTML page. We can provide security tokens using a security token web service,
sending them attached either to a SOAP message using WS-Security or to an HTTP POST.
Once we’ve done this, all that remains is to provide a simple, secure and intuitive user experience: a
piece of software on the user’s device—on the client machine—that drives the whole process end-toend, allows the user to choose an identity provider and control what information is provided and to
whom. What we need is an identity selector.
So how does a user select an identity provider to create the security token? Well, how do we select our
identity in the physical world? If a policeman asks for our identity we might choose our driving license or
passport, if a shop asks then we choose one of our credit cards, if a business associate asks we typically
choose a business card. We use cards to identify ourselves.
So the obvious way—but not the only way—to ask the user to select an identity provider is to present
her with a set of virtual information cards—not physical cards—each representing a particular identity
provider. The user simply picks one that can meet the relying party’s requirements and a token is
requested and returned. The user then checks the information being sent and the token is passed on to
the web site or service; then the claims are extracted and the user is authorized.
Windows CardSpace is an identity selector for Windows Vista, Windows XP and Windows Server 2003.
Let’s see how it works…
Exercise 1: Using the Windows CardSpace Control Panel applet
Log in to the virtual machine using the User account with a password of Pass@word1.
The Windows CardSpace Control Panel applet allows you to manage your information cards.
Before going any further, start Task Manager (Ctrl-Shift-Esc), select the Processes tab, and position the
Task Manager window so that it is flush with the right edge of the screen and so that you can see the
entire list of processes. Make sure processes from all users are visible and sort the list by Image Name.
Once you’ve done this, go into Control Panel and launch the Windows CardSpace
control panel applet. If you are using Category View, you will find the applet under User
Accounts. If you’re using the lab virtual machine, you’ll find a shortcut on the desktop.
Launching the applet for the first time brings up a Welcome to Windows CardSpace
dialog. When you’re done reading it, click OK. The Select a card to preview dialog will be displayed
(similar to the image below):
This window and the other CardSpace windows are running on a private desktop—much in the same
way that the Windows Security dialog runs in a separate desktop. The “desktop” behind CardSpace
windows appears frozen and grayed out and you cannot access other processes—including Task
Manager—until you exit from the CardSpace UI. In fact, what you see in the background is not your
desktop at all but a bitmap of how the screen looked when you launched CardSpace. No matter how
long you stare at it, the time shown on the clock won’t update until you exit the CardSpace UI! By
running on a separate Windows desktop and limiting access rights we raise the bar for the bad guys,
making it more difficult to emulate the user experience, especially from within a browser.
Just before a snapshot was taken of the screen, Task Manager should have had time to show that two
new processes had been launched: icardagt.exe, running as the logged-on user, and infocard.exe,
running as SYSTEM (you might only see infocard.exe). The icardagt.exe process takes care of the
CardSpace user interface and the infocard.exe process is the main CardSpace engine. They communicate
with each another using RPC. You might also see rundll32.exe which is used to launch the infocardcpl.cpl
control panel applet.
Ultimately, Windows CardSpace and information cards are a means for the user to acquire a security
token containing identity information. It is this security token which is presented to the requesting
service or website the user is trying to access.
The identity data within a security token takes the form of a set of claims, an abstract representation of
identity that can cover almost anything that might be appropriate in a given context. For example, one
claim might be the user’s age, another might be the user’s address, but equally there might be a claim
that that the user has a dog called Fido, or has access to a million doughnuts.
The security token is acquired from an identity provider. This could be the user’s employer, their
government, their bank or any entity the user has a relationship with that could provide identity
information. Your vet might be a good identity provider to indicate you had a dog (named Fido).
Each card represents a relationship with an identity provider and contains metadata about how to
obtain a security token from that identity provider. It specifies what the token format is (e.g. SAML,
XrML or Kerberos) and it has a list of claims that can be provided (e.g. name, surname and address).
When the user selects a card a request is sent to the corresponding identity provider for a security
token. Once this token has been issued by the identity provider—containing the required claims for that
transaction—it can be presented, with the user’s consent, to the web site or service requesting that
particular set of identity claims. You are able to use the same card at many sites or multiple cards at the
same site.
There are two types of card: personal cards and managed cards. Personal cards—also known as selfissued cards—are created and maintained by the user in the Windows CardSpace user interface. In
other words, with personal cards it is the user who is the identity provider. This is a common scenario:
we often provide information about ourselves to websites. In the physical world we use business cards
we have authored and we provide signed forms all too frequently. Personal cards have a small and fixed
set of claims. The cards are stored locally and the personal identity information associated with the
cards is also stored locally.
Managed cards—also referred to as provider cards—are supplied to the user in the form of a signed
.CRD file by a third party identity provider (e.g. an employer, a financial institution or a government) and
installed into the CardSpace system by the user. The set of claims associated with a managed card is not
limited in any way. It is determined by the identity provider (e.g. an employer might provide an
employee id claim, a bank might provide an account balance claim). Managed cards, once installed, are
stored locally on the user’s device but the personally identifiable information (PII) associated with a card
is not stored locally. The data is owned and stored by the identity provider who supplied the card.
Functionally, personal cards and managed cards are the same. They both point to a location where
information about the user can be retrieved and presented in the form of a security token. It’s just that
for one type of card that information is created by the user and stored locally and for the other type of
card the data is created and stored by a third party.
In neither case does the card itself contain any personally identifiable information (e.g. “Bill Gates”,
“One Microsoft Way”, “Male” or “+1 425 705 8080”). Cards contain metadata about where and how to
obtain security tokens. It is the security tokens that contain the PII and they have cryptographic
protection.
Click Add a card task on the right or double-click on the Add a card card in the Your cards group, to
create a personal card. You can also use this option to install managed cards.
Go ahead and create some personal cards. Create at least two, putting in whatever information you feel
like. Once you’ve saved a card, you can select it in the main screen and select Preview to view the card’s
details. Preview one of your cards. You should see something like this:
The dialog states that the details of the personal card are encrypted and stored on the local machine.
Semantically, the card and its details are separate. Physically, they are both located in the CardSpace
store which is created the first time CardSpace is used by someone:
<Drive>:\Users\<username>\AppData\Local\Microsoft\CardSpace\CardSpace.db on Windows Vista.
<Drive>:\Documents and Settings\<username>\Local Settings\Application Data\Microsoft\
CardSpace\CardSpace.db on Windows XP and Windows Server 2003
The card itself is never sent anywhere—unless you explicitly export it and move it yourself! The card
contains metadata about how to get a security token and what that token might look like. It is the
security token that contains the claim data and it is the security token which travels over the wire (not
the card). However, from the user’s perspective the natural thing to visualize is the card being presented
to a web site or service. This is analogous to our day-to-day use of cards such as our driving license,
passport, credit cards or vet card. Therefore it is this language and metaphor that is used in the user
interface.
Personal cards are assigned a card ID at creation time by CardSpace. This globally unique URI is used to
uniquely identify a card and also to help create a per site private personal identifier (PPID) to help
uniquely identify the user to a site. The PPID claim is normally included in security tokens since it helps
sites to identify returning users. If the only claims you provided were First Name = John and Last Name =
Smith a site wouldn’t be able to tell you apart from every other John Smith on the Internet! Any attempt
at personalizing your experience would be wasted (or at least shared with a lot of people!).
With managed cards it is left to the identity provider to generate a unique card ID for each card. Identity
providers are also free to create private personal identifiers in whichever way they choose. Indeed an
identity provider may not choose to provide a PPID claim at all. Your employer, for example, might use
an “Employee Number” claim to uniquely identify you whereas a government identity provider might
use your social security number. However, the PPID claim uri is available and it is a good idea for identity
providers to use it so that relying parties can consistently rely on the PPID as a user identifier for an IP.
Because two IPs could potentially use the same PPID value, relying parties should not rely on the PPID
claim alone to identify users. For example, two employers could each use incrementally increasing
numbers starting at 1 for their employee number: Bill Gates and Steve Jobs could both be “employee
number = 1”. Therefore relying parties should use a combination of the RSA key used to sign the security
token (uniquely identifying the IP) plus the PPID (uniquely identifying the user for that particular IP). RPs
can store these values as separate fields of their user account database or hash the two together and
store that.
Try exporting your personal cards, deleting some and importing them again. When you are done close
the CardSpace UI (and Task Manager) and go on to the next exercise.
Exercise 2: Build a simple WCF application
Next we are going to build a very simple web service and client using Windows Communication
foundation (WCF). We will add CardSpace support in the next exercise.
Launch Visual Studio 2005 and open a new project (File > New > Project).
NOTE: If prompted select General Development Settings in the Chosse Default Environment Settings
dialog.
Select the Visual C# | Windows | Console Application template, make sure Create directory for solution
is checked, and enter:
Name: HelloService
Location: C:\Cardspace\Exercises
Solution Name: HelloCardspace
This service is our relying party—it relies on a user identity being provided.
Add a reference to System.ServiceModel either through the Project menu or by right-clicking either the
project name or References in the Solution Explorer. You’ll find the System.ServiceModel.dll assembly in
C:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\ folder.
Let’s define the interface for our service class. Select Add New Item… for the project, select the
Interface template and call it HelloServiceContract.cs. Then enter the following code:
using System.ServiceModel;
namespace HelloService
{
[ServiceContract]
interface IHello
{
[OperationContract]
string Say();
}
}
We have a very simple service interface with one method, Say(), exposed as a web service using the
ServiceContract and OperationContract attributes. Let’s implement the interface and create a service
host using that class in Program.cs:
using System;
using System.ServiceModel;
namespace HelloService
{
class Hello : IHello
{
public string Say()
{
return "Hello World";
}
}
class Program
{
static void Main(string[] args)
{
ServiceHost sh= new ServiceHost(typeof(Hello), new Uri("http://localhost:4123/helloService"));
sh.Open();
Console.WriteLine("Service listening...");
Console.WriteLine("Press <Enter> to finish");
Console.ReadLine();
sh.Close();
// close the service
}
}
}
Next add an application configuration file, App.config, for the service through Project Add New Item…
NOTE: This can be copied from the manual which is located on the VM in C:\Manuals.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service
name="HelloService.Hello"
behaviorConfiguration="helloServiceBehavior">
<endpoint
address="helloEndpoint"
contract="HelloService.IHello"
binding="wsHttpBinding"
bindingConfiguration="helloBinding">
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="helloBinding">
<security mode="Message">
<message />
</security>
</binding>
</wsHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="helloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
That’s our service finished. You might like to build it to check that the code compiles correctly (Ctrl-ShiftB) and runs (F5).
Now we’ll create a client to call this service. Normally you would use svcutil.exe to create a service proxy
and use that. However, in this case we’re going to reuse the service contract directly and create our
client code by hand to keep it very simple and easy to understand.
From the File menu or from the context menu for the HelloCardspace solution in the Solution Explorer,
add a new Console Application project called Client to the HelloCardspace solution.
Add a reference to System.ServiceModel either through the Project menu or by right-clicking either the
project name or References in the Solution Explorer.
Next, in the Solution Explorer, drag and drop HelloServiceContract.cs onto the Client project. You should
end up with two copies of HelloServiceContract.cs: one in the client project and one in the service
project.
Now open the client’s Program.cs and enter the following:
using System;
using System.ServiceModel;
namespace Client
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hit any key when service is ready");
Console.ReadKey();
ChannelFactory<HelloService.IHello> cnFactory =
new ChannelFactory<HelloService.IHello>("helloClient");
HelloService.IHello chn = cnFactory.CreateChannel();
Console.WriteLine(chn.Say());
// Clean up
cnFactory.Close(); // close the client’s channel
Console.WriteLine("Press <Enter> to finish");
Console.ReadLine();
}
}
}
And finally add a client configuration file, App.config, again through Project Add New Item…
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint
name="helloClient"
address="http://localhost:4123/helloService/helloEndpoint"
contract="HelloService.IHello"
binding="wsHttpBinding"
bindingConfiguration="helloBinding">
</endpoint>
</client>
<bindings>
<wsHttpBinding>
<binding name="helloBinding">
<security mode="Message">
<message />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
</configuration>
There, we’re done. Build the solution and make sure that it compiles successfully.
Before we run it, we’ll set the project run order. Right mouse click the solution and select Properties… In
the Startup Project property page choose Multiple startup projects, make sure that both projects have
an action of Start and that HelloService appears above Client in the running order.
You can now hit ‘F5’ and the application should run successfully. If all goes well, “Hello World” should
appear in the client console. If it doesn’t, it is probably because there is a mistake in one or both of the
App.config files.
Once you have this working you are ready for the next exercise.
Exercise 3: Add Information Card support to the WCF application
What do we need to do to add Information Card support to our application? Well, the key point is that
we don’t have to write any code to enable CardSpace. We are able to add information card
authentication by editing the client and service App.config files which, as you’ve probably discovered by
having to debug them at runtime in the previous exercise, can be changed without recompiling the
source files. So, if you want to, you can close all the code windows in Visual Studio and just keep the
client and service App.config files open.
Using just config files is consistent with the WCF vision of enabling administrators to make configuration
changes without having to recompile. Everything you can do in config files you can still do in code if you
want to (the converse is not true), but by using config files you add deployment flexibility to your
application. A wise developer could expose two (or more) endpoints for a particular service: one using
Windows authentication for Enterprise single sign-on and one using CardSpace authentication for
federated clients. Should you need to limit the flexibility of config files you can use binding requirements
and custom channels, preventing system administrators from making inappropriate changes.
CardSpace is used by WCF when we use message level security with the “IssuedToken” client credential
type. In this case WCF passes policy information (required token format, claims, etc) to the Windows
CardSpace system which then displays the CardSpace UI to the user so they can choose which of their
digital identities they want to use.
Open the HelloService App.config and alter the bindings section to look like the following:
<bindings>
<wsHttpBinding>
<binding name="helloBinding">
<security mode="Message">
<message clientCredentialType="IssuedToken" />
</security>
</binding>
</wsHttpBinding>
</bindings>
Now open the Client App.config and adjust the client’s binding to match that of the service. This is
identical to the service binding:
<bindings>
<wsHttpBinding>
<binding name="helloBinding">
<security mode="Message">
<message clientCredentialType="IssuedToken" />
</security>
</binding>
</wsHttpBinding>
</bindings>
We’ve specified that the client should use an issued token to pass credentials to the service but how
does the service identify itself to the client app? How does the client trust the service?
One of the requirements of CardSpace is that any potential recipients of the user’s digital identity must
identify themselves to the user using cryptographically verifiable but human-friendly means. Only then
can a rational decision be made by the user about whether to trust that party and provide them with
information.
Web servers identify themselves on the Internet today using X.509v3 certificates—SSL certificates
typically purchased from Certificate Authorities such as Verisign and Thawte—and CardSpace takes
advantage of this.
A service must identify itself using an X.509 certificate. The first time a service requires the user’s
identity—or the interaction with the service changes (e.g. a new claim is added or the privacy policy
changes)—CardSpace pops up a special dialog displaying information from the certificate (CardSpace
keeps track of where the user’s digital identities are used). Ideally, this certificate should be an Extended
Validation (EV) certificate—also referred to as a high assurance certificate—which the CardSpace UI will
reflect.
The X.509 certificate should identify the organization behind the service and, to help the human in this
process, the certificate should utilize logotypes [RFC 3709] for the issuer (the Certificate Authority) and
the subject (the relying party). Logotypes provide a mechanism where signed jpeg or gif images are
bound to the certificate to help users recognize the relevant parties and make an informed decision to
release their personal information.
The certificate we use in this lab to identify the service (www.fabrikam.com) is an EV certificate and it
has logotypes associated with it.
WCF and CardSpace need to access the service certificate in both the client and the service applications.
The public key of the relying party’s certificate is needed to encrypt security tokens. On the service side
WCF and CardSpace will use the certificate’s private key to decrypt the supplied security token. This
means that the client and the service must have access to the service’s certificate, which is usually done
via the certificate store. For websites we can avoid this configuration issue on the client since browsers
do the legwork of accessing the certificate’s public key for us.
Let’s modify the client and service App.config files so that WCF can locate and use the
www.fabrikam.com certificate.
Start with the service App.config.
The mechanism for enabling WCF to access a resource on the local machine is to use a behavior. We
already have a service behavior which exposes service metadata (WSDL). Modify this behavior as
follows:
<behaviors>
<serviceBehaviors>
<behavior name="helloServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceCredentials>
<issuedTokenAuthentication allowUntrustedRsaIssuers="true" />
<serviceCertificate
findValue="www.fabrikam.com"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
Here you can see we are using the www.fabrikam.com certificate in the LocalMachine Personal (“My”)
store. You could store the certificate in any of the certificate stores but this is an appropriate place for a
service. Make sure that the account the service runs under has access to this certificate’s private key.
For this lab it should already be done for you.
Here are the steps should you need to do it yourself (not necessary if you’re using the lab virtual
machine):
Locate the private key using the Windows SDK tool findprivatekey.exe, enter at the command prompt:
findprivatekey.exe My LocalMachine
Choose the Fabrikam certificate from the dialog box to get the location and then use Windows Explorer
to add read access (“Read and Execute”) for the service account. You can do this programmatically by
using CACLS or ICACLS on Vista:
cacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\<privatekeyfilename> /E /G <account>:R
icacls C:\ProgramData\Microsoft\Crypto\RSA\MachineKeys\<privatekeyfilename> /grant
<account>:RX
WCF needs the untrusted issuers flag if we are not using a fully trusted certificate. This might be, for
example, because the certificate has expired, or a certificate revocation list (CRL) is inaccessible (which is
the case here).
WCF can now access the certificate’s private key.
However, we are not quite finished—we can add an identity element to the service’s endpoint:
<services>
<service
name="HelloService.Hello"
behaviorConfiguration="helloServiceBehavior">
<endpoint
address="helloEndpoint"
contract="HelloService.IHello"
binding="wsHttpBinding"
bindingConfiguration="helloBinding">
<identity>
<certificateReference
findValue="www.fabrikam.com"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</identity>
</endpoint>
<endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex" />
</service>
</services>
This will allow clients to verify that the identity of the service endpoint matches the identity as given in
the service’s metadata (WSDL). The WCF runtime on the client essentially authenticates the service
before sending a message to it: a client shouldn’t send a message to a service until the service has been
authenticated based upon what is known in advance from the service’s metadata.
Actually it’s not necessary for you to explicitly add this <identity> element to the service config (but you
can if you want to). Because we’re using the Issued Token client credential type, WCF knows that this
<identity> element should be exposed in the endpoint and includes it for you if you omit it from the
configuration file.
Now comes the client’s App.config. On the service side we needed a behavior to access the private key.
On the client we can either add a behavior or use the MetadataResolver class to retrieve the certificate
information via WS-MetadataExchange. Let’s add a <behaviors> section as with the service:
<behaviors>
<endpointBehaviors>
<behavior name="helloClientBehavior">
<clientCredentials>
<serviceCertificate>
<authentication
trustedStoreLocation="LocalMachine"
revocationMode="NoCheck"/>
<defaultCertificate
findValue="www.fabrikam.com"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</serviceCertificate>
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
For convenience, in this tutorial we are accessing the certificate in the LocalMachine “My” store. In a
real client deployment you are likely to put the certificate containing the service’s public key into the
CurrentUser’s store. The client app should be running using the logged-in user account so the Current
User store is a natural location for the certificate.
Now link the behavior to the client:
<client>
<endpoint
name="helloClient"
address="http://localhost:4123/helloService/helloEndpoint"
contract="HelloService.IHello"
binding="wsHttpBinding"
behaviorConfiguration="helloClientBehavior"
bindingConfiguration="helloBinding">
</endpoint>
</client>
On the client, we do need to specify the expected service endpoint identity (this will also be created by
svcutil.exe should you use it):
<client>
<endpoint
name="helloClient"
address="http://localhost:4123/helloService/helloEndpoint"
contract="HelloService.IHello"
binding="wsHttpBinding"
behaviorConfiguration="helloClientBehavior"
bindingConfiguration="helloBinding">
<identity>
<certificateReference
findValue="www.fabrikam.com"
storeLocation="LocalMachine"
storeName="My"
x509FindType="FindBySubjectName" />
</identity>
</endpoint>
</client>
Now comes the moment of truth: run the application. If all goes well you should get a dialog similar to
the screen shot below. Once you have chosen a personal card, a security token is created by the
CardSpace system and, following your approval, sent to the service. You have the opportunity to see
what information will be passed to the website by selecting Preview. The site-specific card Id is a
human-readable form of the private personal identifier. Once you’ve previewed the card, select Send to
continue.
In our example the service doesn’t actually do anything with the security token (yet) and simply displays
“Hello World”. Step through the client code to see which line of code it is that triggers the CardSpace
dialog.
At any time you can cancel and return to the website without doing anything—the user is in control! If
you don’t choose a card then you get an ugly Unhandled Exception error but you can catch errors on the
client using the exceptions defined in the System.IdentityModel.Selectors namespace.
Exercise 4: Processing claims using System.IdentityModel
The next step is to write some service code to crack open the security token that has been generated by
the identity provider that was selected by the user. In this case the token is self-issued and has been
generated by the CardSpace system on the user’s machine but exactly the same process applies to
tokens generated by third party identity providers.
WCF and the System.IdentityModel namespace have some very useful classes for security token
processing on the server-side.
In the HelloService project, add a reference to System.IdentityModel.dll at C:\Program Files\Reference
Assemblies\Microsoft\Framework\v3.0\.
Next we’ll modify the service implementation in Program.cs.
Add two using statements:
using System.IdentityModel.Claims;
using System.IdentityModel.Policy;
Next modify the Hello class to display the claims in the service console window:
class Hello : IHello
{
public string Say()
{
GetIdentity();
return "Hello World";
}
private void GetIdentity()
{
AuthorizationContext ctx =
OperationContext.Current.ServiceSecurityContext.AuthorizationContext;
foreach (ClaimSet claimSet in ctx.ClaimSets)
{
foreach (Claim claim in claimSet)
{
Console.WriteLine();
Console.WriteLine("ClaimType: " + claim.ClaimType);
Console.WriteLine("Resource: " + claim.Resource);
Console.WriteLine("Right: " + claim.Right);
}
}
return;
}
}
Now run the application and see what you get in the service console window.
Did you get all the claims you specified in the card?
The reason that you didn’t is that we are using wsHttpBinding. This binding by default only allows a
restricted claim set. To take advantage of all the claims in a security token we shall use a different
binding.
Open the service App.config and add the following binding inside the <bindings> section after the
existing <wsHttpBinding>section:
<wsFederationHttpBinding>
<binding name="helloFederatedBinding" >
<security mode="Message">
<message
algorithmSuite="Basic128"
issuedTokenType="urn:oasis:names:tc:SAML:1.0:assertion"
issuedKeyType="SymmetricKey">
<issuer address="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self"/>
<claimTypeRequirements>
<add claimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"/>
<add claimType ="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"/>
</claimTypeRequirements>
</message>
</security>
</binding>
</wsFederationHttpBinding>
Here we are specifying that we will only accept security tokens containing the EmailAddress claim and
the givenname claim and we are only accepting self-issued security tokens.
We also need to modify our service endpoint to use this binding:
binding="wsFederationHttpBinding"
bindingConfiguration="helloFederatedBinding"
That’s our service App.config completed. Now let’s turn our attention to the client App.config.
Cut and paste the <wsFederationHttpBinding> section you just entered into the <bindings>section of the
client App.config, again after the existing <wsHttpBinding>section. Then change the binding and binding
configuration under the client <endpoint>:
binding="wsFederationHttpBinding"
bindingConfiguration="helloFederatedBinding"
Now run the application again and see what you get. This time you should get a more interesting
confirmation UI. For example:
and the included claims should displayed in the service console window.
WCF Service Configuration Editor and Svcutil.exe
Don’t make the mistake of thinking that WCF is all about tedious config file editing. In this lab we have
done things by hand to improve the learning experience. In practice, developers will take advantage of
the WCF Service Configuration Editor to perform service configuration and they will use the Service
Model Metadata Utility Tool (Svcutil.exe) to generate client proxies for those services. This makes life
much simpler!
Exercise 5: Adding Information Cards to Browser Applications
The sample web files are in the website folder. Run the samples with Windows Explorer, or Internet
Explorer by navigating to https://www.fabrikam.com/CardSpace. All the URLs are saved as favorites in
Internet Explorer. You can examine the code either by using notepad or by using Visual Studio and
choosing File > Open > Web site… and selecting C:\CardSpace\Website\.
Sample 1 demonstrates the html code required to engage the Identity Selector:
https://www.fabrikam.com/CardSpace/sample1.htm
The Identity Selector can be invoked in a browser application by using either a particular <object>
element or binary behavior object. Essentially the browser—or an add-in—needs to recognize the object
tag / binary behavior and invoke the CardSpace system in much the same way that WCF invokes the
CardSpace system when it sees the “IssuedToken” client credential type.
Sample 1 uses the simplest option which is to include the object element in the body of a <form>
element. This activates the Identity Selector when the form is submitted:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Sample 1</title>
</head>
<body>
<form id="form1" method="post" action="login1.aspx">
<div>
<button type="submit">Click here to sign in with your Information Card</button>
<object type="application/x-informationcard" name="xmlToken">
<param name="tokenType" value="urn:oasis:names:tc:SAML:1.0:assertion" />
<param name="issuer" value="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self"
/>
<param name="requiredClaims"
value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" />
</object>
</div>
</form>
</body>
</html>
In this example, we are posting the results of the <object> element (i.e. the security token) directly into
the login1.aspx page, where the processing will occur. The Identity Selector will display when the submit
button is pressed.
The important elements of the object element are:
type="application/xinformationcard"
Instructs the browser to engage the identity selector object
param
name="tokenType"
Controls the token type the identity selector will emit; in this case a SAML
1.0 token
param name="issuer"
The identity provider’s URL that will provide the identity. In this case, a
hard-wired URI that invokes the built-in self-issuing provider.
param
name="requiredClaims"
The claims that the relying party is asking of the User to provide from the
Identity Provider. The examples here are part of the pre-defined set for
self-issued (personal) cards.
When the button is pressed, the identity selector will display. The user experience is exactly the same as
with web services applications. The user chooses a card; a request is made to the corresponding identity
provider for a security token meeting the web site’s requirements and, in the browser case, the
encrypted card data is posted to the login1.aspx page:
<%@ Page Language="C#" Debug="true" ValidateRequest="false"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
string xmlToken;
xmlToken = Request.Params["xmlToken"];
if (xmlToken == null || xmlToken.Equals(""))
{
xmlToken = "*NULL*";
}
Label1.Text = xmlToken;
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Login Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
The value of the token is: <asp:Label ID="Label1" runat="server"
Text="Label"></asp:Label></div>
</form>
</body>
</html>
The login1.aspx page displays the encrypted token back to the screen.
Sample 2 activates the Identity Selector on demand:
https://www.fabrikam.com/CardSpace/sample2.htm
Developers may wish more flexibility around the timing and handling of the invocation of the Identity
Selector. In order to engage the Identity Selector at a time more suited to a particular application, here
the <object> element is scripted in order return the encrypted token on request.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
<title>Authenticate</title>
<object type="application/x-informationcard" name="_xmlToken">
<param name="tokenType" value="urn:oasis:names:tc:SAML:1.0:assertion" />
<param name="issuer" value="http://schemas.xmlsoap.org/ws/2005/05/identity/issuer/self"
/>
<param name="requiredClaims"
value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier" />
</object>
<script language="javascript">
function GoGetIt()
{
var xmltkn=document.getElementById("_xmltoken");
var thetextarea = document.getElementById("xmltoken");
thetextarea.value = xmltkn.value ;
}
</script>
</head>
<body onload="GoGetIt()">
<form id="form1" method="post" action="login2.aspx">
<div>
<button name="go" id="go" onclick="javascript:GoGetIt();">Click here to get the token.</button>
<button type="submit">Click here to send the card to the server</button>
<textarea cols=100 rows=20 id="xmltoken" name="xmlToken" ></textarea>
</div>
</form>
</body>
</html>
The <object> element is placed in the header of the HTML document, and the Identity Selector is
invoked when the value property is accessed. The script in this example places the token XML into a
<textarea> allowing the developer to view the contents before the <form> is submitted.
Note: The token text in the text area will not exactly match the token text in the login2.aspx page
because the browser is suppressing the display of the xml tags.
Sample 3 shows how to get access to the claims on the server using the TokenProcessor class to access
the claims in a card sent to a website.
https://www.fabrikam.com/CardSpace/sample3.htm
In order to extract the claims out of the encrypted data, developers using ASP.NET 2.0 can use the
TokenProcessor.cs sample code presented in this exercise. The TokenProcessor class handles all of the
decryption and verification of the token, using the Windows Communication Foundation classes. The
TokenProcessor is used in the target page of the post, in this case the login3.aspx page:
<%@ Page Language="C#" Debug="true" ValidateRequest="false" %>
<%@ Import Namespace="System.IdentityModel.Claims" %>
<%@ Import Namespace="Microsoft.IdentityModel.TokenProcessor" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void ShowError(string text)
{
fields.Visible = false;
errors.Visible = true;
errtext.Text = text;
}
protected void Page_Load(object sender, EventArgs e)
{
string xmlToken;
xmlToken = Request.Params["xmlToken"];
if (xmlToken == null || xmlToken.Equals(""))
{
ShowError("Token presented was null");
}
else
{
Token token= new Token(xmlToken);
givenname.Text = token.Claims[ClaimTypes.GivenName];
surname.Text = token.Claims[ClaimTypes.Surname];
email.Text = token.Claims[ClaimTypes.Email];
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Login Page</title>
</head>
<body>
<form id="form1" runat="server">
<div runat="server" id="fields">
Given Name:<asp:Label ID="givenname" runat="server" Text=""></asp:Label><br/>
Surname:<asp:Label ID="surname" runat="server" Text=""></asp:Label><br/>
Email Address:<asp:Label ID="email" runat="server" Text=""></asp:Label><br/>
</div>
<div runat="server" id="errors" visible="false">
Error:<asp:Label ID="errtext" runat="server" Text=""></asp:Label><br/>
</div>
</form>
</body>
</html>
In order to get the values of the claims out of the token, use the Claims property passing it the claimURI
you are looking for. The URIs for self-issued cards are listed later in this document and they are
predefined in the ClaimTypes class.
There is one piece of configuration data that goes along with the TokenProcessor. In the web.config file,
you can find the following:
<appSettings>
<add key="MaximumClockSkew" value="60"/>
</appSettings>
Maximum Clock Skew specifies the maximum number of seconds the client and the server can be out of
skew. The default is 10 seconds.
When the token is posted to the login page, and decrypted we can now see the claim values (similar to
the screen shot below):
Sample 4 shows how to uniquely identify the user using the TokenProcessor class:
https://www.fabrikam.com/CardSpace/sample4.htm
Given that anyone can create a card with the same values for the claim, it is necessary to be able to
uniquely identify a particular card, other than the self-issued claim values. This is done by calculating the
UniqueID of the card. This UniqueID is the hash of the Issuer’s Public Key and any claim unique to that
issuer. Since the keys for the conversation between the client and the server are randomly generated on
the first communication, the public key is unique to card—if you use the same self-issued card at
multiple sites, a new key pair is generated with every new association. For the unique claim, the Private
Personal Identifier (PPID) of the card is unique to that issuer. The UniqueID() property does the
calculation and hash of the value for the developer.
Change login4.aspx as follows:
<%@ Page Language="C#" Debug="true" ValidateRequest="false" %>
<%@ Import Namespace="System.IdentityModel.Claims" %>
<%@ Import Namespace="Microsoft.IdentityModel.TokenProcessor" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
protected void ShowError(string text)
{
fields.Visible = false;
errors.Visible = true;
errtext.Text = text;
}
protected void Page_Load(object sender, EventArgs e)
{
string xmlToken;
xmlToken = Request.Params["xmlToken"];
if (xmlToken == null || xmlToken.Equals(""))
{
ShowError("Token presented was null");
}
else
{
Token token= new Token(xmlToken);
givenname.Text = token.Claims[ClaimTypes.GivenName];
surname.Text = token.Claims[ClaimTypes.Surname];
email.Text = token.Claims[ClaimTypes.Email];
uid.Text = token.UniqueID;
}
}
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Login Page</title>
</head>
<body>
<form id="form1" runat="server">
<div runat="server" id="fields">
Given Name:<asp:Label ID="givenname" runat="server" Text=""></asp:Label><br/>
Surname:<asp:Label ID="surname" runat="server" Text=""></asp:Label><br/>
Email Address:<asp:Label ID="email" runat="server" Text=""></asp:Label><br/>
Unique ID:<asp:Label ID="uid" runat="server" Text=""></asp:Label><br/>
</div>
<div runat="server" id="errors" visible="false">
Error:<asp:Label ID="errtext" runat="server" Text=""></asp:Label><br/>
</div>
</form>
</body>
</html>
The result is a 20 character base64 encoded hash value that can be used in a database to uniquely
identify a visitor:
In order to change the claim used for uniqueness, add the following key to the appSettings section of
the web.config, and change the value to a different claim URI.
<add key="IdentityClaimType"
value="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier"/>
To gain more information about the format and consumption of the encrypted XML token, consult the
following:
http://www.identityblog.com/codesample.php?simple-infocard-demo/encrypted_token
To get an idea of what a website might look like with information card support please check out
https://www.fabrikam.com/friends/default.aspx
This is also included in the IE7 favorites.
Reference
Claims in Self Issued Cards
Given Name = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname";
Email Address = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress";
Surname = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname";
Street Address = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/streetaddress";
Locality = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/locality";
State/Province = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/stateorprovince";
Postal Code = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/postalcode";
Country = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country";
Home Phone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/homephone";
Other Phone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/otherphone";
Mobile Phone = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/mobilephone";
Date of Birth = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth";
Gender = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/gender";
PPID = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/privatepersonalidentifier";
Glossary
Identity Provider
The agency (company, website, organization, yourself) that is asserting claims about a Subject (person).
Identity Selector
The Identity Selector is the dialog that presents the user’s different digital identities and allows the
selection of a relevant one for submission to the Relying Party.
Relying Party
The agency (website, server, or other party) that will rely on the claims presented by the subject.
Subject
The entity (the user) that has claims made about their identity. The Subject is always in control in the
release of their digital identity information to the Relying Party.
Additional Reading
Windows CardSpace Community Website
http://cardspace.netfx3.com/
Windows CardSpace Community Sandbox
http://sandbox.netfx3.com/
Introducing InfoCard
http://msdn.microsoft.com/windowsvista/default.aspx?pull=/library/enus/dnlong/html/IntroInfoCard.asp
Windows Vista Developer Center: Cardspace
http://msdn.microsoft.com/windowsvista/building/infocard/
The Identity Blog
http://www.identityblog.com/
The Laws of Identity
http://msdn.microsoft.com/windowsvista/building/infocard/default.aspx?pull=/library/enus/dnwebsrv/html/lawsofidentity.asp
Microsoft's Vision for an Identity Metasystem
http://msdn.microsoft.com/windowsvista/building/infocard/default.aspx?pull=/library/enus/dnwebsrv/html/identitymetasystem.asp
A Guide to Integrating with InfoCard v1.0
http://download.microsoft.com/download/6/c/3/6c3c2ba2-e5f0-4fe3-be7f-c5dcb86af6de/infocardguide-beta2-published.pdf
A Technical Reference for InfoCard v1.0 in Windows
http://download.microsoft.com/download/5/4/0/54091e0b-464c-4961-a934-d47f91b66228/infocardtechref-beta2-published.pdf
Web Services Specifications
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnglobspec/html/wsspecsover.asp
Microsoft Open Specification Promise
http://www.microsoft.com/interop/osp/default.mspx