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