(updated 12/3/04, pointed to the newly refactored source)
It's been nearly two months since the last update to my managed sn.exe port, so its long-past overdue for some new features. This update implements the various key container features that are exposed via sn.exe. Namely, three more Modes have been implemented:
None of the strong name APIs exposed are very complex, so this update should be relatively easy. Lets look at them one at a time.
The StrongNameKeyInstall API installs a .snk key into a key container on the local machine. From MS.StrongName\Native\NativeMethods.cs we can see that this API requires three parameters:
This method is fairly self-explanatory. The name of the key container to install into is passed into wszKeyContainer, and the key that needs to be installed is given through pkKeyBlob and cbKeyBlob. StrongNameKeyInstall returns true on success and false on failure. The function GetLastStrongNameError() found in can be used to get an error string if StrongNameKeyInstall fails.
Obtaining the data to pass into pkKeyBlob is also easy, since .snk files are raw dumps of key blobs. All that has to be done is read the .snk file directly from disk and pass that data into pkKeyBlob. The actual file reading is done in KeyFile::ReadKeyFile(), in MS.StrongName\KeyFile.cs. The code that actually implements the -i command is found in two places, both in the new msn\KeyContainer.cs file. KeyContainer::OnInstallKeyPair() interprets the command line parameters, validates them, reads in the key file, and then calls to KeyContainer::InstallKey() which does the actual work involved. InstallKey basically just boils down to the P/Invoke call into StrongNameInstallKey, with some error detection code included.
StrongNameKeyDelete, defined in MS.StrongName\Native\NativeMethods.cs is the counterpart of StrongNameKeyInstall, and is even easier to use. It takes only one parameter:
Which is the name of the key container to delete. Implementation of the -d command is found with the -i implementation in msn\Commands.cs. The entire implementation can be found in KeyContainer::OnDeleteKeyPair(). This method simply validates its arguments, then passes them onto StrongNameKeyDelete and displays the appropriate success / error message.
The -m command deals with which key store the other commands will operate upon. It has no corresponding strong name API behind it, instead -m is implemented by checking the DWORD flag in HKLM\Software\Microsoft\StrongKey\MachineKeySet. Since there are several things that the sn.exe tool does that rely on reading the registry directly, I've created a MS.StrongName\Registry.cs file which contains the names of the keys and values, so that they don't get hard coded throughout the msn.exe tool. The possible values of MachineKeySet are:
-m has two modes, inspection and setting. In inspection mode, no additional parameters are passed to the command, and it simply displays if the current mode is to use the user key store or the machine key store. This is done by the KeyContainer::DisplayKeySore method in msn\KeyContainer.cs. DisplayKeyStore first tries to open the registry key for reading by calling the KeyContainer::OpenMachineStore() utility method. It then checks and reports the value of the MachineKeySet value under this key.
When -m is given a parameter (either y or n), it enters setting mode. This mode will either switch the strong name APIs to use the machine store (if passed y), or the user store (if passed n). Setting mode is implemented in the KeyContainer::SetMachineKeyStoreState() method in MS.StrongName\Keys.cs. This uses the OpenMachineStoreKey method to open the registry key for writing if it exists, and if it doesn't exist create it. Once the registry key is created, it's set to the appropriate value, and KeyContainer::OnMachineKeyStore() falls through into a call to DisplayKeyStore so that the user can see the effect of their call.
I did some cleanup this time, changing some code layout to use shorter lines, and pulling some common functions to the internal Utility class defined in msn\Utility.cs. I also changed error output to go to Console.Error instead of Console.Out. The complete changelist is: