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 }