SQL Server 2005 Fail-over Cluster Upgrade Post Mortem

11:31 AM j. montgomery 9 Comments

I just finished up a SQL Server 2000 to SQL Server 2005 upgrade on a large fail-over production cluster. I ran into a couple of issues that extended the upgrade outside the maintenance window and caused me some serious headaches. The information below may save you some time and sanity before upgrading your SQL Server Fail-Over cluster from 2000 to 2005.

Issue one: The new SQL Server 2005 Password Policy

I'm all for security features - password policies rank up there as an important feature for any product that requires authentication. SQL Server 2005 combined with Windows Server 2003 brought SQL Server into this century as far as SQL Account are concerned (though I prefer using Windows Logins with SSPI when possible but most third party vendors whose software runs on SQL Server don't seem to get this).

One of the worst things about using SQL Accounts in SQL Server 2000 is that SQL account passwords are case insensitive if you're using the default collation (which 99% of you are). That's right, there's nothing like reducing the security of a password by removing an additional 26 combinations from the keyspace.

So back to SQL Server 2005's password policy, which I feel I need to say again, is a welcome addition as it not only resolves the case insensitivity issue with the default collation in SQL 2000, but also integrates Windows 2003 password complexity requirements into SQL Accounts as well (including strong password enforcement by extending passfilt.dll).

My main issue with the new Password Policy is not so much with how it's implemented in SQL Server 2005, but how it applies to the SQL Server 2005 upgrade process. The SQL Server 2005 Setup fails to check the service identity's password strength against the Windows 2003 policy before performing the upgrade thus potentially causing a major issue near the end of the installation.

The million dollar question: What happens if the windows service account that you're using to run SQL Server 2000 doesn't meet the complexity requirements in the NEW and IMPROVED Password Policy in SQL Server 2005 when you do the upgrade?

Answer: The Setup on a high-availability cluster will reach about 85% on one cluster node and 100% on the other cluster node and THEN it checks the service account password to see if it meets complexity requirements.  This wouldn't necessarily be a problem if they gave you the opportunity to change it here, but they don't - the only option appears to be to roll back the entire SQL Server 2005 installation back to SQL 2000...OH...MY...GOD.

I'm too stubborn to just let it rollback - I'm stunned, staring at a modal window that offers two options: "Retry" and "Cancel" - canceling seems like a terrible idea - especially since one of the nodes 100% completed.  I check the status and the setup program - it is in the process of migrating the msdb database with SQL Scripts. I also reviewed the Setup Logs files to see where it was at. All signs seem to point to SQL Server 2005 up and running and executing migration scripts - basically SQL code on the newly upgraded engine. I checked the Services snap-in and the install folders and it looks like everything is in place and running. I even connected to it with SQL Server Management Studio and looked at the version. My Analysis? The SQL Service instance was UP AND RUNNING SQL SERVER 2005 within the cluster! The Setup is basically done! The System tables are the only things that still need to be migrated.

And why does the Setup program want to rollback the entire installation? What is the critical failure that requires such a heavy hand?

My service account password doesn't meet complexity requirements.

That's right - I don't have two upper, two lower, two numbers, and two symbols (or whatever our enterprise requirements are) for the SQL service account password so I just better uninstall the whole darn thing and start over. Genius.

Some poor soul ran into this exact issue here - AND HAD TO ROLLBACK!

"We, unfortunately, had to fall back to SQL 2000 (painful) and then re-do the upgrade."

Source: http://www.msdner.com/dev-archive/149/19-95-1495645.shtm

I REFUSED to hit cancel. I figured the Retry button must be there for a reason. So after pushing it a few times in a hopeless attempt for it to just give in and move on, I did some digging with Google.

Turns out there's a real simple fix: Just add the trace flag -T4606 to the service command arguments on SQL Server 2000 before upgrading and it will disable the password policy check on upgrade and viola, upgrade works just fine OR change your service account password to meet the complexity requirements and then upgrade.  WOW, wish I would have known that!

I realize I might be able to fix this by simply changing the SQL Server 2005 startup parameters, restarting the SQL Service, and then hitting "Retry" on the SQL Server 2005 Setup program and if all went as planned, the setup would move on.

Turns out, this solution WORKS just fine and I would have been able to add the trace flag, cycle the service, click "Retry" and finished the upgrade...except for another small issue...

Issue Two - SQL Server 2005 Configuration Manager Unexpected Behavior

