Three Is It

Because two isn't enough and four is just too many

Behind the ostensible government sits enthroned an invisible government owing no allegiance and acknowledging no responsibility to the people.
President Theodore Roosevelt
Home Blogs Genealogy Brad's Bookshelf Subscriptions Contact Sign in
 

About the author

Brad Butts is a .NET developer and architect. He is married with children and enjoys reading, working out, and genealogy is his five minutes of spare time.
E-mail me Send mail
National Debt Clock

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2010

Key/Value Pair Configuration Section

Here’s another suggestion for Microsoft: give me a nice key/value pair configuration section.

Now, you might say, don’t you already have appSettings?  The appSettings configuration section is certainly handy, but occasionally, I’ll have to store sensitive data in my configuration file—data required to be encrypted.  I’ve made a few attempts to encrypt the appSettings section to no avail, although I’m not sure why it won’t encrypt—the new System.Configuration.AppSettingsSection v2.0 implements System.Configuration.ConfigurationSection, which tends to play nicely with the configuration encryption infrastructure…so either I’m not doing something right or I’m missing something.

You might say, fooey on appSettings, why don’t you put your secret in the connectionStrings section and encrypt that?  I could do that (and I know it does play nicely with configuration encryption), but what if I have sensitive data I need to store that’s not a connection string—like a plain old password?  That would look kind of silly in a connectionStrings section:

<connectionStrings>
  <add name="myPassword" connectionString="bob sent me"/>
</connectionStrings>

In code:

string myPassword = ConfigurationManager.
                ConnectionStrings["myPassword"].ConnectionString;

 

I just don’t think that reads very well.

No, what I really want is a third option: a good ‘ol Key/Value pair configuration section that will encrypt/decrypt consistently.  Well, interestingly, two-thirds of the work to make such a section available is already done in the .NET framework. 

Most custom configuration sections require that you implement three abstract classes: ConfigurationSection, ConfigurationElementCollection, and ConfigurationElement.  As it turns out, the .NET framework already contains a public System.Configuration.KeyValueConfigurationCollection which manages a collection of System.Configuration.KeyValueConfigurationElement objects.  The only thing left to do is to expose this collection to configuration via an implementation of ConfigurationSection.  It’s as simple as this:

using System.Configuration;

namespace Brad.Configuration
{
    public class KeyValueSection : ConfigurationSection
    {
        [ConfigurationProperty("", IsRequired = true, IsDefaultCollection = true)]
        public KeyValueConfigurationCollection Settings
        {
            get
            {
                return (KeyValueConfigurationCollection)this[""];
            }
            set
            {
                this[""] = value;
            }
        }
    }
}
It’d be nice if such a class were already available in the framework instead of me having to code the last piece.  Maybe some nice Microsoft employee can add this class in a future release.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Tuesday, September 15, 2009 11:38 PM
Permalink | Comments (1) | Post RSSRSS comment feed

Setting Enums from the Config

This has been one of several nagging curiosities in the back of my mind for quite some time, but until recently, I never had much motivation to explore the question…

Recently, I was given the assignment to deploy some .NET code that would activate an auditing feature with a variety of enumerated flags.  I wanted to make sure I provided enough flexibility in my component such that any combination of the flags could be set from outside the component without the need to recompile the component—the easy answer was to provide a means to set these flags from configuration.

I know I’ve seen other components do this—particularly different logging components—so there ought to be an easy way to set an enumeration from XML, but what is it?  Well, here’s the short-and-dirty solution:

Imagine this kind of configuration file:

<configuration>
  <appSettings>
    <add key="MyEnumSettings" value="OptionOne,OptionTwo"/>
  </appSettings>
</configuration>

Here’s code you can use to parse and convert string representations of an enumerator into the actual enumerator type:

public enum SomeEnumFlag
{
    None,
    OptionOne,
    OptionTwo,
    OptionThree,
    MoreOptions
}

