Sunday, November 25, 2012

Comparing X509 distinguished names with BouncyCastle

In this post I am going to show how to compare X509 distinguished name using BouncyCastle. Distinguished names are used to specify the subject and issuer identity in X509 certificate. Distinguished name(DN) is like a LDAP directory name and contains one or more relative distinguished name(RDN) separated by comma. The definition for distinguished name can be found in X500 specification.

The problem with comparing distinguished name is the relative distinguished names they contain can be in any order. So simple string comparison will not work and parsing the distinguished name is not straight forward.

But BouncyCastle provides a class named X509Name through which we can easily parse and compare the X509 distinguished names. The below code snippets demos the parsing and comparison of X509 distinguished names
static void Main(string[] args)
{
    var name1 = new X509Name("CN=person1, OU=dept1, O=org1");
    var name1Dup = new X509Name("O=org1, OU=dept1, CN=person1");
    var name2 = new X509Name("CN=person2, OU=dept2, O=org1");

    Console.WriteLine("name1 = {0}", name1);
    Console.WriteLine("name2Dup = {0}", name1Dup);
    Console.WriteLine("name2 = {0}", name2);
    Console.WriteLine();

    Console.WriteLine("name1 == name1Dup => {0}", name1.Equivalent(name1Dup));
    Console.WriteLine("name1 == name2 => {0}", name1.Equivalent(name2));
}
and the output
name1 = CN=person1,OU=dept1,O=org1
name2Dup = O=org1,OU=dept1,CN=person1
name2 = CN=person2,OU=dept2,O=org1

name1 == name1Dup => True
name1 == name2 => False
The X509Name class makes it so simple to parse and manipulate the X509 distinguished names. But I encountered a situation where one of the relative distinguished name contained by X509 distinguished name is not recognizable by X509Name even though the underlying OID(object identifier) of the relative distinguished name is supported by the X509Name. The problematic relative distinguished name is "dnQualifier" whose OID is 2.5.4.46. The same OID is supported and parsable by X509Name if the relative distinguished name is "DN" instead "dnQualifier".

Digging little deep into X509Name source reveals that we can make it to parse/support "dnQualifier" relative distinguished name like below

static void Main(string[] args)
{
    X509Name.DefaultLookup["dnqualifier"] = X509Name.DnQualifier;

    var name1 = new X509Name("CN=person1, OU=dept1, O=org1, dnQualifier=aaaabbbbccccddddeeeeffffggg=");
    Console.WriteLine(name1);
}
In the above code basically we are providing another name("dnQualifier") for X509Name.DnQualifier which is by default identified with name "DN".

Executing plink from non-interactive shell

Today I had a requirement to launch plink.exe(a command-line interface to the PuTTY back ends) from a C# code and this code can run as Windows service or from an ASP.NET application. I accomplished the task with the following code
System.Diagnostics.Process.Start(PLINK_PATH_WITH_PARAMETERS);
The above line launches the plink program which I can see running in TaskManager but for some reason it is not connecting with the remote host. After some google search and experimentation with plink I finally found the reason why it is waiting and not connecting with the remote program.

Actually the plink was launched in non-interactive shell(as it is launched from Windows service/ASP.NET) and it runs under SYSTEM/ASP.NET user account. As we don't usually run plink in these user accounts plink sees the remote host's certificate as new unauthorized certificate and asking us to authorize it. As it is non-interactive shell we don't see the console and the authorization request.

After finding this I found that launching plink from another batch file with the following command line automatically answers "yes" to the plink's certificate authorization request and plink successfully able to execute the commands on remote host.
echo yes | C:\path\to\plink.exe {parameters}