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 }