Tuesday, March 26, 2013

Automated combinatorial and spot testing of web service operations.

Introduction & Scope

When developing a new web service, you may need to do an initial spot test walk through the operations or try to break the service based on the permissible data type values. You could hand craft all the operations, with their respective arguments, into a handful of URLs but that would seem pretty inefficient. I found myself in a similar situation and wanted a programmatic way to walk through the service operations with a specific input data set. To be clear, I am not attempting to recreate a full web service testing application but rather a simple script to spot check responses and catch any bugs in a web service endpoint's handler.

Investigating the WSDL

We can automatically flesh out our operations and the message data types by leveraging the endpoint's WSDL. In my case, I was specifically interested in the request/response operations available under the HTTP GET verb. We can get the parameter names and argument types by looking at the types section and then the actual operations under a WSDL binding that includes an HTTP binding element with the verb attribute value of GET.

Fortunately, I am dealing with input messages using just the built in data type schema provided by the WSDL specification. This means that no complex types are involved and the testing input domain is somewhat simplified. Also, most of the web service operations use a standard set of parameter names such that I am able to create a smaller data set of inputs based around them.

The Code

Your modifications would be to the test_args dictionary. You can specify a wide range of different arguments to feed your operations by parameter names. The service I was testing against used many of the same parameter names and corresponding domain.

Conclusion

Thrown together, the script provides an easy way to spot test a web service by automatically processing the WSDL for a particular binding and running those bound operations against a provided set of input messages for it.

More Reading & Resources

Friday, January 4, 2013

Text generation using Markov chains.

Introduction

If you are already familiar with finite automata, then understanding a Markov chain will not be as hard. If you are not familiar with one, you can think of finite automata as a flowchart, with states and paths leading from one state to another based on some decision being made. An example could be a room with a light switch; there are the two states of having the light on or off. Using the light switch will transition you between the two states.

The difference between standard finite automata and Markov chains is that instead of having some definite outcome determining each path (eg. yes or no), they are probabilities. Take for example a coin flip, there is a 50% (0.5) chance that it could either be heads or tails.

An Example

Let's explore Markov chains a little deeper with respect to natural languages and take a look at some sample text that will form the corpus of our analysis.

I like turtles. I like rabbits. I don't like snails.

This would render a Markov chain as follows:

The above chain means that, among the sentences:

  • 100% (1.0) will start with I.
  • This will be followed by like for 66% (0.66) of the time and don't for 33% (0.33) of the time.
  • The word don't will always (100% / 1.0) be followed by like.
  • Then lastly, turtles, rabbits and snails will all follow like 33% (0.33) of the time.

The nice thing about Markov chains, with respect to natural language modelling, is that they form word dependencies without any knowledge of the language's syntax or semantics. The chain gets created purely based on statistical knowledge extracted from the corpus.

Text Generation

Random Selection

Using the Markov chain example from before and taking the weight of probabilities into account, we can randomly traverse the chain to generate new sentences like:

I don't like turtles. I like snails.

Although, our small corpus does not make for very interesting or new text. We could make it more interesting by adding to it or switching to a much larger corpus altogether. In my case, I decided to take all of Shakespear's sonnets and generate the text from that. You can see some example code that uses it as the corpus below.

The Code

The Markov chains will be built up for each new sentence it comes across. The critical parts to the MarkovChain object are its:

  • Cardinalities: The cardinality of the sample space for a given state. In our original example, the cardinality of like is 3 since snails, rabbits and turtles follow out of the like state. The cardinality of I would be 2 since don't and like follow.
  • Occurrences: The edge valued occurrences between the different states. For example, a transition from the I to like state occurred twice. The probability can then be easily calculated by taking the number of occurrences on a transition and divide it by the cardinality of the state's sample space. Note, there is also a special start state transition that can be used at the beginning of each new sentence generation.

Using these two parts, a random walk can be made across a chain path, starting from the special start state. Each walk will generate a new sentence based on probabilities of the transitions along the path.



Additional Resources

Wednesday, October 3, 2012