In order to change the startup parameters to SQL Server 2005 to add the additional Trace flag of -T4606, you have do something fairly simple.

The Microsoft Knowledge Base Article describes it like this:
1. Start the SQL Server service by using trace flag 4606.
    a.  Open SQL Server Configuration Manager.
    b.  Click SQL Server 2005 Services, and then double-click SQL Server (InstanceName).
    c.  In the SQL Server (InstanceName) Properties dialog box, click the Advanced tab.
    d.  On the Advanced tab, add the following text at the end of the existing string in the Startup Parameters box: -T4606
    e.  Click OK.

Now I've changed startup parameters on probably hundreds of programs and I expect this sort of thing to work a certain way...and the instructions above fail to mention the MOST CRITICAL part.

You need a semicolon between the previous parameter and the -T4505 parameter. A semicolon? Why would I need that? What happens if you don't put a semicolon between the parameters? Something unexpected if you're not familiar with SQL Server 2005 Configuration Manager.

So I change the startup parameter from this:

...-lc:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\DATA\mastlog.ldf

to this:

...-lc:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\DATA\mastlog.ldf -T4606

That looks fine, doesn't it....I thought so too. But when I took down and started back up the SQL 2005 Service via the Cluster manager, the error when the database service tries to start in ERRORLOG is:

Can't open file "c:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\DATA\mastlog.ldf -T4606"

That's right, it appended -T4606 to the master LDF file name.  Talk about unexpected.

I remove the -T4606 the same way I added it - using the SQL Server Configuration Manager then recycle the SQL service.  But I'm getting the same error. I look in the Configuration Manager again and the -T4606 parameter is back. I remove it again, recycle - but -T4606 shows back up as a part of the filename!

This is maddening. So I go to the registry and find the startup parameters, completely bypassing SQL Server 2005 Configuration Manager. The settings are here:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLServer\Parameters

SQLArg0 = -dc:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\DATA\master.mdf"
SQLArg1 = -ec:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\LOG\ERRORLOG"
SQLArg2 = -lc:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\DATA\mastlog.ldf -T4606"

So I modify SQLArg2 to remove the tailing trace flag and then add:

SQLArg3 = "-T4606"

