Adventures in Strong Names with Enterprise Library
At my current client we are using Enterprise Library. We’ve used the previous two releases of the library (versions 1.0/Jan. 2005 and 1.1/June 2005), but we had not strong named the assemblies. Each application that went out had it’s own references to the framework assemblies. Actually, the client had not been using strong names for any of their assemblies.
With the client looking at rolling out new applications based on .Net 2.0 it was decided that the custom framework pieces we had extended from the Enterprise Library would also be updated to .Net 2.0. This meant starting to use the Enterprise Library 2.0/Jan. 2006 edition and updating all the custom extensions to .Net 2.0 and EntLib 2.0 compatible. We also decided to start strong naming all of the EntLib assemblies and custom framework pieces and deploy those to the GAC.
I have been doing the component update over the last two or three weeks and today I tackled adding the strong name to the assemblies. I am very familiar with strong naming in theory since I studied it quite heavily for on my certification exams; however, I have to be honest in saying I had not done more than try it out while studying. So, I took 30 minutes to refresh my knowledge of the Strong Naming tool (sn.exe) and jumped right in. I ran into several issues while doing this, so I thought I’d document them here for everyone else that may hit the same issues.
First of all, let me say that Tom Hollander of Patterns and Practices fame somewhat documented the process in a comment he made to his own blog; however, I found there were a few steps he left out and something I decided to do differently.
Steps to strong name Enterprise Library:
- Create a strong name key This is the easy part. I opened up a VS.Net 2005 command prompt and went to the directory I was going to store the strong name key in. In my case, this was the directory directly above all of the EntLib code and custom extensions source. It just happened to be where I kept my solution file for the entire solution (which contained all the EntLib code and my custom stuff), but you can basically put this anywhere you want really.
In the command prompt I typed:
sn -k MyKeyFile.snk
This created a strong name key pair (both public and private in the same file). This is a very important and potentially dangerous file since it is used to authenticate your code. To learn more about what this file is for and how it all works check out this MSDN article.
- We had decided that regular developers didn’t need to have access to the full strong name key, so we used the Strong Name tool to extract the public key only. Again in the on the same command line window I typed:
sn -p MyKeyFile.snk MyPublicKeyFile.snk
This extracted the public key only and put it in the MyPublicKeyFile.snk file. This is what we will use for now. The MyKeyFile.snk file was/will be placed in a very safe place (read locked down share) that only certain managers had access to. Regular developers don’t need that file.
- While we were at the command prompt we went ahead and got the public key value (not the public key token) from the public key file. To do this you again use the Strong Name tool. Basically I ran the following command line and then copied the full string (it’s pretty long) out to a notepad file for right now. We’ll use it later.
sn -tp MyPublicKeyFile.snk
Okay, this part has been really easy and should take just minutes. Now the fun part begins. The next step is to set each project to use the strong name key. This will take you a while. In Tom Hollander’s instructions (see link above) he states that you open up the VS.Net solution you are using for EntLib (I actually had my own solution since I was including my own custom extensions as well) and then go project by project and set the project properties to use the strong name key. That’s pretty much correct, but this is where I took a different route.
-
Open up the VS.Net 2005 solution for EntLib (or a solution you are working with that contains all of the Enterprise Library source code). For each EntLib project (including all unit test projects) in the solution and any custom components you may have you can do the following steps:
- Right click on the project file and Select Add Existing Item.
- When the open dialog box appears browse to the location of the MyPublicKeyFile.snk file you created.
- Click on the MyPublicKeyFile.snk in the dialog box, but instead of just hitting the add button or double clicking the file to add the file (which, by the way will automatically copy that file to that project’s directory), there is a drop down arrow on the right side of the Add button. Select that dropdown and click Add as Link. This will leave the file where it’s at and create a link in the project file to the strong name key file. This allows you have ONE copy of your public strong name key file instead of over forty of them scattered over all the project directories.
- Now we take the next step which is to set the strong name key file. Double click the project properties node and then select the “Signing” tab. Click the radio button to strong name sign and in the dropdown there should be your strong name key listed. Note that at first this will show a full path to the file, but it gets saved as a referential path. Also select the Delayed Signed option.
- NOTE: Unlike VS.Net 2003 these options are saved to the project file and not written to the Assembly Info file. Interesting difference and important if you plan on using some other tool than MSBuild/VS.Net 2005 to compile your solution.
-
Click Save for the project.
-
Now if you compile now you’ll get some interesting errors:
Friend assembly reference 'Microsoft.Practices.EnterpriseLibrary.xxxx' is invalid.
Strong-name signed assemblies must specify a public key in their InternalsVisibleTo declarations.
Hmmm, interesting. This comes from several of the EntLib assemblies (and in my case the custom extensions) allows the unit test assemblies access to their internals so that unit tests could be performed. The attribute used to do that is the InternalsVisibleTo attribute. Tom indicates how to fix this, as well.
Do a search using Find in Files (over the entire solution) looking for InternalsVisibleTo. There are quite a few of them, but for each one you’ll need to add the public key string (not the public token). Remember that really long string of numbers you copied to the notepad window after using Sn -tp
? That’s the one. You add to the attribute like this example:
[assembly: InternalsVisibleTo("Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Tests, PublicKey=29384383893933..... <reallylongstringofnumbers....>")]
This is the point that Tom states to save all your changed files and rebuild and while that works I found some issues later. If you rebuild now everything should compile and you’d be good to go; however, now that you’ve added a strong name to all of the assemblies none of the unit tests in Enterprise Library (or your own if you strong name your extensions) will most likely work. They all have configuration that is keyed to an assembly that had no strong name. So, to fix that do the following:
- Compile just one of the assemblies (say, ObjectBuilder since it doesn’t reference anything). Now take a look at this assembly using ILDASM, Reflector or even the shell extensions for .Net Assemblies. There are many ways to locate the public key token.
- Do another Find in Files across the whole solution looking for PublicKeyToken=null. You should find several .config files and a .resex file with many of these in it.
- For each of these locations in the config files you’ll need to add the public token (not the public key which is the really long string above) to get the unit tests to work. So, for example, instead of:
…., PublicKeyToken=null"…
You’ll have:
…., PulicKeyToken=b77a5c516934e089"…
Obviously you use the public key token you created, not the one listed above.
Now it’s time to save all the changed files and then run a complete rebuild of the solution. Once this is done you’ll need to run the batch file that copies all of the generated assemblies to the bin directory of Enterprise Library (where the config tool resides). You’ll also need to copy any of your extensions to this directory that also work with the configuration tool.
One thing you may run into is that when you attempt to reference one of these aseemblies after they’re signed, or if you attempt to load them into the GAC you will get an error saying you can’t do this since the assembly has either been tampered with or is only partially signed. To get around this you’ll have to tell your machine to skip the verification for these files. You can do this by using the following commandline:
sn -Vr *,b77a5c516934e089
Again, using your own public key token. This tells your machine to ignore validation (or skip it) for any file that has the provided public key token. Now note, this is a security risk in that now your machine will not validate assemblies that have this public token. If your assemblies are exposed to the internet or 3rd parties it is VERY easy to get the public token (you just did it above) and so be careful with this. This is an operation only suitable for developer machines in my opinion. You can use the following command to remove public tokens from the skip list by using the sn -Vu
command. You can also specify specific files to be skipped as well for more security. See the Strong Name tool help for more information. Note that if VS.Net 2005 was open while you were adding assemblies or a public key token to be skipped you’ll have to close and reopen VS.Net in order for the change to be picked up. Otherwise the IDE will keep giving you the error at compile time.
Okay, with all of that you should be good. Oh, and remember that really secret MyKeyFile.snk file we locked away? Yeah, you use that when you are completely done with development on your extensions/EntLib changes and you’re ready to ship it off to Test or Production. One of the people that have access to it will simply us the Strong Name tool again to fully sign the assembly.
sn -R Microsoft.Practices.EnterpriseLibrary.Logging.dll MyKeyFile.snk
Do that for each file that needs to be fully signed and you’re ready to go. For ease of use you may create a batch file to do this so the person doing the signing doesn’t strain their fingers. Or you can use the command line For command to make this much simpler.
So there you have it. Hopefully this will help some people. The things that tripped me up were the public key vs public key token distinction when using InternalsVisibleTo (that’s where I found Tom’s comment) and the failing of all the unit tests due to the configuration files having the references to the non signed assemblies in them.
It would be nice if there was a tool that would rip through all of the project files and add the necessary attributes in XML for the linked strong name file, use of the strong name file and delayed signing attribute. If this tool existed and correctly applied the relative path from the project file to the location of the public key file then this would cust off probably an hour of opening every project, setting these properties and saving them. Maybe another day….maybe EntLib 3.0 will have that.