class Program
{
    static void Main(string[] args)
    {
        string enumSettings = ConfigurationManager.AppSettings["MyEnumSettings"];
        string[] settingsArray = enumSettings.Split(new[] { ',' });

        SomeEnumFlag myEnumSettings = new SomeEnumFlag();

        foreach (string setting in settingsArray)
        {
            try
            {
                SomeEnumFlag settingFromConfig =
                    (SomeEnumFlag)Enum.Parse(typeof(SomeEnumFlag), setting.Trim(), true);
                //use the bitwise operator to concatenate enum values together
                myEnumSettings |= settingFromConfig;
            }
            catch (ArgumentException ex)
            {
                //if my config feeds a bad enum value, ArgumentException will be thrown
            }
        }

        if (myEnumSettings == SomeEnumFlag.None)
        {
            //my enum defaults to None and will remain so if no values were
            //found in the config
        } 
    }
}

 

Side note: my real-life problem dealt with a SharePoint enumerator, but, since I have yet to set up a SharePoint development environment at home, I decided to create my own enumerator for this example.  The concept is still the same, though.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Sunday, March 15, 2009 8:51 PM
Permalink | Comments (2) | Post RSSRSS comment feed

Thinking about Logging

I think about logging pretty often as I code solutions.  To me, logging is an important part of a quality software product.  In the middle of the night, when your software goes haywire, being able to explore a well-detailed application log file (not just the event logs or IIS logs, for example) can be critical in remediating the problem and reducing the number of lost hours of sleep.

I think a good logging solution should include:

  1. The ability to be turned off or on from outside the compiled solution (eg. from the configuration file).
  2. Persistence ignorance—maybe today I want to log to a flat file, but tomorrow I want to log to a database.  I should be able to easily swap persistence providers without recompilation.  With regard to flat file logging, I’d like the option to be able to specify a relative path or absolute path for the log file (although, for web apps, that could serve as a security vulnerability).  Also, a rolling log file provider is a must.
  3. The ability to crank up or crank down the verbosity of the logging solution. 
  4. Some control over the look of the log file.  Although I might, on occasion, crack open a log file in notepad and manually walk through it, most log files I deal with are quite large—I’d rather use tools like LogParser to produce human-readable reports from the generally-not-human-readable log file.  By having some control over the data that’s added to my log files and how that data is delimited, it can be easier for me to write scripts that render powerful reports (some day, I do hope to put up a post on how I’ve used LogParser to produce meaningful reports based on my application logs).

I could also add to the list that the solution must be hidden behind an interface for easy injection via Dependency Injection.  The easy winner here, in my opinion, is log4net.  If you’re not familiar with log4net, it is a light, open-source API that meets all of these needs and more.  Here are my quick notes on implementing the library:

Step 1: Configure it

…in your application configuration file

<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
</configSections>
<log4net>
<!-- A1 is set to be a ConsoleAppender -->
<appender name="A1" type="log4net.Appender.RollingFileAppender">
<file value="logs\log4net.log"/>
<appendToFile value="true"/>
<datePattern value="yyyyMMdd"/>
<rollingStyle value="Date"/>
<!-- A1 uses PatternLayout -->
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-4timestamp|%thread|%-5level|%logger|%ndc|%message%newline"/>
</layout>
</appender>
<!-- Set root logger level to DEBUG and its only appender to A1 -->
<root>
<level value="ALL"/>
<appender-ref ref="A1"/>
</root>
</log4net>

…and in your AssemblyInfo.cs file

[assembly: log4net.Config.XmlConfigurator(Watch = true)]

Step 2: Add a reference to log4net.dll

Step 3: Code Away

using System;
using System.Reflection;
using log4net;
namespace WebApplication1
{
public partial class log4net : System.Web.UI.Page
{
private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected void logButton_Click(object sender, EventArgs e)
{
log.Debug("some Debug msg");
log.Error("some Error msg");
log.Fatal("some Fatal msg");
log.Info("some Info msg");
log.Warn("some Warn msg");
}
}
}

 