Then I cycle the service - guess what (and if you know clustering services well, you know exactly what's happening) - the SQLArg2 is back with the -T4606 appended to the LDF filename and the SQLArg3 is GONE.

A little background on Windows Clustering Services

Windows Clustering Services is in charge of SQL Server and its' startup parameters (which live in the registry location listed above), you never start the SQL Service directly on a cluster. The Windows Clustering Service is in charge because in a situation where there's a failure on one node, the other node needs to know when to take over - that's Windows Clustering Service job. It notices the failure and the nodes work out which one should bring up the SQL Server service.

For this to function properly, the SQL Server registry configuration must match between the two servers. Windows Clustering Services stores the 'master' settings for these services in the CPT hive file in the cluster quorum - this is called a checkpoint. When a cluster resource comes online, Windows Clustering Services copies out these particular sections of the registry to each servers so they are consistent.  If a change is made while the resource is online, these changes get check-pointed back into the CPT hive. If a change happens while the cluster resource is offline, then the changes DO NOT get check-pointed into the CPT hive in the cluster quorum. 

So here's what I did wrong:
My 'bad' change (master ldb file with -T4606 appended to it) was check-pointed to the cluster quorum because I did it when the cluster resource, that is SQL Server, was on line. Since the resource couldn't come back online due to the bad change, my attempt to fix the setting was never check-pointed back to the quorum.

Then when I tried to bring the resource back online, the service parameters in the registry were overwritten with the last 'good' (yea right) checkpoint in the quorum which contained the bad setting.

What took me a while to figure out is that the best way to deal with this issue is to delete the Cluster checkpoint for the registry key that handles the Startup Parameters for SQL Server 2005.

Like so:

1. Disable the cluster checkpoint for the specific registry key:
c:\> cluster res "SQL Server" /removecheck: "Software\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLSERVER"

2. Correct the trace flag mistake in the SQL 2005 Configuration Manager OR just edit the registry directly which is the approach I took.

3. Enable the cluster checkpoint for the specific registry key:
c:\> cluster res "SQL Server" /addcheck: "Software\Microsoft\Microsoft SQL Server\MSSQL.1\MSSQLSERVER"

Once the trace flag -T4606 was added in, I brought up the Cluster node, hit "Retry" in the SQL Server 2005 setup and then received another error about losing the connection to the SQL Server. This is expected since I cycled the service. Then I click "Retry" again and the setup process completes successfully.

What a pain!

Next steps : remove the -T4606 flag from the startup parameters and increase the complexity of my service account password.


Microsoft KB Articles
Error message in the SQL Server ERRORLOG file after you upgrade SQL Server 2000 to SQL Server 2005 on a server that is running Windows Server 2003: "Unable to update password policy"

The SQL Server service cannot start when you change a startup parameter for a clustered instance of SQL Server 2000 or of SQL Server 2005 to a value that is not valid

Unable to correct invalid SQL Server Network Configuration on clustered SQL Server causes clustered SQL Server fail to start “permanently”
(Discusses Clustering Services Checkpoint Behavior)


K2 [blackpearl] Launches, new K2.com web site

9:09 PM j. montgomery 0 Comments

With the Launch of K2 [blackpearl] comes the launch of the new K2 Web Site!

The K2 platform has really expanded with this new release - there's a lot of new stuff to learn. The K2 [blackpearl] platform consists of:
K2 [blackpearl]
K2 [blackJax]
K2 [play] (coming soon)
K2 [blackpoint]
K2 [connect]

You can download a PDF of the K2 [blackpearl] platform here.


Geekonomics: The Real Cost of Insecure Software

11:04 PM j. montgomery 0 Comments

David Rice, SANS Instructor and Security Guru has a new book coming out this October called, Geekonomics: The Real Cost of Insecure Software. It's now available on Amazon.com.

I heard a brief introduction on this very topic in David Rice's Security 616: Defensible .NET class at SANS Network Security 2006 in Las Vegas and found it eye opening - I'll be pre-ordering this one.

If you'd like to get a taste of what the book will have to offer, David Rice will be giving a talk on this exact topic at SANS@Night at SANS Network Security 2007 in Las Vegas that I'm really looking forward to.


K2 Underground Launches!

As SourceCode prepares to launch their K2 [blackpearl] enterprise workflow server built on top of .NET 3.0 Windows Workflow Foundation (WFF) - Today they also are launching the next version of their community site K2 Underground of which I have been granted the "Insider" designation. The SourceCode K2 "Insider" label is analogous to Microsoft's MVP status.

I now have another blog over there where I'll be posting K2 specific information. You'll probably see posts here that link over there from time to time.

If you haven't heard of K2.net, check out their new offerings here.


Sample: Smart Card AuthN / AuthZ in ASP.NET

10:28 AM j. montgomery 5 Comments

UPDATE: An updated version is out. Read more about it here.


I have finally posted the C# code samples for the Smart Card HTTP Handler. See Implementing SmartCard Authentication with ASP.NET for the full article. A PDF version is included in the sample download package.

This sample code demonstraites how to implement authentication and authorization in ASP.NET with Smart Card (or Client Certificates) by:

  1. Implementing a custom Smart Card Aware HTTP Authentication Module for .NET

  2. Building an IPrincipal and IIdentity to use with ASP.NET for Smart Card Authenticaiton Http Module

You can download the Sample code in C# here.


PDF version of SmartCard article

12:50 PM j. montgomery 6 Comments

UPDATE: An updated version is out. Read more about it here.


I've posted a PDF version of Implementing Smart Card Authentication and Authorization using ASP.NET.

You can download it here.


Implementing SmartCard Authentication with ASP.NET

9:19 PM j. montgomery 45 Comments

UPDATE: An updated version is out. Read more about it here.


Table of Contents

This is a more complete follow-up article on how to implement Smart Card Authentication in ASP.NET using Http Modules. Sample code in C# is now available here. VB.Net Projects to be posted soon.
You can also download a PDF version of this article here.
I. Introduction
II. Building and Installing the Smart Card HttpModule
III. IIS Configuration
IV. Implementing SmartCardAuthenticationModule
V. SmartCardAuthenticationEventArgs Implementation
VI. SmartCardIdentity Implementation
VII. SmartCardPrincipal Implementation
VIII. Additional Implementation Details
IX. Implementing Authorization in ASP.NET
X. Further Reading


Further Reading on HTTP Modules and IIS Configuration

9:16 PM j. montgomery 0 Comments

Back to Table of Contents: Implementing SmartCard Authentication with ASP.NET

1. http://support.microsoft.com/kb/307985

INFO: ASP.NET HTTP Modules and HTTP Handlers Overview

This article introduces the ASP.NET HTTP modules and HTTP handlers

2. http://msdn.microsoft.com/msdnmag/issues/02/09/HTTPPipelines/

This article introduces the architecture of the pipeline and shows how you can use it to add sophisticated functionality to an ASP.NET-based app.

3. http://msdn.microsoft.com/msdnmag/issues/02/05/asp/default.aspx

A (brief) look at HTTP modules in ASP.NET.

4. http://support.microsoft.com/kb/887289

HTTP module to check for canonicalization issues with ASP.NET

To aid customers in protecting their ASP.NET applications, Microsoft has made available an HTTP module that implements canonicalization best practices.

5. http://msdn2.microsoft.com/en-us/library/aa479332.aspx

Using HTTP Modules and Handlers to Create Pluggable ASP.NET Components

In this article, Scott Mitchell and Atif Aziz show how you can use HTTP modules and handlers to add error logging to your ASP.NET applications. (22 printed pages)

6. http://msdn2.microsoft.com/en-us/library/ms972974.aspx

URL Rewriting in ASP.NET (using HTTP Handlers)

Examines how to perform dynamic URL rewriting with Microsoft ASP.NET. URL rewriting is the process of intercepting an incoming Web request and automatically redirecting it to a different URL. Discusses the various techniques for implementing URL rewriting, and examines real-world scenarios of URL rewriting. (31 printed pages)

7. http://support.microsoft.com/kb/313070

HOW TO: Configure Client Certificate Mappings in Internet Information Services (IIS) 5.0

8. http://support.microsoft.com/kb/272175/EN-US/

HOW TO: Configure Active Directory Certificate Mapping

9. http://support.microsoft.com/kb/216906/EN-US/

Comparing IIS 5.0 Certificate Mapping and Native Windows 2000 Active Directory Certificate Mapping

10. http://www.google.com/microsoft?q=HTTP+modules+&hq=microsoft&btnG=Google+Search

Back to Table of Contents: Implementing SmartCard Authentication with ASP.NET


Implementing Authorization in ASP.NET

9:13 PM j. montgomery 0 Comments

Page 9 of 9

Previous Page: Additional Implementation Details | Next Page: Further Reading

Once you have your SmartCardPrincipal setup, there are several ways to implement authorization with the IPrincipal using Code Access Security (CAS) for authorization within ASP.NET.

We can configure Role base authorization using the web.config file, using PrincipalPermission Demands, or IPrincipal.IsInRole() checks in code.


  • Principal Permissions can be used to decorate methods that will demand upstream callers in the stack have a particular Role.


using System.Security.Permissions;
[PrincipalPermission(SecurityAction.Demand, Role
PrincipalPermission(SecurityAction.Demand, Role
public void DoSomethingImportant()


Imports System.Security.Permissions
<PrincipalPermission(SecurityAction.Demand, Role:="Administrator"), _
PrincipalPermission(SecurityAction.Demand, Role:
="Auditors")> _
Public Sub DoSomethingImportant()
End Sub


  • Principal Permissions can be used to make demands programmatically to upstream callers in the stack have a particular Role.


using System.Security.Permissions;
public void DoSomethingImportant()
PrincipalPermission permCheck
= new PrincipalPermission(Nothing, "Administrators");


Imports System.Security.Permissions
Public Sub DoSomethingImportant()
Dim permCheck As New PrincipalPermission(Nothing, "Administrators")
End Sub

IPrincipal.IsInRole() Check

  • We can check if the IPrincipal is in the role we require (which is exactly what the PrincipalPermission class does by using the IPrincipal stored in the Thread.CurrentPrincipal):


if (myPrincipal.IsInRole("Administrators")


If myPrincipal.IsInRole("Administrators") Then
End If

Web.Config - Specify access permissions to files and/or folders in the web.config

  • To allow all Administrators and deny everyone else to a folder called ‘Admin’, and to allow only Auditors into a folder called ‘Reports’, we’d add the following to the web.config:

<location path="Admin">
<allow roles="Administrator" />
<deny users="*" />
<location path="Reports">
<allow roles="Auditor" />
<deny users="*" />


ASP.NET provides a powerful, yet simple way to implement custom authentication functionality in the HTTP Pipeline using HTTP Modules. IIS also has robust support for Client Certificates and when combined, Http Modules in ASP.NET and IIS make a great platform for developing sites that need to use Smart Cards for authentication and authorization.

Previous Page: Additional Implementation Details | Next Page: Further Reading

Page 9 of 9


Additional Implementation Details

9:10 PM j. montgomery 0 Comments

Page 8 of 9
Previous Page: SmartCardPrincipal Implementation | Next Page: Implementing Authorization in ASP.NET

There are several more classes involved in this implementation, but they go beyond the scope of the HttpModule. If you download the sample code, you can take a look at my particular implementation. They are barely implemented so I’d recommend you use them at your own risk.
The Configuration Class
The Configuration class resolves some things like 401 error page, the database connection string, and the database connection timeout. The class had the following structure:

These values are retrieved from the web.config file:
1 <configuration> 2 <system.web> 3 4 </system.web> 5 <appSettings> 6 <add key="SmartCardAuthentication_UnauthorizedPage" 7 value="C:\WINNT\help\iisHelp\common\401-1.htm"/> 8 <add key="SmartCardAuthentication_ConnectionString" 9 value="integrated security=SSPI;data source=Server;initial catalog=Database"/> 10 <add key="SmartCardAuthentication_ConnectionTimeout" 11 value="180" /> 12 </appSettings> 13 </configuration>

The DataAccess Class

The DataAccess class had some methods for quickly retrieving data from the database.

The CryptoUtility Class
This class performed operations (hash compare, BinToHex() / HexToBin() conversions, etc.) on the X509 certificates. It also generates the Public Key hash used to retrieve users out of the database, and validates some simple properties on the X509 certificate for an internal sanity check. IIS should already be catching these problems before it gets to our code, but it can’t hurt to check again.

Some Final Tests

Once you have the SmartCardAuthenticationModule code up and running, a simple way to test it is as follows:

1 private void Page_Load(object sender, EventArgs e) 2 { 3 SmartCardIdentity smartCardIdentity = (SmartCardIdentity)Me.User.Identity; 4 SmartCardPrincipal smartCardPrincipal = (SmartCardPrincipal)Me.User; 5 6 Response.Write("Name: " + smartCardIdentity.Name + "<br>"); 7 Response.Write("Is Authenticated: " + smartCardIdentity.IsAuthenticated + "<br>"); 8 Response.Write("Authentication Type: " + smartCardIdentity.AuthenticationType + "<br>"); 9 Response.Write("Elevated User: " & SmartCardPrincipal.IsElevatedUser + "<br>");10 Response.Write("Is in role Administrator: " + this.User.IsInRole("Administrator"));11 }12


1 Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) _ 2 Handles MyBase.Load 3 4 Dim smartCardIdentity As SmartCardIdentity = DirectCast(Me.User.Identity, SmartCardIdentity) 5 Dim smartCardPrincipal As SmartCardPrincipal = DirectCast (Me.User, SmartCardPrincipal) 6 7 Response.Write("Name: " & smartCardIdentity.Name & "<br>") 8 Response.Write("Is Authenticated: " & smartCardIdentity.IsAuthenticated & "<br>") 9 Response.Write("Authentication Type: " & smartCardIdentity.AuthenticationType & "<br>")10 Response.Write("Elevated User: " & SmartCardPrincipal.IsElevatedUser & "<br>")11 Response.Write("Is in role Administrator: " & Me.User.IsInRole("Administrator"))12 End Sub 13

Previous Page: SmartCardPrincipal Implementation | Next Page: Implementing Authorization in ASP.NET
Page 8 of 9


SmartCardPrincipal Implementation

9:08 PM j. montgomery 1 Comments

Page 7 of 9
Previous Page: SmartCardIdentity Implementation | Next Page: Additional Implementation Details

The SmartCardPrincipal Class – Inherits from System.Security.Principal.IPrincipal
From the MSDN documentation, a principal object represents the security context of the user on whose behalf the code is running, including that user's identity (IIdentity) and any roles to which they belong. The Principal is the object that gets interrogated when a PrincipalPermission Demand is made to make sure it is in the proper role to perform the requested operation.
When SmartCardPrincipal.IsInRole() is called, our Smart Card aware object will return whether or not the Principal is in the requested role. On first run, this class will populate a Hashtable with the roles for the current user from the database. The key to lookup the Users’ Roles in the database will be a SHA256 hash of the Users’ public key.
Also notice that both IsElevatedUser and IsInRole() is virtual/Overridable – This allows for custom role resolution if you have a different methodology to resolve roles other then the database role resolution provided.

1 using System;
2 using System.Configuration;
3 using System.Web;
4 using System.Web.Security;
5 using System.Collections;
6 using System.Security.Principal;
7 8 namespace SmartCardAuthentication
9 {
10 public class SmartCardPrincipal : IPrincipal
11 {
12 private SmartCardIdentity _identity;
13 private Hashtable _roles;
14 private bool _isElevatedUser;
15 private bool _rolesLoaded;
16 private bool _isElevatedLoaded;
17 18 public IIdentity Identity
19 {
20 get
21 {
22 return this._identity;
23 }
24 }
25 26 public SmartCardPrincipal(SmartCardIdentity identity)
27 {
28 this._identity = identity; 
29 this._rolesLoaded = false;
30 this._isElevatedLoaded = false;
31 }
34 public virtual bool IsElevatedUser
35 {
36 get {
37 if (!_isElevatedLoaded)
38 {
39 lock (this)
40 {
41 // Evaluate the user against the database
42 // to see if they have an elevated account 43 DataAccess dAccess = new DataAccess();
44 this._isElevatedUser =
45 dAccess.AuthenticateUser(_identity.PublicKeyHash);
46 }
47 this._isElevatedLoaded = true;
48 }
49 return this._isElevatedUser;
50 }
51 }
52 53 public virtual bool IsInRole(string role)
54 {
55 if (!_rolesLoaded)
56 {
57 lock (this)
58 {
59 if (this.IsElevatedUser)
60 {
61 DataAccess dAccess = new DataAccess();
62 _roles = dAccess.GetPrincipalRoles(_identity.PublicKeyHash);
63 _roles.Add("User", "User");
64 }
65 else
66 {
67 _roles = new Hashtable(1);
68 _roles.Add("User", "User");
69 }
70 _rolesLoaded = true;
71 }
72 }
73 return _roles.Contains(role);
74 }
75 }
76 }

1 Imports System.Configuration
2 Imports System.Web
3 Imports System.Web.Security
4 Imports System.Collections
5 Imports System.Security.Principal
6 7 Public Class SmartCardPrincipal
8 Implements IPrincipal
9 10 Private _identity As SmartCardIdentity
11 Private _roles As Hashtable
12 Private _rolesLoaded As Boolean 13 Private _isElevatedLoaded As Boolean 14 15 Public ReadOnly Property Identity() As IIdentity _
16 Implements IPrincipal.Identity
17 Get 18 Return Me._identity
19 End Get 20 End Property 21 22 Public Sub New( _
23 ByVal identity As SmartCardIdentity _
24 )
25 Me._identity = identity
26 Me._rolesLoaded = False 27 Me._isElevatedLoaded = False 28 End Sub 29 30 Public Overridable ReadOnly Property IsElevatedUser() As Boolean 31 Get 32 If (Not _isElevatedLoaded) Then 33 SyncLock (Me)
34 ' Authenticate the user against the database 35 Dim dAccess As New DataAccess
36 _isElevatedUser = dAccess.AuthenticateUser(Me._identity.PublicKeyHash)
37 38 _isElevatedLoaded = True 39 End SyncLock 40 End If 41 42 Return _isElevatedUser43 End Get 44 End Property 45 46 Public Overridable Function IsInRole(ByVal role As String) As Boolean _
47 Implements IPrincipal.IsInRole
48 If (Not _rolesLoaded) Then 49 SyncLock (Me) 50 If _identity.IsElevatedUser Then 51 ' If the user is elevated, retrieve roles from the database 52 Dim dAccess As New DataAccess
53 _roles = dAccess.GetPrincipalRoles(_identity.PublicKeyHash)
54 ' Finally add the Default Role 55 _roles.Add("User", "User")
56 Else 57 _roles = New Hashtable(1)
58 ' Regular user 59 _roles.Add("User", "User")
60 End If 61 _rolesLoaded = True 62 End SyncLock 63 End If 64 Return _roles.Contains(role)
65 End Function 66 End Class

Previous Page: SmartCardIdentity Implementation | Next Page: Additional Implementation Details

Page 7 of 9


SmartCardIdentity Implementation

9:04 PM j. montgomery 0 Comments

Page 6 of 9

Previous Page: SmartCardAuthenticationEventArgs| Next Page: SmartCardPrincipal Implementation

SmartCardIdentity Class – Inherits from System.Security.Principal.IIdentity

The IIdentity Interface defines the basic functionality of an identity and is used to encapsulate information about the user or entity being validated (MSDN Documentation).



1 using System;
2 using System.Web;
3 using System.Security.Cryptography.X509Certificates;
4 using System.Security.Principal;
6 namespace SmartCardAuthentication
7 {
8 public class SmartCardIdentity : IIdentity
9 {
10 private string _subject;
11 private string _email;
12 private string _publicKeyHash;
13 private X509Certificate _certificate;
15 public string AuthenticationType
16 {
17 get { return "SmartCard"; }
18 }
20 public bool IsAuthenticated
21 {
22 get { return true; }
23 }
25 public string Name
26 {
27 get { return _subject; }
28 }
30 public string PublicKeyHash
31 {
32 get { return _publicKeyHash; }
33 }
35 public string EmailAddress
36 {
37 get { return this._email; }
38 set { this._email = value; }
39 }
40 public X509Certificate Certificate
41 {
42 get { return this._certificate; }
43 }
47 public DateTime ExpirationDate
48 {
49 get
50 {
51 return DateTime.Parse(
52 this._certificate.GetExpirationDateString()
53 );
54 }
55 }
57 public string Subject
58 {
59 get { return this._subject; }
60 }
62 public SmartCardIdentity(HttpClientCertificate certificate)
63 {
64 this._certificate = new X509Certificate(certificate.Certificate);
65 this._publicKeyHash = CryptoUtility.GetPublicKeyHash(certificate);
66 this._subject = certificate.Subject;
67 }
69 public SmartCardIdentity(X509Certificate certificate)
70 {
71 this._certificate = certificate;
72 this._publicKeyHash = CryptoUtility.GetPublicKeyHash(certificate);
73 this._subject = certificate.GetName();
74 }
75 }
76 }


1 Imports System.Web
2 Imports System.Security.Cryptography.X509Certificates
3 Imports System.Security.Principal
5 Public Class SmartCardIdentity
6 Implements IIdentity
8 Private _subject As String
9 Private _email As String
10 Private _publicKeyHash As String
11 Private _certificate As X509Certificate
13 Public ReadOnly Property PublicKeyHash() As String
14 Get
15 Return _publicKeyHash
16 End Get
17 End Property
19 Public ReadOnly Property AuthenticationType() As String _
20 Implements IIdentity.AuthenticationType
21 Get
22 Return "SmartCard"
23 End Get
24 End Property
26 Public ReadOnly Property IsAuthenticated() As Boolean _
27 Implements IIdentity.IsAuthenticated
28 Get
29 ' The fact that this identity object exists means that
30 ' the user was Authenticated
31 Return True
32 End Get
33 End Property
35 Public ReadOnly Property Name() As String _
36 Implements IIdentity.Name
37 Get
38 Return _subject
39 End Get
40 End Property
42 Public Property EmailAddress() As String
43 Get
44 Return Me._email
45 End Get
46 Set(ByVal Value As String)
47 Me._email = Value
48 End Set
49 End Property
51 Public ReadOnly Property Certificate() As X509Certificate
52 Get
53 Return Me._certificate
54 End Get
55 End Property
57 Public ReadOnly Property ExpirationDate() As DateTime
58 Get
59 Return DateTime.Parse(Me._certificate.GetExpirationDateString())
60 End Get
61 End Property
63 Public ReadOnly Property Subject() As String
64 Get
65 Return Me._subject
66 End Get
67 End Property
69 Public Sub New(ByVal certificate As HttpClientCertificate)
70 Me._certificate = New X509Certificate(certificate.Certificate)
71 Me._publicKeyHash = CryptoUtility.GetPublicKeyHash(certificate)
72 Me._subject = certificate.Subject
73 End Sub
75 Public Sub New(ByVal certificate As X509Certificate)
76 Me._certificate = certificate
77 Me._publicKeyHash = CryptoUtility.GetPublicKeyHash(certificate)
78 Me._subject = certificate.GetName()
79 End Sub
81 End Class

Previous Page: SmartCardAuthenticationEventArgs| Next Page: SmartCardPrincipal Implementation

Page 6 of 9