2 using System.Diagnostics;
3 using System.Diagnostics.CodeAnalysis;
5 using System.Runtime.InteropServices;
6 using System.Security.Permissions;
7 using Microsoft.Win32.SafeHandles;
9 namespace System.Security.Cryptography
11 public sealed class RSACng : RSA
14 public RSACng() : this(2048) { }
16 public RSACng(int keySize)
18 throw new NotImplementedException ();
22 public RSACng(CngKey key)
24 throw new NotImplementedException ();
30 [SecuritySafeCritical]
33 throw new NotImplementedException ();
38 throw new NotImplementedException ();
42 public override RSAParameters ExportParameters(bool includePrivateParameters)
44 throw new NotImplementedException();
47 public override void ImportParameters(RSAParameters parameters)
49 throw new NotImplementedException();
53 // See https://msdn.microsoft.com/en-us/library/windows/desktop/bb931354(v=vs.85).aspx
54 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(512, 16384, 64) };
56 // CngKeyBlob formats for RSA key blobs
57 private static CngKeyBlobFormat s_rsaFullPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaFullPrivateBlob);
58 private static CngKeyBlobFormat s_rsaPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPrivateBlob);
59 private static CngKeyBlobFormat s_rsaPublicBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPublicBlob);
65 /// Create an RSACng algorithm with a random 2048 bit key pair.
67 public RSACng() : this(2048) { }
70 /// Creates a new RSACng object that will use a randomly generated key of the specified size.
71 /// Valid key sizes range from 384 to 16384 bits, in increments of 8. It's suggested that a
72 /// minimum size of 2048 bits be used for all keys.
74 /// <param name="keySize">Size of the key to generate, in bits.</param>
75 /// <exception cref="CryptographicException">if <paramref name="keySize" /> is not valid</exception>
76 public RSACng(int keySize)
78 LegalKeySizesValue = s_legalKeySizes;
83 /// Creates a new RSACng object that will use the specified key. The key's
84 /// <see cref="CngKey.AlgorithmGroup" /> must be Rsa.
85 /// CngKey.Open creates a copy of the key. Even if someone disposes the key passed
86 /// copy of this key object in RSA stays alive.
88 /// <param name="key">Key to use for RSA operations</param>
89 /// <exception cref="ArgumentException">if <paramref name="key" /> is not an RSA key</exception>
90 /// <exception cref="ArgumentNullException">if <paramref name="key" /> is null.</exception>
91 [SecuritySafeCritical]
92 public RSACng(CngKey key)
96 throw new ArgumentNullException("key");
98 if (key.AlgorithmGroup != CngAlgorithmGroup.Rsa)
100 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "key");
102 LegalKeySizesValue = s_legalKeySizes;
103 Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
107 /// Gets the key that will be used by the RSA object for any cryptographic operation that it uses.
108 /// This key object will be disposed if the key is reset, for instance by changing the KeySize
109 /// property, using ImportParamers to create a new key, or by Disposing of the parent RSA object.
110 /// Therefore, you should make sure that the key object is no longer used in these scenarios. This
111 /// object will not be the same object as the CngKey passed to the RSACng constructor if that
112 /// constructor was used, however it will point at the same CNG key.
114 /// <permission cref="SecurityPermission">
115 /// SecurityPermission/UnmanagedCode is required to read this property.
119 [SecuritySafeCritical]
122 // If our key size was changed from the key we're using, we need to generate a new key
123 if (_key != null && _key.KeySize != KeySize)
129 // If we don't have a key yet, we need to generate a random one now
132 CngKeyCreationParameters creationParameters = new CngKeyCreationParameters()
134 ExportPolicy = CngExportPolicies.AllowPlaintextExport,
137 CngProperty keySizeProperty = new CngProperty(NCryptNative.KeyPropertyName.Length,
138 BitConverter.GetBytes(KeySize),
139 CngPropertyOptions.None);
140 creationParameters.Parameters.Add(keySizeProperty);
141 _key = CngKey.Create(CngAlgorithm.Rsa, null, creationParameters);
149 Debug.Assert(value != null, "value != null");
150 if (value.AlgorithmGroup != CngAlgorithmGroup.Rsa)
152 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "value");
154 // If we already have a key, clear it out
162 // Our LegalKeySizes value stores the values that we encoded as being the correct
163 // legal key size limitations for this algorithm, as documented on MSDN.
165 // But on a new OS version we might not question if our limit is accurate, or MSDN
166 // could have been innacurate to start with.
168 // Since the key is already loaded, we know that Windows thought it to be valid;
169 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
172 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
173 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
174 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
175 // alignment requirement. (In both cases Windows loads it just fine)
176 KeySizeValue = _key.KeySize;
181 /// Helper property to get the NCrypt key handle
183 private SafeNCryptKeyHandle KeyHandle
185 [SecuritySafeCritical]
186 get { return Key.Handle; }
189 protected override void Dispose(bool disposing)
191 if (disposing && _key != null)
197 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
199 // we're sealed and the base should have checked this already
200 Debug.Assert(data != null);
201 Debug.Assert(offset >= 0 && offset <= data.Length);
202 Debug.Assert(count >= 0 && count <= data.Length);
203 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
205 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
207 hasher.HashCore(data, offset, count);
208 return hasher.HashFinal();
212 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
214 // We're sealed and the base should have checked these alread.
215 Debug.Assert(data != null);
216 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
218 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
220 hasher.HashStream(data);
221 return hasher.HashFinal();
227 /// This function checks the magic value in the key blob header
229 /// <param name="includePrivateParameters">Private blob if true else public key blob</param>
230 private void CheckMagicValueOfKey(int magic, bool includePrivateParameters)
232 if (false == includePrivateParameters)
234 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPublic)
236 //Check for Private key magic as public key can be derived from private key blob
237 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
239 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPublicOrPrivateKey));
243 //If includePrivateParameters is true then certainly check for the private key magic
246 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
248 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPrivateKey));
254 // Key import and export
258 /// Exports the key used by the RSA object into an RSAParameters object.
260 [SecuritySafeCritical]
261 public override RSAParameters ExportParameters(bool includePrivateParameters)
263 byte[] rsaBlob = Key.Export(includePrivateParameters ? s_rsaFullPrivateBlob : s_rsaPublicBlob);
264 RSAParameters rsaParams = new RSAParameters();
267 // We now have a buffer laid out as follows:
268 // BCRYPT_RSAKEY_BLOB header
269 // byte[cbPublicExp] publicExponent - Exponent
270 // byte[cbModulus] modulus - Modulus
271 // -- Private only --
272 // byte[cbPrime1] prime1 - P
273 // byte[cbPrime2] prime2 - Q
274 // byte[cbPrime1] exponent1 - DP
275 // byte[cbPrime2] exponent2 - DQ
276 // byte[cbPrime1] coefficient - InverseQ
277 // byte[cbModulus] privateExponent - D
279 byte[] tempMagic = new byte[4];
280 tempMagic[0] = rsaBlob[0]; tempMagic[1] = rsaBlob[1]; tempMagic[2] = rsaBlob[2]; tempMagic[3] = rsaBlob[3];
281 int magic = BitConverter.ToInt32(tempMagic, 0);
282 //Check the magic value in key blob header. If blob does not have required magic
283 // then it trhows Cryptographic exception
284 CheckMagicValueOfKey(magic, includePrivateParameters);
288 fixed (byte* pRsaBlob = rsaBlob)
290 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
292 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
294 // Read out the exponent
295 rsaParams.Exponent = new byte[pBcryptBlob->cbPublicExp];
296 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Exponent, 0, rsaParams.Exponent.Length);
297 offset += pBcryptBlob->cbPublicExp;
299 // Read out the modulus
300 rsaParams.Modulus = new byte[pBcryptBlob->cbModulus];
301 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Modulus, 0, rsaParams.Modulus.Length);
302 offset += pBcryptBlob->cbModulus;
304 if (includePrivateParameters)
307 rsaParams.P = new byte[pBcryptBlob->cbPrime1];
308 Buffer.BlockCopy(rsaBlob, offset, rsaParams.P, 0, rsaParams.P.Length);
309 offset += pBcryptBlob->cbPrime1;
312 rsaParams.Q = new byte[pBcryptBlob->cbPrime2];
313 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Q, 0, rsaParams.Q.Length);
314 offset += pBcryptBlob->cbPrime2;
317 rsaParams.DP = new byte[pBcryptBlob->cbPrime1];
318 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DP, 0, rsaParams.DP.Length);
319 offset += pBcryptBlob->cbPrime1;
322 rsaParams.DQ = new byte[pBcryptBlob->cbPrime2];
323 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DQ, 0, rsaParams.DQ.Length);
324 offset += pBcryptBlob->cbPrime2;
327 rsaParams.InverseQ = new byte[pBcryptBlob->cbPrime1];
328 Buffer.BlockCopy(rsaBlob, offset, rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
329 offset += pBcryptBlob->cbPrime1;
332 rsaParams.D = new byte[pBcryptBlob->cbModulus];
333 Buffer.BlockCopy(rsaBlob, offset, rsaParams.D, 0, rsaParams.D.Length);
334 offset += pBcryptBlob->cbModulus;
344 /// ImportParameters will replace the existing key that RSACng is working with by creating a
345 /// new CngKey for the parameters structure. If the parameters structure contains only an
346 /// exponent and modulus, then only a public key will be imported. If the parameters also
347 /// contain P and Q values, then a full key pair will be imported.
350 /// <exception cref="ArgumentException">
351 /// if <paramref name="parameters" /> contains neither an exponent nor a modulus.
353 /// <exception cref="CryptographicException">
354 /// if <paramref name="parameters" /> is not a valid RSA key or if <paramref name="parameters"
355 /// /> is a full key pair and the default KSP is used.
357 [SecuritySafeCritical]
358 public override void ImportParameters(RSAParameters parameters)
360 if (parameters.Exponent == null || parameters.Modulus == null)
362 throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidRsaParameters));
364 bool publicOnly = parameters.P == null || parameters.Q == null;
367 // We need to build a key blob structured as follows:
368 // BCRYPT_RSAKEY_BLOB header
369 // byte[cbPublicExp] publicExponent - Exponent
370 // byte[cbModulus] modulus - Modulus
371 // -- Private only --
372 // byte[cbPrime1] prime1 - P
373 // byte[cbPrime2] prime2 - Q
376 int blobSize = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB)) +
377 parameters.Exponent.Length +
378 parameters.Modulus.Length;
381 blobSize += parameters.P.Length +
385 byte[] rsaBlob = new byte[blobSize];
388 fixed (byte* pRsaBlob = rsaBlob)
391 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
392 pBcryptBlob->Magic = publicOnly ? BCryptNative.KeyBlobMagicNumber.RsaPublic :
393 BCryptNative.KeyBlobMagicNumber.RsaPrivate;
395 pBcryptBlob->BitLength = parameters.Modulus.Length * 8;
397 pBcryptBlob->cbPublicExp = parameters.Exponent.Length;
398 pBcryptBlob->cbModulus = parameters.Modulus.Length;
402 pBcryptBlob->cbPrime1 = parameters.P.Length;
403 pBcryptBlob->cbPrime2 = parameters.Q.Length;
406 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
409 Buffer.BlockCopy(parameters.Exponent, 0, rsaBlob, offset, parameters.Exponent.Length);
410 offset += parameters.Exponent.Length;
413 Buffer.BlockCopy(parameters.Modulus, 0, rsaBlob, offset, parameters.Modulus.Length);
414 offset += parameters.Modulus.Length;
419 Buffer.BlockCopy(parameters.P, 0, rsaBlob, offset, parameters.P.Length);
420 offset += parameters.P.Length;
423 Buffer.BlockCopy(parameters.Q, 0, rsaBlob, offset, parameters.Q.Length);
424 offset += parameters.Q.Length;
428 Key = CngKey.Import(rsaBlob, publicOnly ? s_rsaPublicBlob : s_rsaPrivateBlob);
432 // Encryption and decryption
434 [SecuritySafeCritical]
435 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
439 throw new ArgumentNullException("data");
444 throw new ArgumentNullException("padding");
447 SafeNCryptKeyHandle keyHandle = Key.Handle;
449 if (padding == RSAEncryptionPadding.Pkcs1)
451 return NCryptNative.DecryptDataPkcs1(keyHandle, data);
453 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
455 return NCryptNative.DecryptDataOaep(keyHandle, data, padding.OaepHashAlgorithm.Name);
459 // no other padding possibilities at present, but we might version independently from more being added.
460 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
464 [SecuritySafeCritical]
465 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
469 throw new ArgumentNullException("data");
473 throw new ArgumentNullException("padding");
476 if (padding == RSAEncryptionPadding.Pkcs1)
478 return NCryptNative.EncryptDataPkcs1(KeyHandle, data);
480 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
482 return NCryptNative.EncryptDataOaep(KeyHandle, data, padding.OaepHashAlgorithm.Name);
486 // no other padding possibilities at present, but we might version independently from more being added.
487 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
496 [SecuritySafeCritical]
497 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
501 throw new ArgumentNullException("hash");
503 if (String.IsNullOrEmpty(hashAlgorithm.Name))
505 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
509 throw new ArgumentNullException("padding");
512 // Keep a local copy of the key.
514 SafeNCryptKeyHandle keyHandle = key.Handle;
516 if (padding == RSASignaturePadding.Pkcs1)
518 return NCryptNative.SignHashPkcs1(keyHandle, hash, hashAlgorithm.Name);
520 else if (padding == RSASignaturePadding.Pss)
522 return NCryptNative.SignHashPss(keyHandle, hash, hashAlgorithm.Name, hash.Length);
526 // no other padding possibilities at present, but we might version independently from more being added.
527 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
532 [SecuritySafeCritical]
533 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
537 throw new ArgumentNullException("hash");
539 if (signature == null)
541 throw new ArgumentNullException("signature");
543 if (String.IsNullOrEmpty(hashAlgorithm.Name))
545 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
549 throw new ArgumentNullException("padding");
552 if (padding == RSASignaturePadding.Pkcs1)
554 return NCryptNative.VerifySignaturePkcs1(KeyHandle, hash, hashAlgorithm.Name, signature);
556 else if (padding == RSASignaturePadding.Pss)
558 return NCryptNative.VerifySignaturePss(KeyHandle, hash, hashAlgorithm.Name, hash.Length, signature);
562 // no other padding possibilities at present, but we might version independently from more being added.
563 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
571 * get_KeyExchangeAlgorithm
572 * get_SignatureAlgorithm
573 * are all implemented on RSA as of net46.
575 * But in servicing situations, System.Core.dll can get patched onto a machine which has mscorlib < net46, meaning
576 * these abstract members have no implementation.
578 * To keep servicing simple, we'll redefine the overrides here. Since this type is sealed it only affects reflection,
579 * as there are no derived types to mis-target base.-invocations.
581 public override byte[] DecryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
582 public override byte[] EncryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
583 public override string KeyExchangeAlgorithm { get { return "RSA"; } }
584 public override string SignatureAlgorithm { get { return "RSA"; } }