DPAPI
I wrote this documentation and project years ago and found it while looking through some of my archives last night. It probably still has some millage left and might be found useful to someone setting up DPAPI. You can download the binaries or source code below.
DPAPI Setup
1. Create the directory C:\DPAPI and copy the contents from the DPAPI directory (found in DPAPIBinaries.zip) into the new directory.
2. Create a local Windows account that will be used to run the Enterprise Services Application and Windows Service with the username DPAPIAccount. Make sure to uncheck the User must change password at next logon check box and check the Password never expires check box. Use the Local Security Policy tool in the Administrative Tools programs group to give the account the Log on locally and Log on as a batch job privileges.
You must log off with your current account and login as the DPAPIAccount user to create the user profile. Once you have logged in as the DPAPIAccount user you can then log off and log back in as your normal account.
3. Open a command prompt and run the following command to register the serviced component.
regsvcs C:\DPAPI\DPAPIComp.dll
4. Open the Component Services management console and navigate to the Component Services->Computers->My Computer->COM+ Applications folder. Right-click on the DPAPI Helper Application application and select the Properties menu option.
6. Click on the Identity tab and select the This user radio button. Enter the MachineName\DPAPIAccount username and password and then click on the OK button.
7. Expand the DPAPI Helper Application->Roles folder. You will need to add the users to the Roles\Users nodes that will need access to encrypt and decrypt. All users that need access to encrypt and/or decrypt must also be added to the Marshaler\Users node.
Add the MachineName\ASPNET ("NETWORK SERVICE" in IIS 6.0) user to all three groups for this example since we will test encrypting and decrypting through an ASP.NET application.
8. Open a command prompt and run the following command to install the Windows service.
installutil C:\DPAPI\DPAPIService.exe
Enter the MachineName\DPAPIAccount ("NETWORK SERVICE" in IIS 6.0) username and password and click the "OK" button.
9. Open the Services management console and start the DPAPI Service service.
10. Open the IIS management console and add a new virtual directory named DPAPIWeb.
11. Copy the contents from the DPAPIWeb directory (found in DPAPIBinaries.zip) into the new virtual directory.
12. Open http://localhost/DPAPIWeb/WebForm1.aspx in a browser and test encrypting and decrypting. Obviously, you would want to make sure that this application was not accessible outside of the local server.
XmlSiteMapProvider – Query String Support
Most of the ASP.NET applications that I create have some type of a menu and the menu is typically dynamic. When I say dynamic I mean that the menu items are usually dependent on the user's current location in the application and sometimes require query strings (i.e. the Id of a photo album). The default XmlSiteMapProvider is nice and can be very useful coupled with the securityTrimmingEnabled attribute (the provider model is very awesome...thank you Rob Howard). However, the default XmlSiteMapProvider does not offer any support for query strings. I wanted a solution that would allow me to use the existing .sitemap format, automatically adding query strings to menu items, hide menu items if those query strings were not available, and did not require creating any custom controls. I first looked at the SiteMapProvider.SiteMapResolveEventHandler event, but this option would only work if I was using the SiteMapPath control. I then decided to see what was overridable in XmlSiteMapProvider since that provider did exactly what I wanted with the exception of handling query strings. I came up with the class below that handles all of my requirements and simply added the new provider to the SiteMap providers in the web.config. You can download a small sample application here.
using System;
using System.Web;
using System.Collections.Specialized;
public class XmlQueryStringSiteMapProvider : XmlSiteMapProvider
{
/// Gets the root node of the site map.
public override SiteMapNode RootNode
{
get
{
// Get the root node
SiteMapNode root = base.RootNode;
// Make sure we have a valid root node object
if (root == null)
return null;
// Clone the root node
SiteMapNode tempNode = root.Clone();
// Check to see if this node requires a query string
string qs = GetQueryString(tempNode);
if (!string.IsNullOrEmpty(qs))
tempNode.Url += qs;
return tempNode;
}
}
/// Retrieves the child site map nodes of a specific SiteMapNode object.
public override SiteMapNodeCollection GetChildNodes(SiteMapNode node)
{
// Make sure we have a valid node
if (node == null)
throw new ArgumentNullException("node");
// Build the site map
this.BuildSiteMap();
// Get a collection of all child nodes
SiteMapNodeCollection collection = base.GetChildNodes(node);
// Make sure we have a vlid collection of child nodes
if (collection == null)
return new SiteMapNodeCollection();
// Create a new collectino that will hold the final nodes
SiteMapNodeCollection nodes = new SiteMapNodeCollection(collection.Count);
foreach (SiteMapNode n in collection)
{
// Clone the current node
SiteMapNode tempNode = n.Clone();
// Make sure this node is accessible
if (base.SecurityTrimmingEnabled && !n.IsAccessibleToUser(HttpContext.Current))
continue;
// Check to see if this node requires a query string
string qs = GetQueryString(tempNode);
if (!string.IsNullOrEmpty(qs))
tempNode.Url += qs;
else if (n["required"] == "true" && !string.IsNullOrEmpty(n["queryString"]))
continue; // DO not add this node to the collection
nodes.Add(tempNode);
}
return SiteMapNodeCollection.ReadOnly(nodes);
}
/// Creates a query string based on the node settings.
private string GetQueryString(SiteMapNode node)
{
// Check to see if this node requires query string values
if (node["queryString"] == null)
return null;
// Get a list of the query string names
NameValueCollection values = new NameValueCollection();
string[] vars = node["queryString"].Split(",".ToCharArray());
// Check to see if we have values for the query string names
foreach (string s in vars)
{
string var = s.Trim();
// Check to see if the query string value exists
if (HttpContext.Current.Request.QueryString[var] == null)
continue;
// Add the query string value to the collection
values.Add(var, HttpContext.Current.Request.QueryString[var]);
}
if (values.Count == 0)
return null;
// Build the query string
string[] parts = new string[values.Count];
for (int i = 0; i < values.AllKeys.Length; i++)
parts[i] = values.AllKeys[i] + "=" + values[values.AllKeys[i]];
return "?" + String.Join("&", parts);
}
}
FIRE IN THE HOLE!!! – Busyness As Usual
Work has been extremely busy over the past few months as we are trying to launch multiple new products simultaneously. I have been using ASP.NET 2.0 and have really enjoyed all of the new features introduced since version 1.1. The Microsoft team has created a very flexible and powerful platform. Over the next few weeks, I look forward to working with Team Foundation Server as I find time. I will also try to blog more regularly and post programming related articles.
New Blog Application
I finished the basic version of my new blog system this evening with the addition of the comment system. My blog application is not as feature rich as .Text at the moment, but it has been a fun project. Over the next week, I am going to update the Articles and Examples sections with new content.
The weekend is at hand! We watched both of The Incredibles DVDs last weekend. I always enjoy watching the "behind the scenes" and seeing what the guys at Pixar go through making their great movies. Their movies would not be possible without their awesome software developers (their artists and animators are ok too
!