So, log4net’s great for your everyday application needs, but I have a new requirement: most of what I do anymore is write libraries and frameworks for others to use.  I want to litter my APIs with lots of log messages so that, should a developer want to know what my API is doing, he should just be able to flip a configuration switch and get a log of the activities of my API.  It seems a little presumptuous of me to introduce yet one more dependency on my customer—the log4net dependency—just to be able to use my API.  In this situation, I’d prefer to be able to leverage any kind of logging opportunities in the base class library itself.  The System.Diagnostics namespace gives me some of this opportunity, but none of the native options are as rich or powerful as log4net.

System.Diagnostics.TraceSource

I find the newer System.Diagnostics.TraceSource not even a viable option for me.  TraceSource seems nothing more than the older System.Diagnostics.Trace with less options (eg. no throttling).  So, let’s skip TraceSource and talk about Trace.

 

System.Diagnostics.Trace

It used to be that I would configure a TraceListener like this:

    <system.diagnostics>
<switches>
<add name="MagicTraceSwitch" value="Verbose"/>
</switches>
        <trace autoflush="true" indentsize="4">
<listeners>
<clear/>
<add name="fileListener" 
type="System.Diagnostics.TextWriterTraceListener" 
initializeData="logs\SysDiag.log" 
traceOutputOptions="DateTime"/>
            </listeners>
</trace>
    </system.diagnostics>

Then I’d start coding against the namespace as such:

public partial class _Default : System.Web.UI.Page
{
private TraceSwitch MyTraceSwitch = new TraceSwitch("MagicTraceSwitch", "switch from config file");
protected void Button1_Click(object sender, EventArgs e)
{
Trace.WriteLineIf(MyTraceSwitch.TraceInfo, "log some informational message.");
Trace.WriteLineIf(MyTraceSwitch.TraceVerbose, "log some verbose message.");
}
}

Since System.Diagnostics is part of the BCL, no extra dependencies are required and I can ship just my APIs with no other third party assemblies.  I have at least two problems with this approach, though: 1) the TextWriterTraceListener has no rolling log file capabilities and 2) the way I’m pushing messages out to the listener, via the Trace.WriteLineIf statement, means that only the exact string message in my argument gets inserted into my log—no nicely delimited line with timestamp, thread id, etc.

To address my second problem, I could use a different set of methods in the Trace class:

Trace.TraceError("an error happened.");
Trace.TraceInformation("this is an information trace {0} {1}", "parm1", "parm2");
Trace.TraceWarning("this is a warning trace {0} {1}", "parm1", "parm2");

Interestingly, though, these methods don’t throttle—they pay no attention to my TraceSwitch setting.  All these messages will get logged regardless of the verbosity setting.  The log entries will, however, include the values “Error”, “Warning”, and “Information” if that’s any consolation.  To format the log entry, the listener configuration includes a “Delimiter” attribute and a traceOutputOptions attribute.

To address the first problem, I’ve recently discovered the Microsoft.VisualBasic.Logging.FileLogTraceListener, thanks to this post on StackOverflow.  Here’s a configuration I’ve used for this listener:

    <system.diagnostics>
<switches>
<add name="MagicTraceSwitch" value="Warning"/>
</switches>
        <trace autoflush="true" indentsize="4">
<listeners>
<clear/>
<!--<add name="fileListener" 
type="System.Diagnostics.TextWriterTraceListener" 
initializeData="logs\SysDiag.log" 
traceOutputOptions="DateTime"/>-->
<add 
name="fileListener2" 
type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" 
CustomLocation="C:\data_files\app_development\LoggingTest\WebApplication1\logs"  
Location="Custom" 
BaseFileName="MyApp" 
LogFileCreationSchedule="Daily" 
Delimiter="|" 
traceOutputOptions="ProcessId, DateTime"/>
</listeners>
</trace>
    </system.diagnostics>

One thing to note about this listener is that, unlike the TextWriterTraceListener, the FileLogTraceListener seems to only accept an absolute file path, not a relative one—if you can deal with that, this listener seems to be a viable option—although if I were writing an end-to-end application, not just a framework, I’d definitely go with log4net for my logging solution.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Saturday, February 21, 2009 3:15 PM
Permalink | Comments (1) | Post RSSRSS comment feed

aspnet_merge.exe exited with code 1

Pretty descriptive error code, eh?  Well, I was going to put together an example of what aspnet_merge.exe can do, so the first thing I wanted to do was grab an example application, particularly one that makes use app_code files.  I decided to use the code from BlogEngine.NET.

Yes...I know, I know...the BlogEngine.NET code was never designed to live under a WAP-type model, but surely it can live under that model, right?  Well, uh, not without difficulty.

Step 1: Run aspnet_merge and see what happens
So, I used the Visual Studio 2005 Web Deployment Projects to construct a WDPROJ--an MSBuild project with sweet tasks to run the aspnet_merge utility.  (Note: as far as I can tell, BlogEngine.NET is still a Visual Studio 2005 solution)

At the command line, I typed: msbuild BlogEngine.web_deploy.wdproj



The build started and 54 seconds later...blam!  error MSB6006: "aspnet_merge.exe" exited with code 1.

So, what happened?  Sounds like this error happens when class names collide.  Like so many things, the Web Site project tends to let you get away with bad coding practices like using the same class name for a page's code behind in multiple places--so long as the pages are in separate folders.  Aspnet_compiler will compile separate DLLs so there's never the chance that a single assembly will contain two classes of the same name; however, when aspnet_merge tries to merge all the assemblies into one, the class names will clashes and cryptic errors like "error code 1" will get thrown.

Step 2: Figure out where my problems are and fix them
Unfortunately, the default console logging behavior of MSBuild does not tell me where my errors are, so, after consulting with my handy-dandy MSBuild command line reference, I figure I can do something like this to get more detail on where my errors are occuring:

msbuild /nologo /v:diag BlogEngine.web_deploy.wdproj > msbuild.log

Scrolling up from the bottom of my log, I see this error:

An error occurred when merging assemblies: ILMerge.Merge: ERROR!!: Duplicate type 'widgets_LinkList_edit' found in assembly 'App_Web_nbdnprem'.

Ah-ha!  Multiple classes each named widgets_LinkList_edit.  A quick search for that name reveals that, yes, that class name is used for both the edit.ascx code behind of the LinkList and TextBox widgets.  To fix, I'll go to the TextBox's edit.ascx.cs file and change that class name to widgets_TextBox_edit and to the page declaration in the associated ASCX page and update that, as well.

1...2...3...4 error code 1 issues later and it looks like I have a build!  But do I have a good build?

Step 3: Fire up my BE build to see if it works
Seems like the easiest thing to do is to fire up an instance of the ASP.NET Development Server to host my BE build, so I execute this at the command line:

WebDev.WebServer.EXE /port:8080 /path:"D:\MyTests\BE_Test1\BlogEngine.Web_deploy\Debug_test1" /vpath:"/BE_Test1"

And I get...a Parse Error:



It looks like the page is barfing when it tries to load the PostCalendar user control.  Interestingly, the PostList user control loads fine.  I wonder why?

Looking at the default.aspx markup code, I see that the PostList user control is registered at the top of the page, but the Calendar user control is not.  What gives?



Well, it looks like, as Phil Haack once explained, the controls with the "blog" prefix are registered in the web.config.  Didn't know you could do that.  Pretty cool.

<pages enableSessionState="false" enableViewStateMac="true" enableEventValidation="true">
<controls>
<add namespace="Controls" tagPrefix="blog"/>
</controls>
</pages>


This doesn't fix my problem, though.  ASP.NET Parse Errors are usually an indication that the CLR can't find the referenced class in the assembly.  Well, I know the PostCalendar class is in my single assembly (.NET Reflector told me that), so maybe the CLR is not able to find the assembly itself.  Maybe I can help.  Let's modify the control registration in the web.config with the assembly attribute:

<pages enableSessionState="false" enableViewStateMac="true" enableEventValidation="true">
<controls>
<add namespace="Controls" tagPrefix="blog" assembly="BlogEngine.Web_deploy"/>
</controls>
</pages>


