[bcl] Updates referencesource to 4.7.1
[mono-project.git] / mcs / class / referencesource / System.Core / System / Security / Cryptography / RsaCng.cs
blob15c809bb4ded168f4d86da31868d3904b6531469
1 using System;
2 using System.Diagnostics;
3 using System.Diagnostics.CodeAnalysis;
4 using System.IO;
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
13 #if MONO
14 public RSACng() : this(2048) { }
16 public RSACng(int keySize)
18 throw new NotImplementedException ();
21 public RSACng(CngKey key)
23 throw new NotImplementedException ();
26 public CngKey Key
28 [SecuritySafeCritical]
29 get
31 throw new NotImplementedException ();
34 private set
36 throw new NotImplementedException ();
40 public override RSAParameters ExportParameters(bool includePrivateParameters)
42 throw new NotImplementedException();
45 public override void ImportParameters(RSAParameters parameters)
47 throw new NotImplementedException();
49 #else
51 // See https://msdn.microsoft.com/en-us/library/windows/desktop/bb931354(v=vs.85).aspx
52 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(512, 16384, 64) };
54 // CngKeyBlob formats for RSA key blobs
55 private static CngKeyBlobFormat s_rsaFullPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaFullPrivateBlob);
56 private static CngKeyBlobFormat s_rsaPrivateBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPrivateBlob);
57 private static CngKeyBlobFormat s_rsaPublicBlob = new CngKeyBlobFormat(BCryptNative.KeyBlobType.RsaPublicBlob);
59 // Key handle
60 private CngKey _key;
62 /// <summary>
63 /// Create an RSACng algorithm with a random 2048 bit key pair.
64 /// </summary>
65 public RSACng() : this(2048) { }
67 /// <summary>
68 /// Creates a new RSACng object that will use a randomly generated key of the specified size.
69 /// Valid key sizes range from 384 to 16384 bits, in increments of 8. It's suggested that a
70 /// minimum size of 2048 bits be used for all keys.
71 /// </summary>
72 /// <param name="keySize">Size of the key to generate, in bits.</param>
73 /// <exception cref="CryptographicException">if <paramref name="keySize" /> is not valid</exception>
74 public RSACng(int keySize)
76 LegalKeySizesValue = s_legalKeySizes;
77 KeySize = keySize;
80 /// <summary>
81 /// Creates a new RSACng object that will use the specified key. The key's
82 /// <see cref="CngKey.AlgorithmGroup" /> must be Rsa.
83 /// CngKey.Open creates a copy of the key. Even if someone disposes the key passed
84 /// copy of this key object in RSA stays alive.
85 /// </summary>
86 /// <param name="key">Key to use for RSA operations</param>
87 /// <exception cref="ArgumentException">if <paramref name="key" /> is not an RSA key</exception>
88 /// <exception cref="ArgumentNullException">if <paramref name="key" /> is null.</exception>
89 [SecuritySafeCritical]
90 [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
91 public RSACng(CngKey key)
93 if (key == null)
95 throw new ArgumentNullException("key");
97 if (key.AlgorithmGroup != CngAlgorithmGroup.Rsa)
99 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgRSAaRequiresRSAKey), "key");
101 LegalKeySizesValue = s_legalKeySizes;
102 Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
105 /// <summary>
106 /// Gets the key that will be used by the RSA object for any cryptographic operation that it uses.
107 /// This key object will be disposed if the key is reset, for instance by changing the KeySize
108 /// property, using ImportParamers to create a new key, or by Disposing of the parent RSA object.
109 /// Therefore, you should make sure that the key object is no longer used in these scenarios. This
110 /// object will not be the same object as the CngKey passed to the RSACng constructor if that
111 /// constructor was used, however it will point at the same CNG key.
112 /// </summary>
113 /// <permission cref="SecurityPermission">
114 /// SecurityPermission/UnmanagedCode is required to read this property.
115 /// </permission>
116 public CngKey Key
118 [SecuritySafeCritical]
119 [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
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)
125 _key.Dispose();
126 _key = null;
129 // If we don't have a key yet, we need to generate a random one now
130 if (_key == null)
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);
144 return _key;
147 private set
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
155 if (_key != null)
157 _key.Dispose();
160 _key = value;
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
170 // check.
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;
180 /// <summary>
181 /// Helper property to get the NCrypt key handle
182 /// </summary>
183 private SafeNCryptKeyHandle KeyHandle
185 [SecuritySafeCritical]
186 [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
187 get { return Key.Handle; }
190 protected override void Dispose(bool disposing)
192 if (disposing && _key != null)
194 _key.Dispose();
198 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
200 // we're sealed and the base should have checked this already
201 Debug.Assert(data != null);
202 Debug.Assert(offset >= 0 && offset <= data.Length);
203 Debug.Assert(count >= 0 && count <= data.Length);
204 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
206 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
208 hasher.HashCore(data, offset, count);
209 return hasher.HashFinal();
213 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
215 // We're sealed and the base should have checked these alread.
216 Debug.Assert(data != null);
217 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
219 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider))
221 hasher.HashStream(data);
222 return hasher.HashFinal();
227 /// <summary>
228 /// This function checks the magic value in the key blob header
229 /// </summary>
230 /// <param name="includePrivateParameters">Private blob if true else public key blob</param>
231 private void CheckMagicValueOfKey(int magic, bool includePrivateParameters)
233 if (false == includePrivateParameters)
235 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPublic)
237 //Check for Private key magic as public key can be derived from private key blob
238 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
240 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPublicOrPrivateKey));
244 //If includePrivateParameters is true then certainly check for the private key magic
245 else
247 if (magic != (int)BCryptNative.KeyBlobMagicNumber.RsaPrivate && magic != (int)BCryptNative.KeyBlobMagicNumber.RsaFullPrivateMagic)
249 throw new CryptographicException(SR.GetString(SR.Cryptography_NotValidPrivateKey));
255 // Key import and export
258 /// <summary>
259 /// Exports the key used by the RSA object into an RSAParameters object.
260 /// </summary>
261 [SecuritySafeCritical]
262 public override RSAParameters ExportParameters(bool includePrivateParameters)
264 byte[] rsaBlob = Key.Export(includePrivateParameters ? s_rsaFullPrivateBlob : s_rsaPublicBlob);
265 RSAParameters rsaParams = new RSAParameters();
268 // We now have a buffer laid out as follows:
269 // BCRYPT_RSAKEY_BLOB header
270 // byte[cbPublicExp] publicExponent - Exponent
271 // byte[cbModulus] modulus - Modulus
272 // -- Private only --
273 // byte[cbPrime1] prime1 - P
274 // byte[cbPrime2] prime2 - Q
275 // byte[cbPrime1] exponent1 - DP
276 // byte[cbPrime2] exponent2 - DQ
277 // byte[cbPrime1] coefficient - InverseQ
278 // byte[cbModulus] privateExponent - D
280 byte[] tempMagic = new byte[4];
281 tempMagic[0] = rsaBlob[0]; tempMagic[1] = rsaBlob[1]; tempMagic[2] = rsaBlob[2]; tempMagic[3] = rsaBlob[3];
282 int magic = BitConverter.ToInt32(tempMagic, 0);
283 //Check the magic value in key blob header. If blob does not have required magic
284 // then it trhows Cryptographic exception
285 CheckMagicValueOfKey(magic, includePrivateParameters);
287 unsafe
289 fixed (byte* pRsaBlob = rsaBlob)
291 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
293 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
295 // Read out the exponent
296 rsaParams.Exponent = new byte[pBcryptBlob->cbPublicExp];
297 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Exponent, 0, rsaParams.Exponent.Length);
298 offset += pBcryptBlob->cbPublicExp;
300 // Read out the modulus
301 rsaParams.Modulus = new byte[pBcryptBlob->cbModulus];
302 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Modulus, 0, rsaParams.Modulus.Length);
303 offset += pBcryptBlob->cbModulus;
305 if (includePrivateParameters)
307 // Read out P
308 rsaParams.P = new byte[pBcryptBlob->cbPrime1];
309 Buffer.BlockCopy(rsaBlob, offset, rsaParams.P, 0, rsaParams.P.Length);
310 offset += pBcryptBlob->cbPrime1;
312 // Read out Q
313 rsaParams.Q = new byte[pBcryptBlob->cbPrime2];
314 Buffer.BlockCopy(rsaBlob, offset, rsaParams.Q, 0, rsaParams.Q.Length);
315 offset += pBcryptBlob->cbPrime2;
317 // Read out DP
318 rsaParams.DP = new byte[pBcryptBlob->cbPrime1];
319 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DP, 0, rsaParams.DP.Length);
320 offset += pBcryptBlob->cbPrime1;
322 // Read out DQ
323 rsaParams.DQ = new byte[pBcryptBlob->cbPrime2];
324 Buffer.BlockCopy(rsaBlob, offset, rsaParams.DQ, 0, rsaParams.DQ.Length);
325 offset += pBcryptBlob->cbPrime2;
327 // Read out InverseQ
328 rsaParams.InverseQ = new byte[pBcryptBlob->cbPrime1];
329 Buffer.BlockCopy(rsaBlob, offset, rsaParams.InverseQ, 0, rsaParams.InverseQ.Length);
330 offset += pBcryptBlob->cbPrime1;
332 // Read out D
333 rsaParams.D = new byte[pBcryptBlob->cbModulus];
334 Buffer.BlockCopy(rsaBlob, offset, rsaParams.D, 0, rsaParams.D.Length);
335 offset += pBcryptBlob->cbModulus;
340 return rsaParams;
343 /// <summary>
344 /// <para>
345 /// ImportParameters will replace the existing key that RSACng is working with by creating a
346 /// new CngKey for the parameters structure. If the parameters structure contains only an
347 /// exponent and modulus, then only a public key will be imported. If the parameters also
348 /// contain P and Q values, then a full key pair will be imported.
349 /// </para>
350 /// </summary>
351 /// <exception cref="ArgumentException">
352 /// if <paramref name="parameters" /> contains neither an exponent nor a modulus.
353 /// </exception>
354 /// <exception cref="CryptographicException">
355 /// if <paramref name="parameters" /> is not a valid RSA key or if <paramref name="parameters"
356 /// /> is a full key pair and the default KSP is used.
357 /// </exception>
358 [SecuritySafeCritical]
359 public override void ImportParameters(RSAParameters parameters)
361 if (parameters.Exponent == null || parameters.Modulus == null)
363 throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidRsaParameters));
365 bool publicOnly = parameters.P == null || parameters.Q == null;
368 // We need to build a key blob structured as follows:
369 // BCRYPT_RSAKEY_BLOB header
370 // byte[cbPublicExp] publicExponent - Exponent
371 // byte[cbModulus] modulus - Modulus
372 // -- Private only --
373 // byte[cbPrime1] prime1 - P
374 // byte[cbPrime2] prime2 - Q
377 int blobSize = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB)) +
378 parameters.Exponent.Length +
379 parameters.Modulus.Length;
380 if (!publicOnly)
382 blobSize += parameters.P.Length +
383 parameters.Q.Length;
386 byte[] rsaBlob = new byte[blobSize];
387 unsafe
389 fixed (byte* pRsaBlob = rsaBlob)
391 // Build the header
392 BCryptNative.BCRYPT_RSAKEY_BLOB* pBcryptBlob = (BCryptNative.BCRYPT_RSAKEY_BLOB*)pRsaBlob;
393 pBcryptBlob->Magic = publicOnly ? BCryptNative.KeyBlobMagicNumber.RsaPublic :
394 BCryptNative.KeyBlobMagicNumber.RsaPrivate;
396 pBcryptBlob->BitLength = parameters.Modulus.Length * 8;
398 pBcryptBlob->cbPublicExp = parameters.Exponent.Length;
399 pBcryptBlob->cbModulus = parameters.Modulus.Length;
401 if (!publicOnly)
403 pBcryptBlob->cbPrime1 = parameters.P.Length;
404 pBcryptBlob->cbPrime2 = parameters.Q.Length;
407 int offset = Marshal.SizeOf(typeof(BCryptNative.BCRYPT_RSAKEY_BLOB));
409 // Copy the exponent
410 Buffer.BlockCopy(parameters.Exponent, 0, rsaBlob, offset, parameters.Exponent.Length);
411 offset += parameters.Exponent.Length;
413 // Copy the modulus
414 Buffer.BlockCopy(parameters.Modulus, 0, rsaBlob, offset, parameters.Modulus.Length);
415 offset += parameters.Modulus.Length;
417 if (!publicOnly)
419 // Copy P
420 Buffer.BlockCopy(parameters.P, 0, rsaBlob, offset, parameters.P.Length);
421 offset += parameters.P.Length;
423 // Copy Q
424 Buffer.BlockCopy(parameters.Q, 0, rsaBlob, offset, parameters.Q.Length);
425 offset += parameters.Q.Length;
429 Key = CngKey.Import(rsaBlob, publicOnly ? s_rsaPublicBlob : s_rsaPrivateBlob);
433 // Encryption and decryption
435 [SecuritySafeCritical]
436 public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding)
438 if (data == null)
440 throw new ArgumentNullException("data");
443 if (padding == null)
445 throw new ArgumentNullException("padding");
448 SafeNCryptKeyHandle keyHandle = KeyHandle;
450 if (padding == RSAEncryptionPadding.Pkcs1)
452 return NCryptNative.DecryptDataPkcs1(keyHandle, data);
454 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
456 return NCryptNative.DecryptDataOaep(keyHandle, data, padding.OaepHashAlgorithm.Name);
458 else
460 // no other padding possibilities at present, but we might version independently from more being added.
461 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
465 [SecuritySafeCritical]
466 public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding)
468 if (data == null)
470 throw new ArgumentNullException("data");
472 if (padding == null)
474 throw new ArgumentNullException("padding");
477 if (padding == RSAEncryptionPadding.Pkcs1)
479 return NCryptNative.EncryptDataPkcs1(KeyHandle, data);
481 else if (padding.Mode == RSAEncryptionPaddingMode.Oaep)
483 return NCryptNative.EncryptDataOaep(KeyHandle, data, padding.OaepHashAlgorithm.Name);
485 else
487 // no other padding possibilities at present, but we might version independently from more being added.
488 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
494 // Signature APIs
497 [SecuritySafeCritical]
498 [SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]
499 public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
501 if (hash == null)
503 throw new ArgumentNullException("hash");
505 if (String.IsNullOrEmpty(hashAlgorithm.Name))
507 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
509 if (padding == null)
511 throw new ArgumentNullException("padding");
514 // Keep a local copy of the key.
515 CngKey key = Key;
516 SafeNCryptKeyHandle keyHandle = key.Handle;
518 if (padding == RSASignaturePadding.Pkcs1)
520 return NCryptNative.SignHashPkcs1(keyHandle, hash, hashAlgorithm.Name);
522 else if (padding == RSASignaturePadding.Pss)
524 return NCryptNative.SignHashPss(keyHandle, hash, hashAlgorithm.Name, hash.Length);
526 else
528 // no other padding possibilities at present, but we might version independently from more being added.
529 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
534 [SecuritySafeCritical]
535 public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding)
537 if (hash == null)
539 throw new ArgumentNullException("hash");
541 if (signature == null)
543 throw new ArgumentNullException("signature");
545 if (String.IsNullOrEmpty(hashAlgorithm.Name))
547 throw new ArgumentException(SR.GetString(SR.Cryptography_HashAlgorithmNameNullOrEmpty), "hashAlgorithm");
549 if (padding == null)
551 throw new ArgumentNullException("padding");
554 if (padding == RSASignaturePadding.Pkcs1)
556 return NCryptNative.VerifySignaturePkcs1(KeyHandle, hash, hashAlgorithm.Name, signature);
558 else if (padding == RSASignaturePadding.Pss)
560 return NCryptNative.VerifySignaturePss(KeyHandle, hash, hashAlgorithm.Name, hash.Length, signature);
562 else
564 // no other padding possibilities at present, but we might version independently from more being added.
565 throw new CryptographicException(SR.GetString(SR.Cryptography_UnsupportedPaddingMode));
570 * The members
571 * DecryptValue
572 * EncryptValue
573 * get_KeyExchangeAlgorithm
574 * get_SignatureAlgorithm
575 * are all implemented on RSA as of net46.
577 * But in servicing situations, System.Core.dll can get patched onto a machine which has mscorlib < net46, meaning
578 * these abstract members have no implementation.
580 * To keep servicing simple, we'll redefine the overrides here. Since this type is sealed it only affects reflection,
581 * as there are no derived types to mis-target base.-invocations.
583 public override byte[] DecryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
584 public override byte[] EncryptValue(byte[] rgb) { throw new NotSupportedException(SR.NotSupported_Method); }
585 public override string KeyExchangeAlgorithm { get { return "RSA"; } }
586 public override string SignatureAlgorithm { get { return "RSA"; } }
587 #endif