Reading and writing large binary data in T-SQL and ADO.NET.

Introduction

In some cases, you may want to read/write binary data from/to your relational database. This can be a problem when the data set size exceeds the memory address size (eg. 32-bit processes) or the managed large object heap cannot load it all without throwing an OutOfMemoryException. In those cases, you will need to load the data into memory in chunks. Fortunately, the T-SQL syntax supports this operation.

Latest Standard

The old standard for reading and writing raw text data was by using the UPDATETEXT, READTEXT and TEXTPTR commands. Although, these are being obsoleted for the newer command SUBSTRING and the addition of the .WRITE argument to the UPDATE command. I found the newer syntax much easier to use and requiring less parameters. Below is an example schema and implementation.

Example Schema

For this example, we will use a very simple table setup with a primary key and binary data column.

CREATE TABLE [dbo].[DataTable] (
 [Id] [int] IDENTITY(1,1) NOT NULL,
 [Data] [varbinary](max) NOT NULL
)

Example Code

The Read method simply provides an offset into the data and the size of the chunk it wishes to read. The same applies for the Write method, except that a data argument is also supplied.

private const int ReadChunkSize = 1 << 21;   // 2MB Chunks
private const int WriteChunkSize = 1 << 21;  // 2MB Chunks

...

public static void Read(string connectionString, string readPath)
{
    using (var connection = new SqlConnection(connectionString))
    {
        int offsetIndex = 0;
        var buffer = new byte[1];
        var offset = new SqlParameter("@Offset", SqlDbType.Int, 32);
        var command = new SqlCommand(@"SELECT SUBSTRING([Data], @Offset, " + ReadChunkSize + ") FROM [dbo].[DataTable] WHERE [Id] = 1", connection);

        command.Parameters.Add(offset);

        connection.Open();

        using (var stream = File.Create(readPath))
        {
            using (var writer = new BinaryWriter(stream))
            {
                while (buffer.Length > 0)
                {
                    offset.Value = offsetIndex;
                    buffer = (byte[])command.ExecuteScalar();
                    if (buffer.Length > 0)
                    {
                        writer.Write(buffer);
                        offsetIndex += ReadChunkSize;
                    }
                }
            }
        }
    }
}

public static void Write(string connectionString, string writePath)
{
    using (var connection = new SqlConnection(connectionString))
    {
        int offsetIndex = 0;
        var buffer = new byte[1];
        var offset = new SqlParameter("@Offset", SqlDbType.Int, 32);
        var data = new SqlParameter("@Data", SqlDbType.Binary, WriteChunkSize);
        var command = new SqlCommand(@"UPDATE [dbo].[DataTable] SET [Data] .WRITE(@Data, @Offset, " + WriteChunkSize + ") WHERE [Id] = 1", connection);

        command.Parameters.Add(offset);
        command.Parameters.Add(data);

        connection.Open();

        using (var stream = File.OpenRead(writePath))
        {
            using (var reader = new BinaryReader(stream))
            {
                while (buffer.Length > 0)
                {
                    buffer = reader.ReadBytes(WriteChunkSize);
                    if (buffer.Length > 0)
                    {
                        offset.Value = offsetIndex;
                        data.Value = buffer;
                        command.ExecuteNonQuery();
                        offsetIndex += WriteChunkSize;
                    }
                }
            }
        }
    }
}

Performance Considerations

When benchmarking my code against the simple database schema, execution time was drastically different if the data was already read into memory. When a read was performed after a write, it took 2 seconds. A read without a write beforehand took about 10 seconds. This is also the same time that the write took before a read. The chunk size did not seems to have any significant impact when adjusted between 512K and 4MB sizes. Your results may vary though depending on your system's memory layout.

These time benchmarks are very informal and highly dependent on system architecture, network latency and hardware specifications. Although, the relative performance gain between the two benchmarks can be gleaned from these tests.

A further performance gain would be to front load the data into memory on a separate thread and process the loaded data into and out of the database on another. This would require some tweaking and knowledge of the system's memory constraints to ensure it doesn't become a performance bottleneck. If properly done, a lot can be gained from not having to block on I/O between every chunk read and write.

