3 // Copyright (c) Microsoft Corporation. All rights reserved.
8 using System.Diagnostics;
10 using System.Security;
11 using System.Security.Permissions;
12 using System.Diagnostics.Contracts;
13 using Microsoft.Win32.SafeHandles;
15 namespace System.Security.Cryptography {
17 /// Wrapper for NCrypt's implementation of elliptic curve DSA
19 [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
20 public sealed class ECDsaCng : ECDsa {
22 public ECDsaCng() : this(521) {
25 public ECDsaCng(int keySize) {
26 throw new NotImplementedException ();
29 [SecuritySafeCritical]
30 public ECDsaCng(CngKey key) {
31 throw new NotImplementedException ();
34 public ECDsaCng(ECCurve curve) {
35 throw new NotImplementedException ();
40 throw new NotImplementedException ();
44 throw new NotImplementedException ();
48 public override byte[] SignHash(byte[] hash) {
49 throw new NotImplementedException();
52 public override bool VerifyHash(byte[] hash, byte[] signature) {
53 throw new NotImplementedException();
56 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
59 private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
65 public ECDsaCng() : this(521) {
66 Contract.Ensures(LegalKeySizesValue != null);
69 public ECDsaCng(int keySize) {
70 Contract.Ensures(LegalKeySizesValue != null);
72 if (!NCryptNative.NCryptSupported) {
73 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
76 LegalKeySizesValue = s_legalKeySizes;
80 [SecuritySafeCritical]
81 public ECDsaCng(CngKey key) {
82 Contract.Ensures(LegalKeySizesValue != null);
83 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
86 throw new ArgumentNullException("key");
88 if (!IsEccAlgorithmGroup(key.AlgorithmGroup)) {
89 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key");
92 if (!NCryptNative.NCryptSupported) {
93 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
96 LegalKeySizesValue = s_legalKeySizes;
98 // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
100 // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
101 // and the handle constructor of CngKey. The assert is safe since ECDsaCng will never expose the
102 // key handles to calling code (without first demanding UnmanagedCode via the Handle property of
105 // We also need to dispose of the key handle since CngKey.Handle returns a duplicate
106 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
107 using (SafeNCryptKeyHandle keyHandle = key.Handle) {
108 Key = CngKey.Open(keyHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
110 CodeAccessPermission.RevertAssert();
112 // Our LegalKeySizes value stores the values that we encoded as being the correct
113 // legal key size limitations for this algorithm, as documented on MSDN.
115 // But on a new OS version we might not question if our limit is accurate, or MSDN
116 // could have been innacurate to start with.
118 // Since the key is already loaded, we know that Windows thought it to be valid;
119 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
122 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
123 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
124 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
125 // alignment requirement. (In both cases Windows loads it just fine)
126 KeySizeValue = m_key.KeySize;
130 /// Hash algorithm to use when generating a signature over arbitrary data
132 public CngAlgorithm HashAlgorithm {
134 Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
135 return m_hashAlgorithm;
139 Contract.Ensures(m_hashAlgorithm != null);
142 throw new ArgumentNullException("value");
145 m_hashAlgorithm = value;
150 /// Key to use for signing
154 Contract.Ensures(Contract.Result<CngKey>() != null);
155 Contract.Ensures(IsEccAlgorithmGroup(Contract.Result<CngKey>().AlgorithmGroup));
156 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
158 // If the size of the key no longer matches our stored value, then we need to replace it with
159 // a new key of the correct size.
160 if (m_key != null && m_key.KeySize != KeySize) {
166 // Map the current key size to a CNG algorithm name
167 CngAlgorithm algorithm = null;
170 algorithm = CngAlgorithm.ECDsaP256;
174 algorithm = CngAlgorithm.ECDsaP384;
178 algorithm = CngAlgorithm.ECDsaP521;
182 Debug.Assert(false, "Illegal key size set");
186 m_key = CngKey.Create(algorithm);
193 Contract.Requires(value != null);
194 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
196 if (!IsEccAlgorithmGroup(value.AlgorithmGroup)) {
197 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey));
205 // We do not duplicate the handle because the only time the user has access to the key itself
206 // to dispose underneath us is when they construct via the CngKey constructor, which does a
207 // copy. Otherwise all key lifetimes are controlled directly by the ECDsaCng class.
212 // Our LegalKeySizes value stores the values that we encoded as being the correct
213 // legal key size limitations for this algorithm, as documented on MSDN.
215 // But on a new OS version we might not question if our limit is accurate, or MSDN
216 // could have been innacurate to start with.
218 // Since the key is already loaded, we know that Windows thought it to be valid;
219 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
222 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
223 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
224 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
225 // alignment requirement. (In both cases Windows loads it just fine)
226 KeySizeValue = m_key.KeySize;
231 /// Clean up the algorithm
233 protected override void Dispose(bool disposing) {
240 base.Dispose(disposing);
249 // There is currently not a standard XML format for ECC keys, so we will not implement the default
250 // To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
251 // use an overload which allows the user to specify the format they'd like to serialize into.
253 // See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
254 // the currently supported format.
257 public override void FromXmlString(string xmlString) {
258 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
261 public void FromXmlString(string xml, ECKeyXmlFormat format) {
263 throw new ArgumentNullException("xml");
265 if (format != ECKeyXmlFormat.Rfc4050) {
266 throw new ArgumentOutOfRangeException("format");
269 Key = Rfc4050KeyFormatter.FromXml(xml);
273 // Signature generation
276 public byte[] SignData(byte[] data) {
277 Contract.Ensures(Contract.Result<byte[]>() != null);
280 throw new ArgumentNullException("data");
283 return SignData(data, 0, data.Length);
286 [SecuritySafeCritical]
287 public byte[] SignData(byte[] data, int offset, int count) {
288 Contract.Ensures(Contract.Result<byte[]>() != null);
291 throw new ArgumentNullException("data");
293 if (offset < 0 || offset > data.Length) {
294 throw new ArgumentOutOfRangeException("offset");
296 if (count < 0 || count > data.Length - offset) {
297 throw new ArgumentOutOfRangeException("count");
300 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
301 hashAlgorithm.HashCore(data, offset, count);
302 byte[] hashValue = hashAlgorithm.HashFinal();
304 return SignHash(hashValue);
308 [SecuritySafeCritical]
309 public byte[] SignData(Stream data) {
310 Contract.Ensures(Contract.Result<byte[]>() != null);
313 throw new ArgumentNullException("data");
316 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
317 hashAlgorithm.HashStream(data);
318 byte[] hashValue = hashAlgorithm.HashFinal();
320 return SignHash(hashValue);
324 [SecuritySafeCritical]
325 public override byte[] SignHash(byte[] hash) {
327 throw new ArgumentNullException("hash");
330 // Make sure we're allowed to sign using this key
331 KeyContainerPermission permission = Key.BuildKeyContainerPermission(KeyContainerPermissionFlags.Sign);
332 if (permission != null) {
336 // Now that know we have permission to use this key for signing, pull the key value out, which
337 // will require unmanaged code permission
338 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
340 // This looks odd, but the key handle is actually a duplicate so we need to dispose it
341 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
342 CodeAccessPermission.RevertAssert();
344 return NCryptNative.SignHash(keyHandle, hash);
351 // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
352 // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
353 // XML serialization of elliptic curve keys
356 public override string ToXmlString(bool includePrivateParameters) {
357 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
360 public string ToXmlString(ECKeyXmlFormat format) {
361 Contract.Ensures(Contract.Result<string>() != null);
363 if (format != ECKeyXmlFormat.Rfc4050) {
364 throw new ArgumentOutOfRangeException("format");
367 return Rfc4050KeyFormatter.ToXml(Key);
371 // Signature verification
374 public bool VerifyData(byte[] data, byte[] signature) {
376 throw new ArgumentNullException("data");
379 return VerifyData(data, 0, data.Length, signature);
382 [SecuritySafeCritical]
383 public bool VerifyData(byte[] data, int offset, int count, byte[] signature) {
385 throw new ArgumentNullException("data");
387 if (offset < 0 || offset > data.Length) {
388 throw new ArgumentOutOfRangeException("offset");
390 if (count < 0 || count > data.Length - offset) {
391 throw new ArgumentOutOfRangeException("count");
393 if (signature == null) {
394 throw new ArgumentNullException("signature");
397 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
398 hashAlgorithm.HashCore(data, offset, count);
399 byte[] hashValue = hashAlgorithm.HashFinal();
401 return VerifyHash(hashValue, signature);
405 [SecuritySafeCritical]
406 public bool VerifyData(Stream data, byte[] signature) {
408 throw new ArgumentNullException("data");
410 if (signature == null) {
411 throw new ArgumentNullException("signature");
414 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
415 hashAlgorithm.HashStream(data);
416 byte[] hashValue = hashAlgorithm.HashFinal();
418 return VerifyHash(hashValue, signature);
422 [SecuritySafeCritical]
423 public override bool VerifyHash(byte[] hash, byte[] signature) {
425 throw new ArgumentNullException("hash");
427 if (signature == null) {
428 throw new ArgumentNullException("signature");
431 // We need to get the raw key handle to verify the signature. Asserting here is safe since verifiation
432 // is not a protected operation, and we do not expose the handle to the user code.
433 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
435 // This looks odd, but Key.Handle is really a duplicate so we need to dispose it
436 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
437 CodeAccessPermission.RevertAssert();
439 return NCryptNative.VerifySignature(keyHandle, hash, signature);
444 /// Helper property to get the NCrypt key handle
446 private SafeNCryptKeyHandle KeyHandle {
447 [SecuritySafeCritical]
448 get { return Key.Handle; }
451 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
452 // we're sealed and the base should have checked this before calling us
453 Debug.Assert(data != null);
454 Debug.Assert(offset >= 0 && offset <= data.Length);
455 Debug.Assert(count >= 0 && count <= data.Length - offset);
456 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
458 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
459 hasher.HashCore(data, offset, count);
460 return hasher.HashFinal();
464 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
465 // we're sealed and the base should have checked this before calling us
466 Debug.Assert(data != null);
467 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
469 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
470 hasher.HashStream(data);
471 return hasher.HashFinal();
475 private static bool IsEccAlgorithmGroup(CngAlgorithmGroup algorithmGroup)
477 // Sometimes, when reading from certificates, ECDSA keys get identified as ECDH.
478 // Windows allows the ECDH keys to perform both key exchange (ECDH) and signing (ECDSA),
479 // so either value is acceptable for the ECDSA wrapper object.
481 // It is worth noting, however, that ECDSA-identified keys cannot be used for key exchange (ECDH) in CNG.
482 return algorithmGroup == CngAlgorithmGroup.ECDsa || algorithmGroup == CngAlgorithmGroup.ECDiffieHellman;