MS.StrongNameKeys.cs

    1  using System;
   2  using System.Diagnostics;
   3  using System.Globalization;
   4  using System.Runtime.InteropServices;
   5  using System.Security;
   6  using Microsoft.Win32;
   7  using MS.StrongName.Native;
   8  
   9  namespace MS.StrongName
  10  {
  11       /// <summary>
  12       ///      Managed StrongName API to deal with key pairs, key containers, and public keys
  13       /// </summary>
  14      public static class Keys
  15      {
  16           /// <summary>
  17           ///      Change the default key store for strong name operations
  18           /// </summary>
  19           /// <exception cref="ArgumentOutOfRangeException">
  20           ///      If DefaultKeyStore is set to an unrecognized value
  21           /// </exception>
  22           /// <exception cref="InvalidOperationException">
  23           ///      If the store could not be changed
  24           /// </exception>
  25           /// <exception cref="UnauthorizedAccessException">
  26           ///      If the user does not have permission to change the store
  27           /// </exception>
  28          public static KeyStore DefaultKeyStore
  29          {
  30              get
  31              {
  32                  RegistryKey machineStoreKey = OpenMachineStoreKey(false);
  33                  if(machineStoreKey == null)
  34                      throw new InvalidOperationException(Resources.CouldNotOpenMachineStoreKey);
  35  
  36                  try
  37                  {
  38                      int useMachineStore = (int)machineStoreKey.GetValue(Registry.MachineKeySet, 1);
  39  
  40                      if(useMachineStore == 0)
  41                          return KeyStore.User;
  42                      else
  43                          return KeyStore.Machine;
  44                  }
  45                  finally
  46                  {
  47                      if(machineStoreKey != null)
  48                          machineStoreKey.Close();
  49                  }
  50              }
  51              
  52              set
  53              {
  54                  if(KeyStore.Machine > value || value > KeyStore.User)
  55                      throw new ArgumentOutOfRangeException("defaultStore", value, Resources.InvalidKeyStore);
  56  
  57                  switch(value)
  58                  {
  59                      case KeyStore.Machine:
  60                          SetMachineKeyStoreState(true);
  61                          break;
  62  
  63                      case KeyStore.User:
  64                          SetMachineKeyStoreState(false);
  65                          break;
  66  
  67                      default:
  68                          Debug.Assert(false, "Unknown key store encountered");
  69                          throw new InvalidOperationException(Resources.InvalidKeyStore);
  70                  }
  71  
  72                  return;
  73              }
  74          }
  75  
  76           /// <summary>
  77           ///      Create a public key token from a signed assembly
  78           /// </summary>
  79           /// <exception cref="ArgumentNullException">
  80           ///      If <paramref name="signedAssembly"/> is null
  81           /// </exception>
  82           /// <exception cref="ArgumentException">
  83           ///      If <paramref name="signedAssembly"/> is empty
  84           /// </exception>
  85           /// <exception cref="InvalidOperationException">
  86           ///      If the token could not be generated
  87           /// </exception>
  88           /// <param name="signedAssembly">assembly to generate the token of</param>
  89           /// <returns>public key token of <paramref name="signedAssembly"/></returns>
  90          public static byte[] CreateTokenFromAssembly(string signedAssembly)
  91          {
  92              if(signedAssembly == null)
  93                  throw new ArgumentNullException("signedAssembly");
  94              if(String.IsNullOrEmpty(signedAssembly))
  95                  throw new ArgumentException(Resources.InvalidAssemblyName, String.Empty);
  96  
  97               // get the key and token
  98              IntPtr blobBuffer = IntPtr.Zero;
  99              IntPtr tokenBuffer = IntPtr.Zero;
 100              
 101              try
 102              {
 103                  int blobSize = 0;
 104                  int tokenSize = 0;
 105  
 106                   // extract the public key token
 107                  if(!NativeMethods.StrongNameTokenFromAssemblyEx(
 108                              signedAssembly,
 109                              out tokenBuffer,
 110                              out tokenSize,
 111                              out blobBuffer,
 112                              out blobSize))
 113                  {
 114                      throw new InvalidOperationException(Utility.GetLastStrongNameError());
 115                  }
 116  
 117                   // copy the token out of unmanaged memory, and return it
 118                  byte[] token = new byte[tokenSize];
 119                  Marshal.Copy(tokenBuffer, token, 0, tokenSize);
 120                  return token;
 121              }
 122              finally
 123              {
 124                  if(blobBuffer != IntPtr.Zero)
 125                      NativeMethods.StrongNameFreeBuffer(blobBuffer);
 126                  if(tokenBuffer != IntPtr.Zero)
 127                      NativeMethods.StrongNameFreeBuffer(tokenBuffer);
 128              }
 129          }
 130  
 131           /// <summary>
 132           ///      Create a public key token from a public key
 133           /// </summary>
 134           /// <exception cref="ArgumentNullException">
 135           ///      If <paramref name="publicKey"/> is null
 136           /// </exception>
 137           /// <exception cref="InvalidOperationException">
 138           ///      If the token could not be generated
 139           /// </exception>
 140           /// <param name="publicKey">public key to generate the token for</param>
 141           /// <returns>public key token of <paramref name="publicKey"/></returns>
 142          public static byte[] CreateTokenFromPublicKey(byte[] publicKey)
 143          {
 144              if(publicKey == null)
 145                  throw new ArgumentNullException("publicKey");
 146              
 147              IntPtr tokenPointer = IntPtr.Zero;
 148              int tokenSize = 0;
 149              
 150               // generate the token
 151              bool createdToken = NativeMethods.StrongNameTokenFromPublicKey(
 152                  publicKey,
 153                  (int)publicKey.Length,
 154                  out tokenPointer,
 155                  out tokenSize);
 156              
 157              try
 158              {
 159                   // if there was a problem, translate it and report it
 160                  if(!createdToken || tokenPointer == IntPtr.Zero)
 161                      throw new InvalidOperationException(Utility.GetLastStrongNameError());
 162  
 163                   // make sure the key size makes sense
 164                  Debug.Assert(tokenSize > 0 && tokenSize <= Int32.MaxValue);
 165                  if(tokenSize <= 0 || tokenSize > Int32.MaxValue)
 166                      throw new InvalidOperationException(Resources.InternalError);
 167      
 168                   // get the key into managed memory
 169                  byte[] token = new byte[tokenSize];
 170                  Marshal.Copy(tokenPointer, token, 0, tokenSize);
 171                  return token;
 172              }
 173              finally
 174              {
 175                   // release the token memory
 176                  if(tokenPointer != IntPtr.Zero)
 177                      NativeMethods.StrongNameFreeBuffer(tokenPointer);
 178              }
 179          }
 180          
 181           /// <summary>
 182           ///      Delete a key from a key container
 183           /// </summary>
 184           /// <exception cref="ArgumentNullException">
 185           ///      If <paramref name="keyContainerName"/> is null
 186           /// </exception>
 187           /// <exception cref="InvalidOperationException">
 188           ///      If the key container could not be deleted
 189           /// </exception>
 190           /// <param name="keyContainerName">name of the key container to delete</param>
 191          public static void Delete(string keyContainerName)
 192          {
 193              if(keyContainerName == null)
 194                  throw new ArgumentNullException("keyContainerName");
 195  
 196              if(!NativeMethods.StrongNameKeyDelete(keyContainerName))
 197                  throw new InvalidOperationException(Utility.GetLastStrongNameError());
 198  
 199              return;
 200          }
 201          
 202           /// <summary>
 203           ///      Extract the public key, either from a key container or a key blob
 204           /// </summary>
 205           /// <exception cref="InvalidOperationException">
 206           ///      if the key could not be extracted
 207           /// </exception>
 208           /// <param name="keyContainer">key container to extract from</param>
 209           /// <param name="keyBlob">key blob to extract from</param>
 210          private static byte[] ExtractPublicKey(string keyContainer, byte[] keyBlob)
 211          {
 212               // one, but not both, of the parameters must be null
 213              Debug.Assert(   (keyContainer == null || keyBlob == null) &&
 214                              !(keyContainer == null && keyBlob == null), "Invalid parameters");
 215              
 216               // extract the public key portion of the blob
 217              IntPtr publicKeyBuffer = IntPtr.Zero;
 218              try
 219              {
 220                  int publicKeyBlobSize = 0;
 221                  
 222                  if(!NativeMethods.StrongNameGetPublicKey(
 223                              keyContainer,
 224                              keyBlob,
 225                              (keyBlob == null) ? 0 : keyBlob.Length,
 226                              out publicKeyBuffer,
 227                              out publicKeyBlobSize))
 228                  {
 229                      throw new InvalidOperationException(Utility.GetLastStrongNameError());
 230                  }
 231  
 232                   // copy the key out of unmanaged memory, and return it 
 233                  byte[] publicKeyBlob = new byte[publicKeyBlobSize];
 234                  Marshal.Copy(publicKeyBuffer, publicKeyBlob, 0, publicKeyBlobSize);
 235                  return publicKeyBlob;
 236              }
 237              finally
 238              {
 239                  if(publicKeyBuffer != IntPtr.Zero)
 240                      NativeMethods.StrongNameFreeBuffer(publicKeyBuffer);
 241              }
 242          }
 243  
 244           /// <summary>
 245           ///      Get the public key from an assembly
 246           /// </summary>
 247           /// <exception cref="ArgumentNullException">
 248           ///      If <paramref name="signedAssembly"/> is null
 249           /// </exception>
 250           /// <exception cref="ArgumentException">
 251           ///      If <paramref name="signedAssembly"/> is empty
 252           /// </exception>
 253           /// <exception cref="InvalidOperationException">
 254           ///      If the public key blob could not be extracted
 255           /// </exception>
 256           /// <param name="signedAssembly">assembly to extract from</param>
 257           /// <returns>public key blob the assembly was signed with</returns>
 258          public static byte[] ExtractPublicKeyFromAssembly(string signedAssembly)
 259          {
 260              if(signedAssembly == null)
 261                  throw new ArgumentNullException("signedAssembly");
 262              if(String.IsNullOrEmpty(signedAssembly))
 263                  throw new ArgumentException(Resources.InvalidAssemblyName, String.Empty);
 264  
 265               // get the key and token
 266              IntPtr blobBuffer = IntPtr.Zero;
 267              IntPtr tokenBuffer = IntPtr.Zero;
 268              
 269              try
 270              {
 271                  int blobSize = 0;
 272                  int tokenSize = 0;
 273  
 274                   // extract the public key blob
 275                  if(!NativeMethods.StrongNameTokenFromAssemblyEx(
 276                              signedAssembly,
 277                              out tokenBuffer,
 278                              out tokenSize,
 279                              out blobBuffer,
 280                              out blobSize))
 281                  {
 282                      throw new InvalidOperationException(Utility.GetLastStrongNameError());
 283                  }
 284  
 285                   // copy the key out of unmanaged memory, and return it
 286                  byte[] keyBlob = new byte[blobSize];
 287                  Marshal.Copy(blobBuffer, keyBlob, 0, blobSize);
 288                  return keyBlob;
 289              }
 290              finally
 291              {
 292                  if(blobBuffer != IntPtr.Zero)
 293                      NativeMethods.StrongNameFreeBuffer(blobBuffer);
 294                  if(tokenBuffer != IntPtr.Zero)
 295                      NativeMethods.StrongNameFreeBuffer(tokenBuffer);
 296              }
 297          }
 298  
 299           /// <summary>
 300           ///      Get the public key from a key container
 301           /// </summary>
 302           /// <exception cref="ArgumentNullException">
 303           ///      If <paramref name="keyContainer"/> is null
 304           /// </exception>
 305           /// <exception cref="ArgumentException">
 306           ///      If <paramref name="keyContainer"/> is empty
 307           /// </exception>
 308           /// <exception cref="InvalidOperationException">
 309           ///      If the key could not be extracted
 310           /// </exception>
 311           /// <param name="keyContainer">key container to get the public key from</param>
 312           /// <returns>public key blob</returns>
 313          public static byte[] ExtractPublicKeyFromKeyContainer(string keyContainer)
 314          {
 315              if(keyContainer == null)
 316                  throw new ArgumentNullException(keyContainer);
 317              if(String.IsNullOrEmpty(keyContainer))
 318                  throw new ArgumentException(Resources.InvalidKeyContainer, String.Empty);
 319  
 320              return ExtractPublicKey(keyContainer, null);
 321          }
 322  
 323           /// <summary>
 324           ///      Get the public key from a key pair blob
 325           /// </summary>
 326           /// <exception cref="ArgumentNullException">
 327           ///      If <paramref name="keyPair"/> is null
 328           /// </exception>
 329           /// <exception cref="InvalidOperationException">
 330           ///      If the key could not be extracted
 331           /// </exception>
 332           /// <param name="keyPair">key blob to get the public key from</param>
 333           /// <returns>public key blob</returns>
 334          public static byte[] ExtractPublicKeyFromKeyPair(byte[] keyPair)
 335          {
 336              if(keyPair == null)
 337                  throw new ArgumentNullException("keyPair");
 338  
 339              return ExtractPublicKey(null, keyPair);
 340          }
 341          
 342           /// <summary>
 343           ///      Generate a key pair blob
 344           /// </summary>
 345           /// <exception cref="ArgumentOutOfRangeException">
 346           ///      If <paramref name="keySize"/> is not positive
 347           /// </exception>
 348           /// <exception cref="InvalidOperationException">
 349           ///      If the key could not be generated
 350           /// </exception>
 351           /// <param name="keySize">size, in bits, of the key to generate</param>
 352           /// <returns>generated key pair blob</returns>
 353          public static byte[] GenerateKeyPair(int keySize)
 354          {
 355              if(keySize <= 0)
 356                  throw new ArgumentOutOfRangeException("keySize", keySize, Resources.InvalidKeySize);
 357              
 358               // variables that hold the unmanaged key
 359              IntPtr keyBlob = IntPtr.Zero;
 360              long generatedSize = 0;
 361  
 362               // create the key
 363              bool createdKey = NativeMethods.StrongNameKeyGenEx(
 364                      null, 
 365                      StrongNameKeyGenFlags.None,
 366                      (int)keySize,
 367                      out keyBlob,
 368                      out generatedSize);
 369              
 370              try
 371              {
 372                   // if there was a problem, translate it and report it
 373                  if(!createdKey || keyBlob == IntPtr.Zero)
 374                      throw new InvalidOperationException(Utility.GetLastStrongNameError());
 375  
 376                   // make sure the key size makes sense
 377                  Debug.Assert(generatedSize > 0 && generatedSize <= Int32.MaxValue);
 378                  if(generatedSize <= 0 || generatedSize > Int32.MaxValue)
 379                      throw new InvalidOperationException(Resources.InternalError);
 380      
 381                   // get the key into managed memory
 382                  byte[] key = new byte[generatedSize];
 383                  Marshal.Copy(keyBlob, key, 0, (int)generatedSize);
 384                  return key;
 385              }
 386              finally
 387              {
 388                   // release the unmanaged memory the key resides in
 389                  if(keyBlob != IntPtr.Zero)
 390                      NativeMethods.StrongNameFreeBuffer(keyBlob);
 391              }
 392          }
 393  
 394           /// <summary>
 395           ///      Install a key into a key container
 396           /// </summary>
 397           /// <param name="keyBlob">Key pair blob</param>
 398           /// <param name="keyContainerName">Name of the key container to install the keys into</param>
 399          public static void InstallKey(byte[] keyBlob, string keyContainerName)
 400          {
 401              if(keyBlob == null)
 402                  throw new ArgumentNullException("keyBlob");
 403              if(keyContainerName == null)
 404                  throw new ArgumentNullException("keyContainerName");
 405              if(String.IsNullOrEmpty(keyContainerName))
 406                  throw new ArgumentException(String.Format(
 407                              CultureInfo.CurrentCulture,
 408                              Resources.InvalidKeyContainer,
 409                              String.Empty));
 410              
 411  
 412              if(!NativeMethods.StrongNameKeyInstall(keyContainerName, keyBlob, keyBlob.Length))
 413                  throw new InvalidOperationException(Utility.GetLastStrongNameError());
 414              
 415              return;
 416          }
 417  
 418           /// <summary>
 419           ///      Set the state of the flag indicating if the strong
 420           ///      name APIs will be using the machine key store or the user
 421           ///      key store
 422           /// </summary>
 423           /// <param name="enabled">true to use the machine key store, false to use the user key store</param>
 424          private static void SetMachineKeyStoreState(bool enabled)
 425          {
 426              RegistryKey machineStoreKey = OpenMachineStoreKey(true);
 427  
 428              try
 429              {
 430                  if(machineStoreKey == null)
 431                      throw new InvalidOperationException(Resources.CouldNotOpenMachineStoreKey);
 432  
 433                  int flag = enabled ? 1 : 0;
 434                  machineStoreKey.SetValue(Registry.MachineKeySet, flag, RegistryValueKind.DWord);
 435              }
 436              finally
 437              {
 438                  if(machineStoreKey != null)
 439                      machineStoreKey.Close();
 440              }
 441  
 442              return;
 443          }
 444  
 445           /// <summary>
 446           ///      Open the machine store registry key
 447           /// </summary>
 448           /// <param name="writable">Open the key for writing</param>
 449           /// <returns>machine store key, null on error</returns>
 450          private static RegistryKey OpenMachineStoreKey(bool writable)
 451          {
 452              RegistryKey machineStoreKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(
 453                      Registry.ConfigurationKey,
 454                      writable);
 455  
 456               // if the key didn't exist, and we're opening for write, create it
 457              if(machineStoreKey == null && writable)
 458                  machineStoreKey = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(Registry.ConfigurationKey);
 459                      
 460              return machineStoreKey;
 461          }
 462      }
 463  }