Thursday, September 20, 2012

Understanding compression and Huffman Coding.

Definitions

In a lot of texts regarding compression you will run across various conventions of syntax to describe the coding of data. Much of them are used in fields like automata theory, group theory and statistics. Although, the field of coding theory itself has a long and rich history in the 20st century; much of the terminology is borrowed from fields that gave rise to it.

I find the best way to demonstrate the following definitions is by example. Let us take the text "abbcc" as a sample.

  • Alphabet: The set of symbols that we will be encoding.
    • Form:
    • Example: where
  • String: A permutation of symbols from our alphabet. From our example alphabet, strings like "a", "aba", "abc" or "cccb" could all be possible. Using regular expression syntax, we can describe this in our example.
    • Form:
    • Example:
  • Probabilities: The set of probabilities for the alphabet. This is also known as their weights and can sometimes be denoted as w instead of p.
    • Form:
    • Example: where since 'a' occurs once, 'b' occurs twice and 'c' occurs twice out of 5 symbols all together.
  • Codewords: The set of codewords that we will create for every symbol in our alphabet.
    • Form:
    • Example: where for symbols respectively. Another way to look at it is that we are providing a bijective function: .

Code Width

Standard coding of information for most computers today is byte aligned. For example, in character codes there is ASCII, which codes the entire English alpha-numeric system in a single byte. While UTF-8 has proven to be a much better standard due to better localization support and variable width codes, ASCII serves our purposes for this discussion. ASCII is considered a fixed width coding scheme:

When performance is at stake, it is best to code information atomically aligned to the computer architecture that it is being used for. This means allocating and defining information in terms of bytes (defined as 8-bits) for the majority of systems today. Although, if space is the priority, performance can take a hit to shift around bits along the byte boundaries and create variable width codes that do not byte align.

Note: The ellipses in the alphabet denote the control characters (eg. CR, LB) and grammatical structure symbols (eg. #, $, %) contained in the ASCII code page. The alphabets will reflect this in the ellipses.

Data Compression

Variable Width Savings

Coming back to the example of ASCII, if we wish to encode a string, w, like "aaabb", it will take up 5 bytes. Although we only really need one bit to encode our information if we assign 0 to 'a' and 1 to 'b'.

We just took a 5 byte ASCII encoded string and compressed it down to under a byte. Now let us take another string, w, and give it "aaabbcc". Three alphabet symbols need to be coded; it will take at least 2 bits. At first glance, this assignment would make sense:

Although this gives us an ambiguous string since it could either be "aaabbcc", "aaabbabab", "aacbcc" or any other permutation of our codeword set when we attempt to decompress it. The reason for this ambiguity is due to the fact that the codeword for 'a' (0), is used to start the codeword for 'c' (01).

Prefix Codes

The way to get around this ambiguity is to ensure that all of your codes are prefix type. Something like this would work:

This ensures that we maintain the one-to-one mapping between our alphabet and codewords; we can translate alphabet symbols to codewords and codewords back to alphabet symbols without confusion.

Codeword Assignment

So how do you decide which symbols, get which codewords? Also, since the size of the codeword is critical to the compression ratio (average codeword length to average symbol length), designing an algorithm to weigh the more frequently used symbols is important. In our previous example, it would be poor design to give 'a' the longest codeword because it is the most used symbol in the string. This is where the Huffman Coding algorithm comes into play.

Huffman Coding Algorithm

  1. Count the number of times each symbol occurs in the data source.
  2. Put [symbol:count] pairing as nodes in a minimum priority queue based on the symbol's count.
  3. Evaluate number of queue nodes.
    • If queue has at least two nodes:
      • Dequeue the two nodes.
      • Create a new node with a null symbol and set the count equal to the sum of the two dequeued node's counts.
      • Attach the two nodes as children to the new node.
      • Put the newly created node into the queue.
      • Go to step 3.
    • If queue has only one node, dequeue and set as the root of the tree.
  4. Assign each left branch a 0 and each right branch a 1, or vice versa if you choose.
  5. The symbol nodes will be the children of the tree and their codeword will be the bits that are traversed to get there.

Algorithm Example

Given our previous example string of "aaaabbcc", let us perform the algorithm on it:

  1. Initial queue state with only symbol nodes in it.



  2. New node created from first two symbol nodes and requeued.



  3. New node created from non-symbol and symbol node, becoming the tree's root node.



  4. The final alphabet to codeword mapping is:

If you need further examples, you can look here, here and here. Once the you get a grasp of the algorithm, you will see the simplicity and beauty of it.

Working Code Example

If you want to see a naive implementation of the Huffman Coding algorithm, I posted some source code on my GitHub account. Below is the central code to the algorithm execution.

More Reading & Resources

If you are interested in coding algorithms, these links might be of interest to you.

Tuesday, September 18, 2012

IMAPI version 2 managed wrapper.

Introduction

Microsoft introduced native image mastering support in Vista, going forward. The library itself is not too complex but the latest version of the image mastering API (IMAPI) has some quirks. This is well documented by Eric Haddan in his article describing how to build an interop file and use it from .NET.

Implementation

I wanted to take it a step further and provide a library wrapper that was CLI compliant. The end result is a class that provides 3 methods, called in order, to burn an image. The project can be found on my GitHub account.

Properties

  • Media: A read only property that populates after the LoadMedia call and reports what type of physical media is connected (eg. CD-R, DVD-R, DVD+RW, etc.).
  • MediaStates: A read only collection of states in which the media is under, that populates after the LoadMedia call (eg. is blank, overwriteable, unsupported).
  • MediaCapacity: A read only property that populates after the LoadMedia call and reports the size capacity of the media.
  • Nodes: A root list of file and directory nodes that are to be written to the media.
  • Recorders: A read only selectable collection. A recorder must be selected for the LoadRecorder call.
  • VolumeLabel: A property that sets the volume label of the media to be written to.

Methods

  • LoadRecorder: Verifies a recorder is selected, loads the recording drive resources and verifies that it is working.
  • LoadMedia: Loads the media resource and verifies that it is valid for the recorder. An exception will occur if the media isn't supported (eg. closed session disc, no disc present).
  • FormatMedia: Formats the media of any previous data.
  • WriteImage: The final step to write your files to disc. This is a blocking call.

Events

  • FormatEraseUpdate: Reports progress of FormatMedia call.
  • FormatWriteUpdate: Reports progress of files being written to the media.
  • ImageUpdate: Reports progress of files being added to the media image.

Caveats

I use this library for the simple single purpose of writing finalized images to a blank CD. It has not been fully tested to accomodate all media and recorder state scenarios; I haven't tried it yet with DVD's or multi-session discs. If you would like to extend the library, feel free to push some changes to the GitHub project and I will wrap them in.

I decided to leave the method calls as blocking (synchronous) since there could be many different synchronization models that may use this library. For my purposes, I used this in an WPF application that would call the WriteImage method from a Task and then post back the progress events of the write using the Dispatcher from my WPF window. Below is a code snippet demonstrating this.

private ImageMaster _burner;

...

private void _CreateCd()
{
 // files added to _burner and _burner.WriteImage called
}

private void _burner_FormatWriteUpdate(IMAPI2.ImageMaster sender, IMAPI2.FormatWriteUpdateEventArgs e)
{
 this.Dispatcher.Invoke(() => this.statusProgressBar.Value == (e.ElapsedTime / e.TotalTime) * 100);
}

private void CreateCdWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
 System.Threading.Tasks.Task.Factory.StartNew(_CreateCd);
}

Thursday, September 13, 2012

Implementing a bit reader/writer in C.

Introduction

I was working on implementing a simple compression tool based on naive Huffman coding and initially assumed all of my prefix codes would be at or under a byte long. Although I failed to account for the worst case of Huffman coding, which can yield codes up to 255 bits in length.

