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 {
21 private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
24 private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
30 public ECDsaCng() : this(521) {
31 Contract.Ensures(LegalKeySizesValue != null);
34 public ECDsaCng(int keySize) {
35 Contract.Ensures(LegalKeySizesValue != null);
37 if (!NCryptNative.NCryptSupported) {
38 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
41 LegalKeySizesValue = s_legalKeySizes;
45 [SecuritySafeCritical]
46 public ECDsaCng(CngKey key) {
47 Contract.Ensures(LegalKeySizesValue != null);
48 Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa);
51 throw new ArgumentNullException("key");
53 if (key.AlgorithmGroup != CngAlgorithmGroup.ECDsa) {
54 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key");
57 if (!NCryptNative.NCryptSupported) {
58 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
61 LegalKeySizesValue = s_legalKeySizes;
63 // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
65 // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
66 // and the handle constructor of CngKey. The assert is safe since ECDsaCng will never expose the
67 // key handles to calling code (without first demanding UnmanagedCode via the Handle property of
70 // We also need to dispose of the key handle since CngKey.Handle returns a duplicate
71 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
72 using (SafeNCryptKeyHandle keyHandle = key.Handle) {
73 Key = CngKey.Open(keyHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
75 CodeAccessPermission.RevertAssert();
77 KeySize = m_key.KeySize;
81 /// Hash algorithm to use when generating a signature over arbitrary data
83 public CngAlgorithm HashAlgorithm {
85 Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
86 return m_hashAlgorithm;
90 Contract.Ensures(m_hashAlgorithm != null);
93 throw new ArgumentNullException("value");
96 m_hashAlgorithm = value;
101 /// Key to use for signing
105 Contract.Ensures(Contract.Result<CngKey>() != null);
106 Contract.Ensures(Contract.Result<CngKey>().AlgorithmGroup == CngAlgorithmGroup.ECDsa);
107 Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa);
109 // If the size of the key no longer matches our stored value, then we need to replace it with
110 // a new key of the correct size.
111 if (m_key != null && m_key.KeySize != KeySize) {
117 // Map the current key size to a CNG algorithm name
118 CngAlgorithm algorithm = null;
121 algorithm = CngAlgorithm.ECDsaP256;
125 algorithm = CngAlgorithm.ECDsaP384;
129 algorithm = CngAlgorithm.ECDsaP521;
133 Debug.Assert(false, "Illegal key size set");
137 m_key = CngKey.Create(algorithm);
144 Contract.Requires(value != null);
145 Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDsa);
147 if (value.AlgorithmGroup != CngAlgorithmGroup.ECDsa) {
148 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey));
156 // We do not duplicate the handle because the only time the user has access to the key itself
157 // to dispose underneath us is when they construct via the CngKey constructor, which does a
158 // copy. Otherwise all key lifetimes are controlled directly by the ECDsaCng class.
162 KeySize = m_key.KeySize;
167 /// Clean up the algorithm
169 protected override void Dispose(bool disposing) {
176 base.Dispose(disposing);
185 // There is currently not a standard XML format for ECC keys, so we will not implement the default
186 // To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
187 // use an overload which allows the user to specify the format they'd like to serialize into.
189 // See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
190 // the currently supported format.
193 public override void FromXmlString(string xmlString) {
194 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
197 public void FromXmlString(string xml, ECKeyXmlFormat format) {
199 throw new ArgumentNullException("xml");
201 if (format != ECKeyXmlFormat.Rfc4050) {
202 throw new ArgumentOutOfRangeException("format");
205 Key = Rfc4050KeyFormatter.FromXml(xml);
209 // Signature generation
212 public byte[] SignData(byte[] data) {
213 Contract.Ensures(Contract.Result<byte[]>() != null);
216 throw new ArgumentNullException("data");
219 return SignData(data, 0, data.Length);
222 [SecuritySafeCritical]
223 public byte[] SignData(byte[] data, int offset, int count) {
224 Contract.Ensures(Contract.Result<byte[]>() != null);
227 throw new ArgumentNullException("data");
229 if (offset < 0 || offset > data.Length) {
230 throw new ArgumentOutOfRangeException("offset");
232 if (count < 0 || count > data.Length - offset) {
233 throw new ArgumentOutOfRangeException("count");
236 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
237 hashAlgorithm.HashCore(data, offset, count);
238 byte[] hashValue = hashAlgorithm.HashFinal();
240 return SignHash(hashValue);
244 [SecuritySafeCritical]
245 public byte[] SignData(Stream data) {
246 Contract.Ensures(Contract.Result<byte[]>() != null);
249 throw new ArgumentNullException("data");
252 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
253 hashAlgorithm.HashStream(data);
254 byte[] hashValue = hashAlgorithm.HashFinal();
256 return SignHash(hashValue);
260 [SecuritySafeCritical]
261 public override byte[] SignHash(byte[] hash) {
263 throw new ArgumentNullException("hash");
266 // Make sure we're allowed to sign using this key
267 KeyContainerPermission permission = Key.BuildKeyContainerPermission(KeyContainerPermissionFlags.Sign);
268 if (permission != null) {
272 // Now that know we have permission to use this key for signing, pull the key value out, which
273 // will require unmanaged code permission
274 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
276 // This looks odd, but the key handle is actually a duplicate so we need to dispose it
277 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
278 CodeAccessPermission.RevertAssert();
280 return NCryptNative.SignHash(keyHandle, hash);
287 // See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
288 // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
289 // XML serialization of elliptic curve keys
292 public override string ToXmlString(bool includePrivateParameters) {
293 throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
296 public string ToXmlString(ECKeyXmlFormat format) {
297 Contract.Ensures(Contract.Result<string>() != null);
299 if (format != ECKeyXmlFormat.Rfc4050) {
300 throw new ArgumentOutOfRangeException("format");
303 return Rfc4050KeyFormatter.ToXml(Key);
307 // Signature verification
310 public bool VerifyData(byte[] data, byte[] signature) {
312 throw new ArgumentNullException("data");
315 return VerifyData(data, 0, data.Length, signature);
318 [SecuritySafeCritical]
319 public bool VerifyData(byte[] data, int offset, int count, byte[] signature) {
321 throw new ArgumentNullException("data");
323 if (offset < 0 || offset > data.Length) {
324 throw new ArgumentOutOfRangeException("offset");
326 if (count < 0 || count > data.Length - offset) {
327 throw new ArgumentOutOfRangeException("count");
329 if (signature == null) {
330 throw new ArgumentNullException("signature");
333 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
334 hashAlgorithm.HashCore(data, offset, count);
335 byte[] hashValue = hashAlgorithm.HashFinal();
337 return VerifyHash(hashValue, signature);
341 [SecuritySafeCritical]
342 public bool VerifyData(Stream data, byte[] signature) {
344 throw new ArgumentNullException("data");
346 if (signature == null) {
347 throw new ArgumentNullException("signature");
350 using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
351 hashAlgorithm.HashStream(data);
352 byte[] hashValue = hashAlgorithm.HashFinal();
354 return VerifyHash(hashValue, signature);
358 [SecuritySafeCritical]
359 public override bool VerifyHash(byte[] hash, byte[] signature) {
361 throw new ArgumentNullException("hash");
363 if (signature == null) {
364 throw new ArgumentNullException("signature");
367 // We need to get the raw key handle to verify the signature. Asserting here is safe since verifiation
368 // is not a protected operation, and we do not expose the handle to the user code.
369 new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
371 // This looks odd, but Key.Handle is really a duplicate so we need to dispose it
372 using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
373 CodeAccessPermission.RevertAssert();
375 return NCryptNative.VerifySignature(keyHandle, hash, signature);
380 /// Helper property to get the NCrypt key handle
382 private SafeNCryptKeyHandle KeyHandle {
383 [SecuritySafeCritical]
384 get { return Key.Handle; }
387 protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
388 // we're sealed and the base should have checked this before calling us
389 Debug.Assert(data != null);
390 Debug.Assert(offset >= 0 && offset <= data.Length);
391 Debug.Assert(count >= 0 && count <= data.Length - offset);
392 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
394 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
395 hasher.HashCore(data, offset, count);
396 return hasher.HashFinal();
400 protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
401 // we're sealed and the base should have checked this before calling us
402 Debug.Assert(data != null);
403 Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
405 using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
406 hasher.HashStream(data);
407 return hasher.HashFinal();