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 ();
21 public RSACng(CngKey key
)
23 throw new NotImplementedException ();
28 [SecuritySafeCritical
]
31 throw new NotImplementedException ();
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();
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
);
63 /// Create an RSACng algorithm with a random 2048 bit key pair.
65 public RSACng() : this(2048) { }
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.
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
;
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.
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
)
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
);
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.
113 /// <permission cref="SecurityPermission">
114 /// SecurityPermission/UnmanagedCode is required to read this property.
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
)
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 [SecurityPermission(SecurityAction
.Assert
, UnmanagedCode
= true)]
187 get { return Key.Handle; }
190 protected override void Dispose(bool disposing
)
192 if (disposing
&& _key
!= null)
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();
228 /// This function checks the magic value in the key blob header
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
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
259 /// Exports the key used by the RSA object into an RSAParameters object.
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
);
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
)
308 rsaParams
.P
= new byte[pBcryptBlob
->cbPrime1
];
309 Buffer
.BlockCopy(rsaBlob
, offset
, rsaParams
.P
, 0, rsaParams
.P
.Length
);
310 offset
+= pBcryptBlob
->cbPrime1
;
313 rsaParams
.Q
= new byte[pBcryptBlob
->cbPrime2
];
314 Buffer
.BlockCopy(rsaBlob
, offset
, rsaParams
.Q
, 0, rsaParams
.Q
.Length
);
315 offset
+= pBcryptBlob
->cbPrime2
;
318 rsaParams
.DP
= new byte[pBcryptBlob
->cbPrime1
];
319 Buffer
.BlockCopy(rsaBlob
, offset
, rsaParams
.DP
, 0, rsaParams
.DP
.Length
);
320 offset
+= pBcryptBlob
->cbPrime1
;
323 rsaParams
.DQ
= new byte[pBcryptBlob
->cbPrime2
];
324 Buffer
.BlockCopy(rsaBlob
, offset
, rsaParams
.DQ
, 0, rsaParams
.DQ
.Length
);
325 offset
+= pBcryptBlob
->cbPrime2
;
328 rsaParams
.InverseQ
= new byte[pBcryptBlob
->cbPrime1
];
329 Buffer
.BlockCopy(rsaBlob
, offset
, rsaParams
.InverseQ
, 0, rsaParams
.InverseQ
.Length
);
330 offset
+= pBcryptBlob
->cbPrime1
;
333 rsaParams
.D
= new byte[pBcryptBlob
->cbModulus
];
334 Buffer
.BlockCopy(rsaBlob
, offset
, rsaParams
.D
, 0, rsaParams
.D
.Length
);
335 offset
+= pBcryptBlob
->cbModulus
;
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.
351 /// <exception cref="ArgumentException">
352 /// if <paramref name="parameters" /> contains neither an exponent nor a modulus.
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.
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
;
382 blobSize
+= parameters
.P
.Length
+
386 byte[] rsaBlob
= new byte[blobSize
];
389 fixed (byte* pRsaBlob
= rsaBlob
)
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
;
403 pBcryptBlob
->cbPrime1
= parameters
.P
.Length
;
404 pBcryptBlob
->cbPrime2
= parameters
.Q
.Length
;
407 int offset
= Marshal
.SizeOf(typeof(BCryptNative
.BCRYPT_RSAKEY_BLOB
));
410 Buffer
.BlockCopy(parameters
.Exponent
, 0, rsaBlob
, offset
, parameters
.Exponent
.Length
);
411 offset
+= parameters
.Exponent
.Length
;
414 Buffer
.BlockCopy(parameters
.Modulus
, 0, rsaBlob
, offset
, parameters
.Modulus
.Length
);
415 offset
+= parameters
.Modulus
.Length
;
420 Buffer
.BlockCopy(parameters
.P
, 0, rsaBlob
, offset
, parameters
.P
.Length
);
421 offset
+= parameters
.P
.Length
;
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
)
440 throw new ArgumentNullException("data");
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
);
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
)
470 throw new ArgumentNullException("data");
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
);
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
));
497 [SecuritySafeCritical
]
498 [SecurityPermission(SecurityAction
.Assert
, UnmanagedCode
= true)]
499 public override byte[] SignHash(byte[] hash
, HashAlgorithmName hashAlgorithm
, RSASignaturePadding padding
)
503 throw new ArgumentNullException("hash");
505 if (String
.IsNullOrEmpty(hashAlgorithm
.Name
))
507 throw new ArgumentException(SR
.GetString(SR
.Cryptography_HashAlgorithmNameNullOrEmpty
), "hashAlgorithm");
511 throw new ArgumentNullException("padding");
514 // Keep a local copy of the 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
);
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
)
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");
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
);
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
));
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"; }
}