This required a bit reading and writing function that would allow for variable length codes. I could have used other methods to balance worst case Huffman trees, but this issue presented a unique challenge. How to write a bit reader/writer in C.

Bit Reader/Writer Specifications

  • Read and write using reader and writer ADT's.
  • Handle any possible bit length given.
  • Remember bits read from file but are outside the requested bit length (eg. 2 bytes read in but only 12 bits requested, leaving 4 bits in the buffer).
  • Flush a partially written block to disk on a free of the bit writer ADT.
  • Return number of bits read and written on the read and write functions.

Design

The underlying structure to the ADT will contain fields for the: file pointer, the byte long buffer and the buffer bit length. The examples given for every case are in steps; what is originally in the buffer, what bits are requested, what bits are read from or written to the file, and finally what bits are left over and buffered.

Reader

There are two main cases to consider when designing the read function.
  1. Empty Buffer: The is simple since the file read bits will already be byte aligned to your output bits. You just need to simply mask out the unneeded bits and buffer them for the next read (eg. no bits buffered, 12 bits requested, 16 bits read in, 4 bits buffered).
  2. Populated Buffer: The can get a little trickier, especially in the last two cases of this.
    1. Requested bits already buffered. This is very easy since it doesn't require any file read and the buffer just needs to shift out the read bits (eg. 7 bits in the buffer and 3 are requested; buffer serves out 3 and truncates to 4).
    2. Not all requested bits buffered; end of read bits not in same byte block as end of output bits. The last output byte block must mask out the unneeded bits, put them in the buffer and then append the additional unneeded read bits from the next byte block (eg. 4 bits buffered, 13 bits requested, 16 bits read in, 8 bits buffered).
    3. Not all requested bits buffered; end of read bits in same byte block as end of output bits. The last output byte block must mask out the unneeded bits and put them in the buffer (eg. 4 bits buffered, 10 bits requested, 8 bits read in, 2 bits buffered).

Writer

There are only two cases to consider when designing the write function.
  1. Full Buffer: Shift in write bits, commit to disk and repeat until all bits are processed. If the last bit to write is not on the buffer byte boundary, it will wait until the next write (eg. 6 bits buffered, 12 bits to write, 16 bits written out, 2 bits buffered).
  2. Partial Buffer: Shift in write bits (eg. 2 bits buffered, 4 bits to write, no bits written out, 6 bits buffered).

Caveats

The code below is purely a demonstration of how a bit reader/writer works in a generic sense. There is room for plenty of optimization if you know what type of architecture your targeting and the domain of your reader/write arguments.

The Code

Header File

Source File

Tuesday, August 14, 2012

Licensing using XML digital signing.

Motivation

One of the main concerns in any licensing implementation is that authored license files are coming from the trusted authority (ie. your company) and not some third party. This can be accomplished in software by encrypting and decrypting the license with a single key, but symmetric key storage can be cumbersome and have added complexity.

Assymetric cryptography, on the other hand, allows for two keys: a private one to author files with and a public one to verify its authenticity. This is well suited for licensing, where we want to ensure that the authored license has not been altered in anyway and the content can be relied on.

Implementation

I ran across a great article that details the use of XML digital signatures in the .NET framework. It describes the type of digital signatures that can be implemented and lays out the classes and methods needed to author and verify license files.

This specific implementation allows for an: authentication key to compare against some unique value tying the license to the system, expiration date to set a time span on the software use and set of available features the user is authorized for.

Base Class

This forms the runtime object representation of the license and will be inherited by the license reader and writer.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;

namespace Licensing
{

public abstract class LicenseBase
{ 
 private byte[] _authenticationKey; 
 private DateTime _expiration; 
 private List _features;
    private int _id;

 public byte[] AuthenticationKey {
  get { return _authenticationKey; }
  protected set { _authenticationKey = value; }
 }
 public DateTime Expiration {
  get { return _expiration; }
  protected set { _expiration = value; }
 }
 public int Id {
  get { return _id; }
        protected set { _id = value; }
 }
 protected List Features {
        get { return _features; }
 } 

 public LicenseBase()
 {
  _features = new List();
 }

    public void Clear()
    {
        _id = 0;
        _authenticationKey = null;
        _features.Clear();
    }
}

}

Writer Class

This class will be used internally to author the license files that will be distributed with the released software. Note that the private key is added as an embedded resource in the project and should not be distributed since the public key can be derived from it.

using System;
using System.IO;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Text;
using System.Xml;

namespace Licensing
{
    class License : LicenseBase
    {

        License(int id, byte[] authenticationKey, DateTime expiration, string[] features)
        {
            this.AuthenticationKey = authenticationKey;            
            this.Expiration = expiration;
            this.Id = id;
            this.Features.AddRange(features);
        }

        /// 
        /// Generates a key pair for digital signing and verification.
        /// 
        /// 
        /// 
        static internal void GenerateAssymetricKeys()
        {
            string datestamp = null;
            StreamWriter output = null;
            RSA key = RSA.Create();
            key.KeySize = 1024;

            datestamp = DateTime.UtcNow.ToString("yyyyMMdd");

            // Generate private key to only be used internally (DO NOT DISTRIBUTE).
            output = File.CreateText("private-" + datestamp + ".key");
            output.Write(key.ToXmlString(true));
            output.Close();

            // Generate public key to be used by customers (distribute).
            output = File.CreateText("public-" + datestamp + ".key");
            output.Write(key.ToXmlString(false));
            output.Close();
        }

        /// 
        /// Digitally sign an XML document.
        /// 
        /// The XML document to sign.
        /// The private key to sign it with.    
        /// 
        private static void _SignXmlDocument(System.Xml.XmlDocument document, RSA privateKey)
        {
            SignedXml signedDocument = new SignedXml(document);
            signedDocument.SigningKey = privateKey;
            signedDocument.SignedInfo.CanonicalizationMethod = SignedXml.XmlDsigCanonicalizationUrl;

            // Add reference to XML data
            Reference @ref = new Reference("");
            @ref.AddTransform(new XmlDsigEnvelopedSignatureTransform(false));
            signedDocument.AddReference(@ref);

            // Build the signature.
            signedDocument.ComputeSignature();

            // Attach the signature to the XML document.
            XmlElement signature = signedDocument.GetXml();
            document.DocumentElement.AppendChild(signature);
        }

        /// 
        /// Write the contents and digitally sign the document.
        /// 
        /// The file path to the digitally signed document.
        public void WriteDocument(string filepath)
        {
            XmlDocument document = new XmlDocument();
            document.AppendChild(document.CreateXmlDeclaration("1.0", "UTF-8", null));

            XmlElement root = document.CreateElement("License");
            document.AppendChild(root);

            XmlElement id = document.CreateElement("Id");
            id.InnerText = this.Id.ToString();
            root.AppendChild(id);

            XmlElement authenticationKey = document.CreateElement("AuthenticationKey");            
            authenticationKey.InnerText = Convert.ToBase64String(this.AuthenticationKey);
            root.AppendChild(authenticationKey);

            XmlElement expiration = document.CreateElement("Expiration");
            expiration.InnerText = this.Expiration.ToString();
            root.AppendChild(expiration);

            XmlElement options = document.CreateElement("Features");
            XmlElement featureItem = null;
            foreach (string feature in this.Features)
            {
                featureItem = document.CreateElement("Feature");
                featureItem.InnerText = feature;
                options.AppendChild(featureItem);
            }
            root.AppendChild(options);

            XmlElement publickey = document.CreateElement("Certificate");
            publickey.SetAttribute("DateCode", KeyCreationDateCode);
            publickey.InnerXml = File.ReadAllText(PublicKeyFilename);
            root.AppendChild(publickey);

            RSA privateKey = RSA.Create();
            privateKey.FromXmlString(Licensing.Properties.Resources.privatekey);
            _SignXmlDocument(document, privateKey);

            File.WriteAllText(filepath, document.InnerXml);            
        }

    }
}

