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  }