programming
Rhino Mocks Callbacks
Rhino Mocks Callbacks are what I have been trying to find for 2 days!
My problem is that I needed to test the internals of a method by checking to see that a value had been evaluated properly. The result of this operation gets passed to my DAL to be inserted into my DB. My test was to test my Business Layer so I needed to mock the DAL to prevent the DB hit from occurring. The problem was, how could I grab the value from inside my BL method?
With Rhino Mocks, I mocked my DAL and recoreded an Expect on the method that does the DB insert. Now all I needed to do was to replace the default implementation of my DAL method with one used for testing. The internals of the testing method would be able to read the params being passed to the DAL from the BL, thus allowing me to verify that the BL evaluated what it was supposed to properly before passing the data on to be inserted.
Rhino Mocks Callbacks to the rescue! I love the power of delegates, unfortunately it’s still the largest concept that repeatedly trips me up. After locating this post on using Rhino Mocks Callbacks, I was finally able to accomplish my goal.
The application in the example below is a WCF service which accepts TFS Notification messages when a WorkItem changes. When I recieve the notification from TFS, I am logging the message in a log table. One of the params that is sent from TFS contains the publisher’s url in an XML string. My BL needs to select the value of the attribute containing this url and insert it into a column in my table. Thus, what I wanted to test was that my xpath is correct (and if TFS changes something in subsequent versions I’ll know immediately that they changed an expected format).
WCF Method to receive the TFS notification:
public void Notify(string eventXml, string tfsIdentityXml, SubscriptionInfo subscriptionInfo)
{
XmlDocument eventXmlDoc = new XmlDocument();
XmlDocument tfsIdentityXmlDoc = new XmlDocument();
try
{
eventXmlDoc.LoadXml(eventXml);
tfsIdentityXmlDoc.LoadXml(tfsIdentityXml);
TfsNotificationDto tfsNotificationDto = new TfsNotificationDto
{
Message = eventXml,
Date = DateTime.Now,
Publisher = tfsIdentityXmlDoc.SelectSingleNode("TeamFoundationServer").Attributes["url"].Value,
Subscriber = "",
Success = 'F'
};
this._repo.LogTfsNotification(tfsNotificationDto);
}
catch (Exception)
{
throw;
}
}
Test method utilizing Rhino Mocks to mock the DAL:
[Test]
public void TestLogTfsNotification()
{
string pub = string.Empty;
With.Mocks(delegate
{
IRepository repo = Mocker.Current.StrictMock<IRepository>();
TfsNotificationDto tfsNotificationDto = RepositoryTests.MockTfsNotificationDto;
Expect.Call(() => repo.LogTfsNotification(tfsNotificationDto)).Callback(new Delegates.Function<bool, TfsNotificationDto>(notification => { pub = notification.Publisher; return true;}));
Mocker.Current.ReplayAll();
ITfsEventService service = new TfsEventService(repo);
service.Notify(eventXml, tfsIdentityXml, subscriptionInfo);
});
Assert.AreEqual(this.publisher, pub);
}
What I can now do is in the LogTfsNotification method of the mocked repo, I can now read the parameter passed to it from the service.Notify method. Service.Notify is extracting the subscriber url and packages it up into a TfsNoficationDto that is passed to LogTfsNotification. Thus I can do notification.Publisher to see what value was extracted from the xml in the service object of my BL.
Regular Expression for Locating a Password in a Connection String
I used this RegEx to locate an encrypted password in a connection string. I wrote a utility that would allow for the encryption of a password string so that the clear text password could be replaced by the encrypted string in the config. I did this because I didn’t want the whole string encrypted, just the password. Thus at runtime the logic needed to retrieve the connection string from the config, find the password value and run it through the decryption algorithm. This is the explanation for the RegEx I used to accomplish this.
string passwordGroup = "pwd"; string searchExpression = "(?i)password=(?<" + passwordGroup + @">[0-9a-zA-Z\+/=]+)";
Example strings that this expression will be run against are :
<add name="AConnectionString" connectionString="Provider=OraOLEDB.Oracle;Data Source=XE; User ID=uid0; Password=AcQtF5W9CGmph8NxtPVfyPnDBxrmHU=;OLEDB.NET=true;Unicode=True" />
<add name="DBConnName" connectionString="packet size=4096;user id=uid0;data source=SERVER01;persist security info=True;initial catalog=catalogName;password=IkX+129i/iUshSJc0O3rCxHKz2lv6+mRNe91vIw=" />
(?i) - This is a modifier affecting how the remainder of the string is processed.
(?x) - this syntax indicates that this is a modifier. The x will be replaced with an appropriate set of modifiers. In my case those are i, s, & m.
i - turns on case insensitivity for the remainder of the string.
password= - Represents a literal string. I am using this string to find the position at which to start the regex match for the password value I am searching for. In this case the expression will begin matching at the p in password.
(?<” + passwordGroup + @”>[0-9a-zA-Z\+/=]+) - boils down to (?<pwd>[0-9a-zA-Z\+/=]+). Let’s take a look at the first part of this expression:
(?<>) - This is the .NET syntax for creating a named group. Groups are organizational structures used to pluck particular search strings out of a larger string. By default regex engines will sequentially number the groups starting at 0. In my case, I want to give my group a specific name for retrieval from the match, thus this syntax. In my expression the “pwd” string represents the name for my matching group. The contents of the matched group will be the string returned by the evaluation of the expression inside the parenthesis.
[0-9a-zA-Z\+/=]+ - matches any string containing alphanumeric characters as well as matching on +, /, and =. These 3 extra characters are allowed in the set of characters the encrypted string may contain. As you can see both examples show that the password field will either end with a ” or a ;. Since neither of those two terminating characters are in the set of allowed characters, the match will end there. Thus the match will start at the “p” in password and end before either the ; or the ” at the end of the password value.
This expression will return an entire match of: password=value. From this match one group will exist which will contain only the password value.
My entire method using this expression and extracting the named group:
protected string RetrievePasswordValue(string connectionString)
{
string passwordGroup = "pwd";
string searchExpression = "(?i)password=(?<" + passwordGroup + @">[0-9a-zA-Z\+/=]+)";
try
{
Match passwordMatch = Regex.Match(connectionString, searchExpression);
return passwordMatch.Groups[passwordGroup].Value;
}
catch (Exception ex)
{
// Do something better than this exception handling, obviously
throw ex;
}
}
}
I’m no regex expert by any means, so there may be better ways to write the expression. Please leave a comment if you see a glaring problem with my expression. One thing I could probably do to it would be to add a more explicit terminator on ” or ; rather than have it end by just not being in the set of valid characters.
Here are some great links for learning and testing your regular expressions:
http://www.regular-expressions.info
http://derekslager.com/blog/posts/2007/09/a-better-dotnet-regular-expression-tester.ashx
WCF Services SSL with Certificate Authentication
I figure since I beat my head against the desk trying to get this to work for half a week I figure I should probably post what I did in case I ever have to do this again. WCF security is not the most straight-forward configuration, and throwing in certificates sure doesn’t help the matter. What also doesn’t help is that I knew very little about certificates heading into this task.
Goals:
- Create a WCF Web Service who’s messages are SOAP over HTTPS utilizing certificate authentication.
- Create a client application that can communicate with a service configured for HTTPS using certificates for authentication.
Out of scope: We will not worry about authorization at this point. We just want a client to be able to communicate with the service.
Create the service:
Start by creating a standard WCF service by using the WCF Service Library project template. Let’s modify the service interface (IService1.cs) provided to expose a few methods for testing:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; namespace WebServicePOC { // NOTE: If you change the interface name "IService1" here, you must also update the reference to "IService1" in App.config. [ServiceContract] public interface IService1 { [OperationContract] string GetData(int value); [OperationContract] string GetExecutingUser(); [OperationContract] CompositeType GetDataUsingDataContract(CompositeType composite); } // Use a data contract as illustrated in the sample below to add composite types to service operations [DataContract] public class CompositeType { bool boolValue = true; string stringValue = "Hello "; [DataMember] public bool BoolValue { get { return boolValue; } set { boolValue = value; } } [DataMember] public string StringValue { get { return stringValue; } set { stringValue = value; } } } }
Now let’s modify the service implementation (Service1.cs) to do something when one of the methods on the service interface is invoked:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; namespace WebServicePOC { // NOTE: If you change the class name "Service1" here, you must also update the reference to "Service1" in App.config. public class Service1 : IService1 { public string GetData(int value) { return string.Format("You entered: {0}", value); } public string GetExecutingUser() { return System.Threading.Thread.CurrentPrincipal.Identity.Name; } public CompositeType GetDataUsingDataContract(CompositeType composite) { if (composite.BoolValue) { composite.StringValue += "Suffix"; } return composite; } } }
Now we should be able to compile our basic service. If you run a debug session on your new service you should be presented with the WCF Test Client application. This app will hookup to your service and provide you with a way to test the methods in your service. Invoke your methods and make sure everything is working as designed. Right now your service is being hosted by the default WCF Service Host. For this project we eventually want to expose our service over HTTPS. We could write a custom host to do that or we could host it in IIS. My vote is IIS since it already has HTTPS support, so let’s configure the service for IIS hosting.
Configure Service for IIS Hosting:
Since Microsoft wrote all this WCF stuff we may as well check-in with them on how to get a service hosted in thier web server. Indeed they do have documentation on getting this up and running.
As for converting your service to run under IIS, at this point it would simplify things to change to an ASP.NET project template for the service since the service will be exposed over HTTP/HTTPS. To do so create a new project in your solution using the ASP.NET Web Application project template. Copy in the IService.cs and Service.cs files from the first service we created and modify the namespaces to match your new ASP.NET project namespace. Next, we need to create a file that we can use as the entry point to our service in IIS. Create a new file named Service.svc in your project and add the following line to the .svc file:
<%@ ServiceHost language=c# Debug="true" Service="IISJunkService.Service1" %>
Compile your new service, set your .svc file as the startup file, and kick-off a debug session. Hrm, according to the message…”Service ‘IISJunkService.Service1′ has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.“…we haven’t configured an endpoint yet.
Add the following configuration to your web.config to setup an endpoint for your service:
<configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="basicBindingConfig"> <security mode="None" /> </binding> </basicHttpBinding> </bindings> <services> <service behaviorConfiguration="IISJunkService.Service1Behavior" name="IISJunkService.Service1"> <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicBindingConfig" contract="IISJunkService.IService1"> </endpoint> </service> </services> <behaviors> <serviceBehaviors> <behavior name="IISJunkService.Service1Behavior"> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" /> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
Now try again to debug your service. You should see a default page that looks like the following:
Our service is running under the Visual Studio web host, but we said we wanted it running under IIS. To do this open up the IIS Manager. Under an existing site add a new virtual directory. For the physical location of the virtual directory choose the directory that your service source is in. Set read and execute permissions on the directory. Browse to the new url and you should now see the same screen as above, only hosted on a port that IIS is providing services for.
Now that we have our service up and running in IIS, let’s create a client to communicate with the service we just created. Create a new project using the Console Application project template. Once the project has been created you can create a service reference to the url you just exposed through IIS. You will notice after you add the service reference that your client’s app.config has been modified. The addition of the service reference has added an endpoint configuration to your config. This will tell WCF on the client side exactly where and how to communicate with the service. Modify the Main() method in Program.cs to call a method on your service:
static void Main(string[] args)
{
try
{
JunkService.IService1 service = new JunkService.Service1Client();
Console.Write("GetData: ");
Console.WriteLine(service.GetData(12).ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine(ex.InnerException.Message);
}
Console.ReadLine();
}
Debug your client app. If all is right with WCF you will see the following output:
GetData: You entered: 12
SSL
Service SSL
So now we have a service hosted in IIS and we are able to talk to the service from our custom client app. Since everything is being transmitted in clear now, let’s add a layer of transport level security. This will mean that the transmission of our messages will be encrypted from prying eyes. Since we are running in IIS we can fairly easily modify the service to communicate via HTTPS.
To do so, in the IIS Manager:
- Right click on your service’s virtual directory
- Choose Properties
- Select the Directory Security tab
- Click Edit under Secure Communications
- Check Require secure channel (SSL)

Now if you try and refresh your service in a web browser you will get a 403.4 forbidden error telling you the service only communicates via HTTPS. So go ahead and change the protocol in the url to https://. You should now see the service’s default page again.
At this point you should be wondering what you have to do to get your client talking to the service again since we have changed protocols. Your first thought might be that all we need to do is change the endpoint url in the client configuration to point to the https:// location, and…you’d be wrong. At this point we need to modify the bindings in both the service and the client to make all this work in an encrypted transmission protocol scenario.
Let’s first start by modifying the service to enable transport level encryption. In your service’s web.config locate the bindingsnode. By default you will have one binding already defined as basicHttpBinding. We no longer need that binding, so you can remove it and replace it with this:
<wsHttpBinding> <binding name="WsBindingConfig"> <security mode="Transport"> <transport clientCredentialType="None"/> </security> </binding> </wsHttpBinding>
This new binding defines a more advanced http binding, one in which we can define transport level security. How does this binding know we want to communicate over HTTPS? I don’t know. Apparently it’s smart. Ultimately, the transport level security configuration within a wsHttpBinding means SSL. So just accept it. You will notice that with this binding, I have chosen to change the name of the binding. If you also change this name, then you must also change the reference to your binding in your endpoint. You may as well rename it because you need to modify the binding type in the endpoint anyways. Your new endpoint should look something like this:
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="WsBindingConfig" contract="IISJunkService.IService1">
You will notice that the bindingand bindingConfiguration attributes have been modified.
Lastly with the service we can modify the serviceMetadata nodes to enable httpsGet for discovery of our service. My node has been modified to look like this:
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
Client SSL
Now that we have our service all set to talk over SSL let’s do the same for the client. Open the client’s app.config. We will need to change the bindings and endpoints in the client configuration just like we did the server. Your new binding config should look like this:
<wsHttpBinding> <binding name="WSHttpBinding_IService1"> <security mode="Transport"> <transport clientCredentialType="None"/> </security> </binding> </wsHttpBinding>
And your endpoint will look like this:
<endpoint address="https://server/JunkService/Service.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="JunkService.IService1" name="BasicHttpBinding_IService1" />
That should be it. Give your client a compile and run it. You should see the same result as before except now it’s talking over a secure channel.
Authentication
Up to this point we have created a WCF service, created a client to use the service, and secured the service’s communication. The last step is to force the client to authenticate with the service so that any-old client can’t just talk to our precious service. Our service can be secured in a number of ways including, username/password, windows, certificates, and others. The three I called out are probably the most common ways to secure a service, each with its own drawbacks. Username/password is probably the easiest to implement, but in the context for which I am developing this service it’s not appropriate. In production the service’s client will actually be another service. Username/password would work in this scenario, except it could be problematic if we have to change the password at a future point. Windows security is nice, but not so much when working across domains. In this case someone from outside the hosted domain will be communicating with the service, eliminating this option. That leaves certificates. Certs will definitely do the job, the problem is they are one of the most complex forms of authentication to setup. However, since the other options are less appropriate, we are stuck with certs.
Enable Certs on the Service
The first thing we have to do is create ourselves some certificates. For this to work we will need at least two certs. One will be installed server-side, the other client-side. The following steps are a summary of Microsoft’s Documentation.
- Create a Root Certificate Authority to use to sign the client/server certs.
makecert -n "CN=RootCaClientTest" -r -sv RootCaClientTest.pvk RootCaClientTest.cer
This step creates a certificate named RootCaClientTest.cer and a private key file named RootCaClientTest.pvk.
- Create a Certificate Revocation List File from the Root Certificate
makecert -crl -n "CN=RootCaClientTest" -r -sv RootCaClientTest.pvk RootCaClientTest.crl
- Install Your Client Root Certificate Authority on the Client and Server Machines
Repeat the following steps on both the client and server machines:- Copy the RootCaClientTest.cer file to the client and server machines.
- Click Start and then click Run.
- In the command line, type MMC and then click OK.
- In the Microsoft Management Console, on the File menu, click Add/Remove Snap-in.
- In the Add Remove Snap-in dialog box, click Add.
- In the Add Standalone Snap-in dialog box, select Certificates and then click Add.
- In the Certificates snap-in dialog box, select the Computer account radio button (because the certificate needs to be made available to all users), and then click Next.
- In the Select Computer dialog box, leave the default Local computer: (the computer this console is running on) selected and then click Finish.
- In the Add Standalone Snap-in dialog box, click Close.
- In the Add/Remove Snap-in dialog box, click OK.
- In the left pane, expand the Certificates (Local Computer) node, and then expand the Trusted Root Certification Authorities folder.
- Under Trusted Root Certification Authorities, right-click the Certificatessubfolder, click All Tasks, and then click Import.
- On the Certificate Import Wizard welcome screen, click Next.
- On the File to Import screen, click Browse.
- Browse to the location of the signed root CA RootCaClientTest.cer file copied in Step 1, select the file, and then click Open.
- On the File to Import screen, click Next.
- On the Certificate Store screen, accept the default choice and then click Next.
- On the Completing the Certificate Import Wizard screen, click Finish.
- Install the Certificate Revocation List File on the Server and Client Machines
Repeat the following steps on both the client and server machines:- Copy the RootCaClientTest.crl file to the client and server machines.
- Click Start and then click Run.
- In the command line, type MMC and then click OK.
- In the Microsoft Management Console, on the File menu, click Add/Remove Snap-in.
- In the Add Remove Snap-in dialog box, click Add.
- In the Add Standalone Snap-in dialog box, select Certificates and then click Add.
- In the Certificates snap-in dialog box, select the Computer account radio button (because the certificate needs to be made available to all users), and then click Next.
- In the Select Computer dialog box, leave the default Local computer: (the computer this console is running on) selected and then click Finish.
- In the Add Standalone Snap-in dialog box, click Close.
- In the Add/Remove Snap-in dialog box, click OK.
- In the left pane, expand the Certificates (Local Computer) node, and then expand the Trusted Root Certification Authorities folder.
- Under Trusted Root Certification Authorities, right-click the Certificatessubfolder, select All Tasks, and then click Import.
- On the Certificate Import Wizard welcome screen, click Next.
- On the File to Import screen, click Browse.
- On the Files of Type screen, select Certificate Revocation List.
- Browse to the location of the signed root CA RootCaClientTest.crl file copied in Step 1, select the file, and then click Open.
- On the File to Import screen, click Next.
- On the Certificate Store screen, accept the default choice and then click Next.
- On the Completing the Certificate Import Wizard screen, click Finish.
- Create and Install Your Temporary Client Certificate
makecert -sk MyKeyName -iv RootCaClientTest.pvk -n "CN=tempClientcert" -ic RootCaClientTest.cer -sr currentuser -ss my -sky signature -pe
In this command:
- -sk specifies the key container name for the certificate. This needs to be unique for each certificate you create.
- -iv specifies the private key file from which the temporary certificate will be created. You need to specify the root certificate private key file name that was created in the previous step and make sure that it is available in the current directory. This will be used for signing the certificate and for key generation.
- -n specifies the key subject name for the temporary certificate. The convention is to prefix the subject name with “CN = ” for “Common Name”.
- -ic specifies the file containing the root CA certificate file generated in the previous step.
- -sr specifies the store location where the certificate will be installed. The default location is currentuser. For certificate authentication, this is the default location that Microsoft Internet Explorer uses for when browsing Web sites that require a client certificate.
- -ss specifies the store name for the certificate. My is the personal store location of the certificate.
- -sky specifies the key type, which could be either signature or exchange. Using signature makes the certificate capable of signing and enables certificate authentication.
- -pe specifies that the private key is generated in the certificate and installed with it in the certificate store. When you double-click the certificate on the General tab, you should see the message “You have a private key that corresponds to this certificate†displayed at the bottom. This is a requirement for certificate authentication. If the certificate does not have the corresponding private key, it cannot be used for certificate authentication.
- In the Enter Private Key Password dialog box, enter the password for the root CA private key file specified in Step 2, and then click OK.
Now that we have our certs created and installed we can require client certs on the service. In IIS go back to the Secure Communications dialog and check the Require client certificates radio button.

Now browse to your service. Since you have now required client certificates, you should be prompted to choose a certificate to send to the server. In this prompt you should see your tempClientcert that we just created.

Choose your tempClientcert and click OK. Whoops. Looks like we still have some problems. If you see the following error you are still doing alright with all this.
What this error means is that while you submitted a certificate to the service, the service isn’t configured to accept client certificates. Previously we defined a wsHttpBinding with Transport level security. That specified that we would be running under HTTPS. Contained within that binding is a transport node with an attribute of clientCredentialType. Change the value of this attribute to Certificate:
<transport clientCredentialType="Certificate"/>
Try refreshing your browser and you should see an error-free service welcome page.
Note:
One thing about server security I was confused on was the contents of the server’s config. When I was putting this together I was going through a number of resources that was telling me to put a section like the following into the server config:
<serviceCertificate
findValue=”35 84 6c 21 74 f6 69 81 4b 4d b9 b8 7d 0a 8f 2c”
storeLocation=”LocalMachine”
storeName=”My”
x509FindType=”FindBySerialNumber” />
Based on this, I thought that the server needed to specify a certificate in the same way as the client did in order to securely communicate.
During my development I was trying to gain a better understanding of what was going on with all this certificate security stuff. One thing I did was to remove the serviceCertificate node and see if the client would no longer be able to communicate with the service. To my surprise, everything still worked. So the service doesn’t need this configuration?
It turns out this does not need to be specified when running under SSL, which makes logical sense. The SSL certificate is the server’s certificate. By specifying transport security in a wsHttpBinding there is not a need for this explicit certificate definition on the server side. This Microsoft Documentation is a great resource when configuring WCF services this way.
The reason why some of the documentation and blog posts were having me define this configuration was because the examples were not communicating over SSL. Secure services configured without SSL is outside the scope of this post.
Enable Certs on the Client
If you try running your client application now you should receive a 403 error. To fix this we are going to need to modify the client app.config to instruct it to load and pass a certificate to the service.
The first thing to do is to add a new behaviors node:
<system.serviceModel> <behaviors> <endpointBehaviors> <behavior name="CustomBehavior"> <clientCredentials> <clientCertificate findValue="tempClientcert" storeLocation="CurrentUser" storeName="My" x509FindType="FindBySubjectName" /> <serviceCertificate> <authentication certificateValidationMode="ChainTrust"/> </serviceCertificate> </clientCredentials> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel>
This node will tell the client where to load the client certificate from. In this case we are loading from the local user certificate store and looking the cert up by its x509 name.
Now that we have that node in place we need to tell our end point to use this certificate. You will notice that we have named this behavior CustomBehavior. We can use that name to add a new attribute to our endpoint. The endpoint should now look like this:
<endpoint address="https://server/JunkService/Service.svc" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1" contract="JunkService.IService1" name="BasicHttpBinding_IService1" behaviorConfiguration="CustomBehavior" />
Lastly, we need to tell the client that we will be using Client authentication. Previously we were not using any authentication. On the transport node simply change the clientCredentialType attribute to Certificate from None.
You can now give your client a whirl and see if everything is communicating. If you see the proper result, all is well in WCF land. You can also check to make sure everything is working by hitting your service with a browser. If you see the service’s welcome page you know everything is working properly.
Machine-to-Machine
Up to this point we have everything running on a single machine, which is also the host for our service. The point of using a service is to allow client machines to communicate with the service remotely. The next steps will allow us to get another machine up and running with our client application. These next steps assume that you have found a client machine to use to test this out on. If not I’ll wait while you find one…
Ok, now that you have a client machine, make sure it can talk to your server. A simple ping test should do. Once you have the machines talking, using a browser, browse to the service from the client machine. If everything is configured properly, at this point you will recieve a 403 error with the message The page requires a client certificate. This error is good. It indicates we need to give the browser a certificate to authenticate with.
At this point, jump back to your server. We need to use the Certificate Authority we created to create a client cert we can give to the client. The last step we took while creating the certificate authority, revocation list, and cert was to create the cert. We can now export that cert and install it on our remote machine.
Jump back to the mmc console that you were using to create your certificate authority. If you browse to Certificates – Current Owner\Personal\Certificatesyou should see the certificate you created named tempClientcert (if not hit F5 to refresh). Right click on this certificate and choose All Tasks > Export. Walk through the wizard and select Yes, export the private key when asked. The rest of the defaults should be fine. You can then name your export file with a .pfx extension.
Once you cert has been exported get it moved over to your client machine. One other thing you also need to have installed on the client machine is the Certificate Authority we created. During this process you created a file named RootCaClientTest.cer. Move this file over to the client machine as well. Once both of these files are there, Import the Certificate Authority into the machine’s certificate store:
- Click Start and then click Run.
- In the command line, type MMC and then click OK.
- In the Microsoft Management Console, on the File menu, click Add/Remove Snap-in.
- In the Add Remove Snap-in dialog box, click Add.
- In the Add Standalone Snap-in dialog box, select Certificates and then click Add.
- In the Select Computer dialog box, leave the default Local computer: (the computer this console is running on) selected and then click Finish.
- In the Add Standalone Snap-in dialog box, click Close.
- In the Add/Remove Snap-in dialog box, click OK.
- In the left pane, expand the Certificates (Local Computer) node, and then expand the Trusted Root Certification Authorities folder.
- Under Trusted Root Certification Authorities, right-click the Certificatessubfolder, click All Tasks, and then click Import.
- On the Certificate Import Wizard welcome screen, click Next.
- On the File to Import screen, click Browse.
- Browse to the location of the signed root CA RootCaClientTest.cer file copied in Step 1, select the file, and then click Open.
- On the File to Import screen, click Next.
- On the Certificate Store screen, accept the default choice and then click Next.
- On the Completing the Certificate Import Wizard screen, click Finish.
Now you can import the certificate. The easiest way to import the certificate into the client machine’s certificate store is to:
- Open Internet Explorer
- Choose Tools > Internet Options
- Select the Content tab
- Click the Certificates button.
- Click the Import button
- Choose the .pfx file that you just created
- Once you finish with the wizard, click the Clear SSL state button under the Content tab
Now if you try to access your service via a web browser (keeping your fingers crossed) you should see the service welcome page. Once you see your service welcome page you can move your client application to the client machine and give it a try. Just make sure to check your settings to be sure you are not pointing to localhost for your service reference, but to the proper machine name.
You have now created a service with transport security that uses certificate authentication and a client that communicates with your service. You may now take a break for milk and cookies. You deserve it!
Repeat the following steps on both the client and server machines:
- Copy the RootCaClientTest.cer file to the client and server machines.
- Click Start and then click Run.
- In the command line, type MMC and then click OK.
- In the Microsoft Management Console, on the File menu, click Add/Remove Snap-in.
- In the Add Remove Snap-in dialog box, click Add.
- In the Add Standalone Snap-in dialog box, select Certificates and then click Add.
- In the Certificates snap-in dialog box, select the Computer account radio button (because the certificate needs to be made available to all users), and then click Next.
- In the Select Computer dialog box, leave the default Local computer: (the computer this console is running on) selected and then click Finish.
- In the Add Standalone Snap-in dialog box, click Close.
- In the Add/Remove Snap-in dialog box, click OK.
- In the left pane, expand the Certificates (Local Computer) node, and then expand the Trusted Root Certification Authorities folder.
- Under Trusted Root Certification Authorities, right-click the Certificatessubfolder, click All Tasks, and then click Import.
- On the Certificate Import Wizard welcome screen, click Next.
- On the File to Import screen, click Browse.
- Browse to the location of the signed root CA RootCaClientTest.cer file copied in Step 1, select the file, and then click Open.
- On the File to Import screen, click Next.
- On the Certificate Store screen, accept the default choice and then click Next.
- On the Completing the Certificate Import Wizard screen, click Finish.
Working with Streams
Since I can never remember how to work with Streams, here’s an example of reading and writing to a memory stream.
[TestMethod]
public void GetPostTEDDelimted_Test()
{
DateTime startDate = DateTime.Parse("01/01/1900");
DateTime endDate = DateTime.Parse("01/01/1900");
IList<string> postTEDData = this.businessFacade.GetData();
Assert.AreEqual(102, postTEDData.Count);
System.IO.MemoryStream ms = null;
System.IO.TextWriter writer = null;
System.IO.TextReader reader = null;
LumenWorks.Framework.IO.Csv.CsvReader csvReader = null;
int counter = 0;
try
{
ms = new System.IO.MemoryStream();
writer = new System.IO.StreamWriter(ms);
reader = new System.IO.StreamReader(ms);
foreach (string record in postTEDData)
{
writer.WriteLine(record);
}
writer.Flush();
ms.Seek(0, System.IO.SeekOrigin.Begin);
csvReader = new LumenWorks.Framework.IO.Csv.CsvReader(reader, false, ',');
while (csvReader.ReadNextRecord())
{
counter++;
}
}
finally
{
if (writer != null) writer.Close();
if (reader != null) reader.Close();
if (ms != null) ms.Close();
}
Assert.AreEqual(102, counter);
Assert.AreEqual(100, csvReader.FieldCount);
}
ImageMagick
I was trying to upload images to WordPress today and hit the php max upload size per image since my new camera is 12MP. Rather than increase the upload size and wait forever to send my pics up, I decided to look for a nice Linux tool for image resizing. Low and behold I have one installed, ImageMagick. I’ve never used this tool before, but some quick investigation tells me that there are APIs for all the popular programming languages plus a command line interface. I know it’s been around for a while, but it’s new to me and it dominates. I’m going to use this post to keep track of common commands I use with this program and how I’ve used the APIs, if it ever gets to that.
convert IMG_0666.JPG -resize 50% OrthoMax.jpg