Reader Class

This class will be used in the field to verify the license files that will be distributed with the released software. It is best to wrap the license read and authenticate methods in try-catch blocks since they represent exceptional behavior that fall outside the post-conditions of the method. Handling the exceptions will allow for better error reporting in the field so you can decide what information you will share with the end user (eg. bad authentication key, malformed/tampered licenses). Note again that the public key is an embedded resource for the project. It should never be distributed with the license since an attacker could generate their own keypair and envelope their own public key, with signature, in the license.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Xml;

namespace Licensing
{
    class LicenseReader : LicenseBase
    {
        LicenseReader()
        {
        }

        public void Authenticate()
        {
            // Check the hardware key value against the AuthenticationKey        
            if (!_AuthenticationKeyMatches(hardwareKey))
                throw new LicenseAuthenticationException("The license failed to authenticate the hardware key.");

            // ... or check some node locking ID (eg. MAC ID, hard drive serial number) against the AuthenticationKey.
            if (!_AuthenticationKeyMatches(nodeLockedId))
                throw new LicenseAuthenticationException("The license failed to authenticate the node locked ID.");

            if (this.Expiration > DateTime.UtcNow)
                throw new LicenseAuthenticationException("The license has expired.");
        }

        /// 
        /// Compare byte array against authentication key byte array.
        /// 
        /// Byte array to compare against.
        /// True if a match, else false.
        private bool _AuthenticationKeyMatches(byte[] compare)
        {
            if (this.AuthenticationKey == null)
                return false;

            int upperBound = Math.Min(this.AuthenticationKey.Length, compare.Length);
            for (int i = 0; i < upperBound; i++)
            {
                if (this.AuthenticationKey[i] != compare[i])
                    return false;
            }
            return true;
        }

        public bool IsFeature(string featureName)
        {
            return this.Features.Contains(featureName);
        }

        /// 
        /// Read the digitally signed document and load its contents.
        /// 
        /// The file path to the digitally signed document.
        protected virtual void ReadDocument(string filepath)
        {
            this.Clear();

            XmlDocument document = new XmlDocument();
            document.Load(filepath);

            RSA publicKey = RSA.Create();
            publicKey.FromXmlString(Licensing.Properties.Resources.publickey);

            if (_VerifyXmlDocument(document, publicKey))
            {
                this.Id = int.Parse(document.SelectSingleNode("//License/Id").InnerText);
                this.AuthenticationKey = Convert.FromBase64String(document.SelectSingleNode("//License/AuthenticationKey").InnerText);
                this.Expiration = DateTime.Parse(document.SelectSingleNode("//License/Expiration").InnerText);

                XmlNodeList features = document.SelectNodes("//License/Features/Feature");
                foreach (XmlNode feature in features)
                {
                    this.Features.Add(feature.InnerText);
                }
            }

        }

        /// 
        /// Verify the digital signature of an XML document.
        /// 
        /// The XML document containing the signature.
        /// The public key to verify signature authenticity.
        /// True if the signature is authentic, else false.
        /// 
        private bool _VerifyXmlDocument(XmlDocument document, RSA publicKey)
        {
            SignedXml signedDocument = new SignedXml(document);
            try
            {
                XmlNode signature = document.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0];
                signedDocument.LoadXml((XmlElement)(signature));
            }
            catch
            {
                return false;
            }
            return signedDocument.CheckSignature(publicKey);
        }

    }
}

Additions & Caveats

Symmetric encryption could also be used to hinder casual viewing of the license file. The issue of symmetric key storage must be revisited but will no longer be critical to maintaining the integrity of the license files.

Strong naming of the license reading assembly is encouraged to prevent dissasembly and re-embedding of an attacker's own public key. Although, this is by no means an attack-proof method since strong naming can be removed by changing information in the CLI header of the portable executable (PE). Even if the application was able to secure the public key outside the application in a PKI, there is still the issue of code injection or recompilation once strong naming is stripped. Indirection of the public key or obfuscation of the assembly can also help but is not a reliable security measure.