Hey, what do you know?  It worked!  Now, the page loads up just fine.  Note that this last change I made in the web.config in my build folder; if you try to change the original web.config and then run BE from the IDE, you'll get a compiler error.  It seems to me that these kinds of issues can introduce a lot of challenges to development, as you try to account for the fact that the web.config you deploy to a Production server will need to look slightly different than the web.config that you run in your IDE.  I wonder if there are any MSBuild tasks designed to modify the web.config spit out by the build engine?  Scott Guthrie has talked about other important changes that should occur in your web.config before deploying it to Production.  I wonder if anyone has written a MSBuild task to accommodate those changes?

Clicking around in my pre-compiled BE instance, everything seems to work fine; however, I did encounter one show-stopping error that prevents me from doing important administration tasks--like adding a new entry.  At least I know that issue's related to the BE distribution I downloaded, not my build experiment with aspnet_merge.

So, what have I learned?  Doing pre-compilation on complicated Web Site projects can be tricky business--particularly when using aspnet_merge.  I wonder if I should just demo how to migrate a Web Site project to a Web Application project?

 

 

Currently rated 2.0 by 1 people

  • Currently 2/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Friday, October 24, 2008 1:48 PM
Permalink | Comments (74) | Post RSSRSS comment feed

Deploying to different environments

My company maintains different environments--Dev, IT, UAT, Production, and Disaster Recovery--in which to run and test software solutions: particularly web solutions.  My company also (I think rightly so) places restrictions on what environments developers can access and which ones they can't--the old Segregation of Duties principle.  So, the question is, how do developers build applications mallable enough to run under different environments that can be deployed as simply as possible so as to not confuse the server admins and deployment personnel?

Our deployment vehicle of choice is the old Microsoft Installer file (MSI).  With regard to environment-sensitive settings, we simply "extend" applicable sections of the web.config to a second environ.config file.  There are always edge cases, but this approach seems to work out pretty well on the whole.  The following is an example...

Step 1: Abstract whatever environment settings to a separate file.  In this example, I use the appSettings section to house my configuration values, but this pattern works for most other sections I've tried (eg. connectionStrings)

In my web.config


<appSettings file="environ.config">
<add key="key1" value="someOtherValue"/>
</appSettings>


In my environ.config
<appSettings>
<add key="MyEnviron" value="Development"/>
</appSettings>
 

Interestingly, the appSettings node seems to be the only node that supports the "file" attribute.  All other configuration sections I've extended use the attribute "configSource".  I noticed the other day, though, that appSettings also seems to support the "configSource" attribute.  It looks like you need to be careful about your use of that attribute, though (see the Note in the description of the "file" attribute).

Step 2: Create other environment configuration files for the different environments you need to address.  In my example, I've created a IT.environ.config, UAT.environ.config, and Prod.environ.config.  Each configuration should look the same--only the values should change.


Step 3: Test your app to make sure it works in your "Development" environment with your default environ.config file.

Step 4: Now, add a web setup project to your solution to package/deploy your solution.  For simplicity, I've used Scott Guthrie's Web Application Project so I can easily add the Primary Output and Content Files to my web setup project.  On the Content Files output, make sure to add the ExcludeFilter: *environ.config.  This will exclude our environment specific config files--we'll add them in later.

 

