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 override RSAParameters ExportParameters(bool includePrivateParameters)
16 throw new NotImplementedException();
19 public override void ImportParameters(RSAParameters parameters)
21 throw new NotImplementedException();
25 // See https://msdn.microsoft.com/en-us/library/windows/desktop/bb931354(v=vs.85).aspx
26 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(512, 16384, 64) };
28 // CngKeyBlob formats for RSA key blobs
29 private static CngKeyBlobFormat s_rsaFullPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaFullPrivateBlob);
30 private static CngKeyBlobFormat s_rsaPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPrivateBlob);
31 private static CngKeyBlobFormat s_rsaPublicBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPublicBlob);
37 /// Create an RSACng algorithm with a random 2048 bit key pair.
39 public RSACng() : this(2048) { }
42 /// Creates a new RSACng object that will use a randomly generated key of the specified size.
43 /// Valid key sizes range from 384 to 16384 bits, in increments of 8. It's suggested that a
44 /// minimum size of 2048 bits be used for all keys.
46 /// <param name="keySize">Size of the key to generate, in bits.</param>
47 /// <exception cref="CryptographicException">if <paramref name="keySize" /> is not valid</exception>
48 public RSACng(int keySize)
50 LegalKeySizesValue = s_legalKeySizes;
55 /// Creates a new RSACng object that will use the specified key. The key's
56 /// <see cref="CngKey.AlgorithmGroup" /> must be Rsa.
57 /// CngKey.Open creates a copy of the key. Even if someone disposes the key passed
58 /// copy of this key object in RSA stays alive.
60 /// <param name="key">Key to use for RSA operations</param>
61 /// <exception cref="ArgumentException">if <paramref name="key" /> is not an RSA key</exception>
62 /// <exception cref="ArgumentNullException">if <paramref name="key" /> is null.</exception>
63 [SecuritySafeCritical]
64 public RSACng(CngKey key)
68 throw new ArgumentNullException("key");
70 if (key.AlgorithmGroup != CngAlgorithmGroup.Rsa)
72 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "key");
74 LegalKeySizesValue = s_legalKeySizes;
75 Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
79 /// Gets the key that will be used by the RSA object for any cryptographic operation that it uses.
80 /// This key object will be disposed if the key is reset, for instance by changing the KeySize
81 /// property, using ImportParamers to create a new key, or by Disposing of the parent RSA object.
82 /// Therefore, you should make sure that the key object is no longer used in these scenarios. This
83 /// object will not be the same object as the CngKey passed to the RSACng constructor if that
84 /// constructor was used, however it will point at the same CNG key.
86 /// <permission cref="SecurityPermission">
87 /// SecurityPermission/UnmanagedCode is required to read this property.
91 [SecuritySafeCritical]
94 // If our key size was changed from the key we're using, we need to generate a new key
95 if (_key != null && _key.KeySize != KeySize)
101 // If we don't have a key yet, we need to generate a random one now
104 CngKeyCreationParameters creationParameters = new CngKeyCreationParameters()
106 ExportPolicy = CngExportPolicies.AllowPlaintextExport,
109 CngProperty keySizeProperty = new CngProperty(NCryptNative.KeyPropertyName.Length,
110 BitConverter.GetBytes(KeySize),
111 CngPropertyOptions.None);
112 creationParameters.Parameters.Add(keySizeProperty);
113 _key = CngKey.Create(CngAlgorithm.Rsa, null, creationParameters);
121 Debug.Assert(value != null, "value != null");
122 if (value.AlgorithmGroup != CngAlgorithmGroup.Rsa)
124 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "value");
126 // If we already have a key, clear it out
134 // Our LegalKeySizes value stores the values that we encoded as being the correct
135 // legal key size limitations for this algorithm, as documented on MSDN.
137 // But on a new OS version we might not question if our limit is accurate, or MSDN
138 // could have been innacurate to start with.
140 // Since the key is already loaded, we know that Windows thought it to be valid;
141 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
144 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
145 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
146 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
147 // alignment requirement. (In both cases Windows loads it just fine)
148 KeySizeValue = _key.KeySize;
153 /// Helper property to get the NCrypt key handle
155 private SafeNCryptKeyHandle KeyHandle
157 [SecuritySafeCritical]
158 get { return Key.Handle; }
161 protected override void Dispose(bool disposing)
163 if (disposing && _key != null)
169 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
171 // we're sealed and the base should have checked this already
172 Debug.Assert(data != null);
173 Debug.Assert(offset >= 0 && offset <= data.Length);
174 Debug.Assert(count >= 0 && count <= data.Length);
175 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
177 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
179 hasher.HashCore(data, offset, count);
180 return hasher.HashFinal();
184 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
186 // We're sealed and the base should have checked these alread.
187 Debug.Assert(data != null);
188 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
190 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
192 hasher.HashStream(data);
193 return hasher.HashFinal();
199 /// This function checks the magic value in the key blob header
201 /// <param name="includePrivateParameters">Private blob if true else public key blob</param>
202 private void CheckMagicValueOfKey(int magic, bool includePrivateParameters)
204 if (false == includePrivateParameters)
206 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPublic)
208 //Check for Private key magic as public key can be derived from private key blob
209 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
211 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPublicOrPrivateKey));
215 //If includePrivateParameters is true then certainly check for the private key magic
218 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
220 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPrivateKey));
226 // Key import and export
230 /// Exports the key used by the RSA object into an RSAParameters object.
232 [SecuritySafeCritical]
233 public override RSAParameters ExportParameters(bool includePrivateParameters)
235 byte[] rsaBlob = Key.Export(includePrivateParameters ? s_rsaFullPrivateBlob : s_rsaPublicBlob);
236 RSAParameters rsaParams = new RSAParameters();
239 // We now have a buffer laid out as follows:
240 // BCRYPT_RSAKEY_BLOB header
241 // byte[cbPublicExp] publicExponent - Exponent
242 // byte[cbModulus] modulus - Modulus
243 // -- Private only --
244 // byte[cbPrime1] prime1 - P
245 // byte[cbPrime2] prime2 - Q
246 // byte[cbPrime1] exponent1 - DP
247 // byte[cbPrime2] exponent2 - DQ
248 // byte[cbPrime1] coefficient - InverseQ
249 // byte[cbModulus] privateExponent - D
251 byte[] tempMagic = new byte[4];
252 tempMagic[0] = rsaBlob[0]; tempMagic[1] = rsaBlob[1]; tempMagic[2] = rsaBlob[2]; tempMagic[3] = rsaBlob[3];
253 int magic = BitConverter.ToInt32(tempMagic, 0);
254 //Check the magic value in key blob header. If blob does not have required magic
255 // then it trhows Cryptographic exception
256 CheckMagicValueOfKey(magic, includePrivateParameters);
260 fixed (byte* pRsaBlob = rsaBlob)
262 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
264 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
266 // Read out the exponent
267 rsaParams.Exponent = new byte[pBcryptBlob->cbPublicExp];
268 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Exponent, 0, rsaParams.Exponent.Length);
269 offset += pBcryptBlob->cbPublicExp;
271 // Read out the modulus
272 rsaParams.Modulus = new byte[pBcryptBlob->cbModulus];
273 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Modulus, 0, rsaParams.Modulus.Length);
274 offset += pBcryptBlob->cbModulus;
276 if (includePrivateParameters)
279 rsaParams.P = new byte[pBcryptBlob->cbPrime1];
280 Buffer.BlockCopy(rsaBlob, offset, rsaParams.P, 0, rsaParams.P.Length);
281 offset += pBcryptBlob->cbPrime1;
284 rsaParams.Q = new byte[pBcryptBlob->cbPrime2];
285 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Q, 0, rsaParams.Q.Length);
286 offset += pBcryptBlob->cbPrime2;
289 rsaParams.DP = new byte[pBcryptBlob->cbPrime1];
290 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DP, 0, rsaParams.DP.Length);
291 offset += pBcryptBlob->cbPrime1;
294 rsaParams.DQ = new byte[pBcryptBlob->cbPrime2];
295 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DQ, 0, rsaParams.DQ.Length);
296 offset += pBcryptBlob->cbPrime2;
299 rsaParams.InverseQ = new byte[pBcryptBlob->cbPrime1];
300 Buffer.BlockCopy(rsaBlob, offset, rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
301 offset += pBcryptBlob->cbPrime1;
304 rsaParams.D = new byte[pBcryptBlob->cbModulus];
305 Buffer.BlockCopy(rsaBlob, offset, rsaParams.D, 0, rsaParams.D.Length);
306 offset += pBcryptBlob->cbModulus;
316 /// ImportParameters will replace the existing key that RSACng is working with by creating a
317 /// new CngKey for the parameters structure. If the parameters structure contains only an
318 /// exponent and modulus, then only a public key will be imported. If the parameters also
319 /// contain P and Q values, then a full key pair will be imported.
322 /// <exception cref="ArgumentException">
323 /// if <paramref name="parameters" /> contains neither an exponent nor a modulus.
325 /// <exception cref="CryptographicException">
326 /// if <paramref name="parameters" /> is not a valid RSA key or if <paramref name="parameters"
327 /// /> is a full key pair and the default KSP is used.
329 [SecuritySafeCritical]
330 public override void ImportParameters(RSAParameters parameters)
332 if (parameters.Exponent == null || parameters.Modulus == null)
334 throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidRsaParameters));
336 bool publicOnly = parameters.P == null || parameters.Q == null;
339 // We need to build a key blob structured as follows:
340 // BCRYPT_RSAKEY_BLOB header
341 // byte[cbPublicExp] publicExponent - Exponent
342 // byte[cbModulus] modulus - Modulus
343 // -- Private only --
344 // byte[cbPrime1] prime1 - P
345 // byte[cbPrime2] prime2 - Q
348 int blobSize = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB)) +
349 parameters.Exponent.Length +
350 parameters.Modulus.Length;
353 blobSize += parameters.P.Length +
357 byte[] rsaBlob = new byte[blobSize];
360 fixed (byte* pRsaBlob = rsaBlob)
363 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
364 pBcryptBlob->Magic = publicOnly ? BCryptNative.KeyBlobMagicNumber.RsaPublic :
365 BCryptNative.KeyBlobMagicNumber.RsaPrivate;
367 pBcryptBlob->BitLength = parameters.Modulus.Length * 8;
369 pBcryptBlob->cbPublicExp = parameters.Exponent.Length;
370 pBcryptBlob->cbModulus = parameters.Modulus.Length;
374 pBcryptBlob->cbPrime1 = parameters.P.Length;
375 pBcryptBlob->cbPrime2 = parameters.Q.Length;
378 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
381 Buffer.BlockCopy(parameters.Exponent, 0, rsaBlob, offset, parameters.Exponent.Length);
382 offset += parameters.Exponent.Length;
385 Buffer.BlockCopy(parameters.Modulus, 0, rsaBlob, offset, parameters.Modulus.Length);
386 offset += parameters.Modulus.Length;
391 Buffer.BlockCopy(parameters.P, 0, rsaBlob, offset, parameters.P.Length);
392 offset += parameters.P.Length;
395 Buffer.BlockCopy(parameters.Q, 0, rsaBlob, offset, parameters.Q.Length);
396 offset += parameters.Q.Length;
400 Key = CngKey.Import(rsaBlob, publicOnly ? s_rsaPublicBlob : s_rsaPrivateBlob);
404 // Encryption and decryption
406 [SecuritySafeCritical]
407 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
411 throw new ArgumentNullException("data");
416 throw new ArgumentNullException("padding");
419 SafeNCryptKeyHandle keyHandle = Key.Handle;
421 if (padding == RSAEncryptionPadding.Pkcs1)
423 return NCryptNative.DecryptDataPkcs1(keyHandle, data);
425 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
427 return NCryptNative.DecryptDataOaep(keyHandle, data, padding.OaepHashAlgorithm.Name);
431 // no other padding possibilities at present, but we might version independently from more being added.
432 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
436 [SecuritySafeCritical]
437 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
441 throw new ArgumentNullException("data");
445 throw new ArgumentNullException("padding");
448 if (padding == RSAEncryptionPadding.Pkcs1)
450 return NCryptNative.EncryptDataPkcs1(KeyHandle, data);
452 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
454 return NCryptNative.EncryptDataOaep(KeyHandle, data, padding.OaepHashAlgorithm.Name);
458 // no other padding possibilities at present, but we might version independently from more being added.
459 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
468 [SecuritySafeCritical]
469 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
473 throw new ArgumentNullException("hash");
475 if (String.IsNullOrEmpty(hashAlgorithm.Name))
477 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
481 throw new ArgumentNullException("padding");
484 // Keep a local copy of the key.
486 SafeNCryptKeyHandle keyHandle = key.Handle;
488 if (padding == RSASignaturePadding.Pkcs1)
490 return NCryptNative.SignHashPkcs1(keyHandle, hash, hashAlgorithm.Name);
492 else if (padding == RSASignaturePadding.Pss)
494 return NCryptNative.SignHashPss(keyHandle, hash, hashAlgorithm.Name, hash.Length);
498 // no other padding possibilities at present, but we might version independently from more being added.
499 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
504 [SecuritySafeCritical]
505 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
509 throw new ArgumentNullException("hash");
511 if (signature == null)
513 throw new ArgumentNullException("signature");
515 if (String.IsNullOrEmpty(hashAlgorithm.Name))
517 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
521 throw new ArgumentNullException("padding");
524 if (padding == RSASignaturePadding.Pkcs1)
526 return NCryptNative.VerifySignaturePkcs1(KeyHandle, hash, hashAlgorithm.Name, signature);
528 else if (padding == RSASignaturePadding.Pss)
530 return NCryptNative.VerifySignaturePss(KeyHandle, hash, hashAlgorithm.Name, hash.Length, signature);
534 // no other padding possibilities at present, but we might version independently from more being added.
535 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
543 * get_KeyExchangeAlgorithm
544 * get_SignatureAlgorithm
545 * are all implemented on RSA as of net46.
547 * But in servicing situations, System.Core.dll can get patched onto a machine which has mscorlib < net46, meaning
548 * these abstract members have no implementation.
550 * To keep servicing simple, we'll redefine the overrides here. Since this type is sealed it only affects reflection,
551 * as there are no derived types to mis-target base.-invocations.
553 public override byte[] DecryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
554 public override byte[] EncryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
555 public override string KeyExchangeAlgorithm { get { return "RSA"; } }
556 public override string SignatureAlgorithm { get { return "RSA"; } }