Step 5: At some point, someone has to know/decide what environment your web app is being deployed into.  I contend that that decision should be made at the point of deployment.  That decision can be fairly easily automated (and I'll probably talk about that in a future post), but for now, we'll place that decision in the hands of the human installing your MSI on a given server.  

  1. In Solution Explorer, right-click on your web setup project and select View > User Interface from the context menu.
  2. In the User Interface tab, right-click on the Start node and select Add Dialog from the context menu.
  3. In the Add Dialog dialog, select the RadioButtons (4 Buttons) choice and click OK
  4. Move your new dialog to the appropriate order (usually after the Welcome dialog)
  5. Set the properties of the dialog more or less as follows:


 

Step 6: Go back to the File System tab.  Under the Web Application Folder, one by one, add each of your environment config files.  For each file you will need to set two properties:

  1. On the Condition property, set and equation like this: ENVIRON="Dev"
  2. On the TargetName property, set the value to "environ.config"

Obviously, your Condition equation will change for each file.  For the Prod.environ.config file, the Condition property will be ENVIRON="Prod".

 

Step 7: Finally, build your MSI and test it out.  When you choose, say, the UAT environment, make sure that your UAT config file gets deployed and renamed appropriately.  Attached is a sample app for your perusal.
 

 


MSIEnviron.zip (568.92 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Posted by Brad on Friday, September 26, 2008 7:51 PM
Permalink | Comments (0) | Post RSSRSS comment feed

WCF, Interop, and the elusive BinarySecurityToken

If you plan to write WCF services that will be consumed by clients of technologies other than .NET--and would like to include some level of security, like message signing (via X.509 certificates)--chances are you'll have to find some way to emit a BinarySecurityToken value in your responses.

When I first configured the security of my service, I went with a custom binding and a configuration like this:

<binding name="CustomBindingForX509">
<textMessageEncoding messageVersion="Soap11" />
<security allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificate"
requireDerivedKeys="false" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
<secureConversationBootstrap />
</security>
<httpTransport />
</binding>


This seemed to make sense (as far as WCF configuration can make sense, anyway): I was sharing certificates with my client (a Java Axis2 client), hence the MutualCertificate setting seemed appropriate.  I was also going for the lowest common denominator SOAP settings (as most folks in the interop space seem to recommend), so I went with those recommended SOAP settings.  This configuration had the effect of rendering my response to look as such (I only show the SOAP header section since it is what's relevant here):

<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-5965cceb-61d9-4d3f-a503-c3f4dc7fe08a-3">
<u:Created>2008-07-17T20:33:42.153Z</u:Created>
<u:Expires>2008-07-17T20:38:42.153Z</u:Expires>
</u:Timestamp>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>3dcC17frvtyzp8G+kR5otzreQf0=</DigestValue>
</Reference>
<Reference URI="#uuid-5965cceb-61d9-4d3f-a503-c3f4dc7fe08a-3">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>h/DtKybbi4Q3RRtYKm26SGM2mcM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>IRlfHoVu/JispcD5CdMCKnbHNZcSVVNNLBtXbnP3fcid+nPi1F4WNGVsHjkF6PnaIzKM/5j2Vhnxbkm1tTwFjeKelQipCHErrwXsxOKMaVKlP/2gjeiJ0K2kkEO7LIUIcmqQ9MNx/AfGr9zE4c6EPGrkbPJVYLvra5jUhypMAcM=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Sample Service, OU=Rampart, O=Apache, L=Colombo, S=Western, C=LK</X509IssuerName>
<X509SerialNumber>1187603713</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>


The problem is, my client didn't like these settings and kept throwing the error "The signature verification failed".  I suspect the client didn't like this response because the MutualCertificate setting seems to want the client to look up the certificate via distinguished name or serial number to verify that the message was signed appropriately.  Java clients, and apparently a few other technologies, don't seem to work that way.

So, what to do?  Well, eventually I found this post describing a similar problem between a WCF service and an SAP NetWeaver client.  Part III of the post describes what they did to resolve the signature verification problem: namely, get the WCF service to inject a BinarySecurityToken instead of the SecurityTokenReference (with issuer name and serial number).

I'm not totally fond of their implementation--they did the old school write-a-console-app-to-host-my-WCF-service approach--but this gave me an important clue to how to inject the BinarySecurityToken: use the AsymmetricSecurityBindingElement class!  Wow, why didn't I think of that?  That's fairly...uh...obvious.

Ok, so how do I use this AsymmetricSecurityBindingElement class?  I know I don't want to write a console app just to plug in this functionality--that's what IIS is for.  Guess I'll have to go to a custom BindingElement extension (see the attachment for the code and thanks to Scott, the Microsoft tech who wrote it--and helped me with this problem):

(excerpt from the client and service configurations)
<extensions>
<bindingElementExtensions>
<add name="MySecurityBindingElement" type="MySecurityBE.AsymetricSecurityBEExtentionElement, MySecurityBE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="MyCoolBinding">
<MySecurityBindingElement/>
<textMessageEncoding messageVersion="Soap11"/>
<httpTransport/>
</binding>
</customBinding>
</bindings>



But wait, there's more!  It turns out that you don't even need to write your own custom BindingElement.  It turns out that the authentication mode MutualCertificateDuplex makes use of the AsymmetricSecurityBindingElement class.  What?  Didn't you know that?  So, all you really have to do is expose an endpoint with this kind of binding and you're set.  So, this service configuration:

<binding name="CustomBindingForX509">
<security authenticationMode="MutualCertificateDuplex"
requireDerivedKeys="false"  messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
<textMessageEncoding messageVersion="Soap11" />
<httpTransport  />
</binding>


Will render this response (again, I'm just including the header for brevity):

<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-39531f92-c0d4-4129-b491-6222c06d3bf5-1">
<u:Created>2008-07-25T16:03:59.959Z</u:Created>
<u:Expires>2008-07-25T16:08:59.959Z</u:Expires>
</u:Timestamp>
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>2x4uPP0r/Zo7auuboFg+8h0k3Yo=</DigestValue>
</Reference>
<Reference URI="#uuid-39531f92-c0d4-4129-b491-6222c06d3bf5-1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>Z161SvyizqMC3alennK2c6FLiZg=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>EDpLDry/QuwxVuXe+/0LZrmrLkTcoZ1Ls45qvu+RebTRFfkq8HksKMN3Ip4T2begyDCLfGOTpEHfX/ohyMS7HIsxluyIwJ971kyLVt6nUZPfjqQ3iD3hCI2cCSRtNbC1p+aZDr3Tn/KbLxjWQ4aFfm7lRKbLGeVDEY5BJYyVCqY=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#uuid-f32fc9b9-695e-4542-98bb-acf40424321b-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>


Note that the response now includes the BinarySecurityToken and, ironically, has removed the x509 name and serial number data.  Don't worry about that "removed" comment, the binary token was sent to my client, it's just that WCF automatically removes privacy information from its logs--and I'm pulling this data from the log files.  For more info on privacy removal, see here.

Now, my non-.NET clients are happy, but I've just hosed my .NET clients.  From what I'm told, duplex mode is no walk in the park for .NET clients.  So, what to do?  Well, I'll tell you what I plan to do: just expose another endpoint.  If you're a .NET client, you'll get a traditional MutualCertificate binding endpoint.  If you're not a .NET client, you'll get the duplex endpoint.  The custom extension is nice, but not absolutely necessary at this point.

AsymExample.zip (899.63 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Thursday, September 18, 2008 10:33 PM
Permalink | Comments (1) | Post RSSRSS comment feed

ASP.NET Routing Security Backdoor

Some time ago, Phil Haack posted a really cool entry on the System.Web.Routing feature, new in .NET 3.5 SP1.

In his post, he noted a "subtle potential security issue" where you might think you've secured a particular directory via a nested web.config and appropriate <authorization> element, but, in reality, you've totally circumvented that security.  Phil then re-included his security check by calling to the UrlAuthorizationModule.CheckUrlAccessForPrincipal method in his IRouteHandler implementation to determine if the user has permissions to the web resource being requested.  Here are two alternative approaches without having to add the UrlAuthorizationModule code:

Option 1: Change the "BackDoor" Route
Since the rule is to deny all requests to resources in the Admin sub-directory, if we change the route slightly to this:
routes.MapWebFormRoute("Secret", "admin/BackDoor", "~/Admin/SecretPage.aspx", false);


we can get the desired result:

 

Option 2: Add a Location Path to the Authorization Configuration
Leaving the Backdoor route as it is in the original demo code, another approach to take would be to add a <location> element around the authorization rule.  Thus, in the parent web.config file, we could add this configuration:

  <location path="backdoor">
<system.web>
<authorization>
<deny users="*"/>
</authorization>
</system.web>
</location>


and achieve the desired result:

 

 

All in all, though, I'm glad Phil chose the route he did because I had the chance to learn about the UrlAuthorizationModule.

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Friday, September 12, 2008 5:14 PM
Permalink | Comments (15) | Post RSSRSS comment feed

Sidebar - signing assemblies

As a sidebar to my post on fully-qualified assembly names, I wanted to post the steps I use to sign an assembly.  These steps have changed from what I used to do in .NET 1.1, where I used an assembly annotation in AssemblyInfo.cs to point to the key file.  That attribute has been deprecated in .NET 2.0.

I sign an assembly by creating an SNK file with the SN.exe utility.

  1. At the command line, execute sn -k BradTestKey.snk
  2. Import the key pair into the machine container by executing, sn -i BradTestKey BradTestKey.snk
  3. Open the CSPROJ file in Notepad
  4. Under the first <PropertyGroup> node, add <KeyContainerName>BradTestKey</KeyContainerName>


Now, your assembly is signed. 

But wait a minute, what if I only want to sign my Release builds, not my Debug builds.  Can I do that?  Why yes, you can.  A default CSPROJ file will contain MSBuild PropertyGroup nodes that execute either under the Debug or Release configuration mode.  So, here's what you do:

  1. Open your CSPROJ file in Notepad
  2. Find the <PropertyGroup> that looks like this: <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
  3. Under that node, add <KeyContainerName>BradTestKey</KeyContainerName>


Now, only your Release binaries will be signed.

Remember that the BradTestKey.snk and the BradTestKey machine container name are my conventions and you should use your own. 

Proof that is worked (test code is also attached to this post):

ConfigFullDNTests.zip (25.66 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: Technology Blog
Posted by Brad on Sunday, July 20, 2008 1:43 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Fully qualified assembly names

This happens to me frequently: many of the assemblies I write require configuration in the configuration file.  So, I'll write a portion of my unit tests to deserialize an assembly and assert that it received the designated settings from the config.  I also generally deploy signed assemblies (so that they can go into the GAC, etc); most of the time, if I sign an assembly and then run my unit tests, the portion of the unit tests testing my assembly loads from the config fail.  Why?  Because I'm using no longer using a valid fully-qualified assembly name in my test config.  Typically, my test configs will look something like this:

<configuration>
<configSections>
<section name="MySection" type="Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=1.0.0.0, PublicKeyToken=null"/>
</configSections>
<MySection someSetting="hello cleveland" />
</configuration>

However, when I sign the Brad.Tests.ConfigSections assembly, the PublicKeyToken value will no longer be null and my test will fail with a configuration exception.

So, why am I bringing all of this up?  Well, the other day, I ran a bunch of tests written by another developer against an assembly I knew was signed.  I figured some of the tests would fail--I just wanted to see which ones.  Interestingly, all passed including the ones written to work with the config file.  So, either the tests were written to work only with a signed assembly (probably not the best approach for a unit test) or something else was afoot.  When I looked at the test config, I saw something like this:

<configuration>
<configSections>
<section name="MySection" type="Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=1.0.0.0"/>
</configSections>
<MySection someSetting="hello cleveland" />
</configuration>

Hmm.  The developer used a truncated version of the assembly name.  I didn't know you could do that.  So, let's try some experiments:

Configuration used: "Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections"
Unsigned assembly: works
Signed assembly: works

Configuration used: "Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=1.0.0.0"
Unsigned assembly: works
Signed assembly: works

Configuration used: "Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=1.0.0.0, PublicKeyToken=null"
Unsigned assembly: works
Signed assembly: fails

Configuration used: "Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=1.0.0.0, PublicKeyToken=f479d21bc132bee6"
Unsigned assembly: fails
Signed assembly: works

Let's try something a little different.  Keeping my version at 1.0.0.0, let's this configuration and see what happens:
Configuration used: "Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=2.0.0.0"
Unsigned assembly: works
Signed assembly: fails

Hmm.  That was interesting: when an assembly is unsigned, the version seems superfluous.  However, when an assembly is signed, the version designation becomes important.

How about this: bump my assembly's version up to 2.0.0.0 and try this configuration:
Configuration used: "Brad.Tests.ConfigSections.MySection, Brad.Tests.ConfigSections, Version=1.0.0.0"
Unsigned assembly: works (guess we still don't care about version number)
Signed assembly: fails


Sidebar on signing assemblies

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:
Categories: Technology Blog
Posted by Brad on Sunday, July 20, 2008 1:16 PM
Permalink | Comments (0) | Post RSSRSS comment feed