You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,8 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
// moved to mscorlib.dll
|
||||
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.Aes))]
|
||||
@@ -0,0 +1,438 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
#if FEATURE_CORESYSTEM
|
||||
using System.Core;
|
||||
#endif
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// AES wrapper around the CAPI implementation.
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class AesCryptoServiceProvider : Aes {
|
||||
private static volatile KeySizes[] s_supportedKeySizes;
|
||||
private static volatile int s_defaultKeySize;
|
||||
|
||||
[SecurityCritical]
|
||||
private SafeCspHandle m_cspHandle;
|
||||
|
||||
// Note that keys are stored in CAPI rather than directly in the KeyValue property, which should not
|
||||
// be used to retrieve the key value directly.
|
||||
[SecurityCritical]
|
||||
private SafeCapiKeyHandle m_key;
|
||||
|
||||
[System.Security.SecurityCritical]
|
||||
public AesCryptoServiceProvider () {
|
||||
Contract.Ensures(m_cspHandle != null && !m_cspHandle.IsInvalid && !m_cspHandle.IsClosed);
|
||||
|
||||
// On Windows XP the AES CSP has the prototype name, but on newer operating systems it has the
|
||||
// standard name
|
||||
string providerName = CapiNative.ProviderNames.MicrosoftEnhancedRsaAes;
|
||||
if (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1) {
|
||||
providerName = CapiNative.ProviderNames.MicrosoftEnhancedRsaAesPrototype;
|
||||
}
|
||||
|
||||
m_cspHandle = CapiNative.AcquireCsp(null,
|
||||
providerName,
|
||||
CapiNative.ProviderType.RsaAes,
|
||||
CapiNative.CryptAcquireContextFlags.VerifyContext,
|
||||
true);
|
||||
|
||||
// CAPI will not allow feedback sizes greater than 64 bits
|
||||
FeedbackSizeValue = 8;
|
||||
|
||||
// Get the different AES key sizes supported by this platform, raising an error if there are no
|
||||
// supported key sizes.
|
||||
int defaultKeySize = 0;
|
||||
KeySizes[] keySizes = FindSupportedKeySizes(m_cspHandle, out defaultKeySize);
|
||||
if (keySizes.Length != 0) {
|
||||
Debug.Assert(defaultKeySize > 0, "defaultKeySize > 0");
|
||||
KeySizeValue = defaultKeySize;
|
||||
}
|
||||
else {
|
||||
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Value of the symmetric key used for encryption / decryption
|
||||
/// </summary>
|
||||
public override byte[] Key {
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
get {
|
||||
Contract.Ensures(m_key != null && !m_key.IsInvalid && !m_key.IsClosed);
|
||||
Contract.Ensures(Contract.Result<byte[]>() != null &&
|
||||
Contract.Result<byte[]>().Length == KeySizeValue / 8);
|
||||
|
||||
if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
|
||||
GenerateKey();
|
||||
}
|
||||
|
||||
// We don't hold onto a key value directly, so we need to export it from CAPI when the user
|
||||
// wants a byte array representation.
|
||||
byte[] keyValue = CapiNative.ExportSymmetricKey(m_key);
|
||||
return keyValue;
|
||||
}
|
||||
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
set {
|
||||
Contract.Ensures(m_key != null && !m_key.IsInvalid && !m_key.IsClosed);
|
||||
|
||||
if (value == null) {
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
byte[] keyValue = (byte[])value.Clone();
|
||||
|
||||
if (!ValidKeySize(keyValue.Length * 8)) {
|
||||
throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidKeySize));
|
||||
}
|
||||
|
||||
// Import the key, then close any current key and replace with the new one. We need to make
|
||||
// sure the import is successful before closing the current key to avoid having an algorithm
|
||||
// with no valid keys.
|
||||
SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle,
|
||||
GetAlgorithmId(keyValue.Length * 8),
|
||||
keyValue);
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
|
||||
m_key = importedKey;
|
||||
KeySizeValue = keyValue.Length * 8;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Size, in bits, of the key
|
||||
/// </summary>
|
||||
public override int KeySize {
|
||||
get { return base.KeySize; }
|
||||
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
set {
|
||||
base.KeySize = value;
|
||||
|
||||
// Since the key size is being reset, we need to reset the key itself as well
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object to perform AES decryption with the current key and IV
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
public override ICryptoTransform CreateDecryptor() {
|
||||
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
|
||||
|
||||
if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
|
||||
throw new CryptographicException(SR.GetString(SR.Cryptography_DecryptWithNoKey));
|
||||
}
|
||||
|
||||
return CreateDecryptor(m_key, IVValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object to perform AES decryption with the given key and IV
|
||||
/// </summary>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) {
|
||||
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
|
||||
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
if (!ValidKeySize(key.Length * 8)) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
|
||||
}
|
||||
if (iv != null && iv.Length * 8 != BlockSizeValue) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
|
||||
}
|
||||
|
||||
byte[] keyCopy = (byte[])key.Clone();
|
||||
byte[] ivCopy = null;
|
||||
if (iv != null) {
|
||||
ivCopy = (byte[])iv.Clone();
|
||||
}
|
||||
|
||||
using (SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, GetAlgorithmId(keyCopy.Length * 8), keyCopy)) {
|
||||
return CreateDecryptor(importedKey, ivCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object to perform AES decryption
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
private ICryptoTransform CreateDecryptor(SafeCapiKeyHandle key, byte[] iv) {
|
||||
Contract.Requires(key != null);
|
||||
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
|
||||
|
||||
return new CapiSymmetricAlgorithm(BlockSizeValue,
|
||||
FeedbackSizeValue,
|
||||
m_cspHandle,
|
||||
key,
|
||||
iv,
|
||||
Mode,
|
||||
PaddingValue,
|
||||
EncryptionMode.Decrypt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object to do AES encryption with the current key and IV
|
||||
/// </summary>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
public override ICryptoTransform CreateEncryptor() {
|
||||
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
|
||||
|
||||
if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
|
||||
GenerateKey();
|
||||
}
|
||||
|
||||
// ECB is the only mode which does not require an IV -- generate one here if we don't have one yet.
|
||||
if (Mode != CipherMode.ECB && IVValue == null) {
|
||||
GenerateIV();
|
||||
}
|
||||
|
||||
return CreateEncryptor(m_key, IVValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object to do AES encryption with the given key and IV
|
||||
/// </summary>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) {
|
||||
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
|
||||
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
if (!ValidKeySize(key.Length * 8)) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
|
||||
}
|
||||
if (iv != null && iv.Length * 8 != BlockSizeValue) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
|
||||
}
|
||||
|
||||
byte[] keyCopy = (byte[])key.Clone();
|
||||
byte[] ivCopy = null;
|
||||
if (iv != null) {
|
||||
ivCopy = (byte[])iv.Clone();
|
||||
}
|
||||
|
||||
using (SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, GetAlgorithmId(keyCopy.Length * 8), keyCopy)) {
|
||||
return CreateEncryptor(importedKey, ivCopy);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an object to perform AES encryption
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
private ICryptoTransform CreateEncryptor(SafeCapiKeyHandle key, byte[] iv) {
|
||||
Contract.Requires(key != null);
|
||||
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
|
||||
|
||||
return new CapiSymmetricAlgorithm(BlockSizeValue,
|
||||
FeedbackSizeValue,
|
||||
m_cspHandle,
|
||||
key,
|
||||
iv,
|
||||
Mode,
|
||||
PaddingValue,
|
||||
EncryptionMode.Encrypt);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Release any CAPI handles we're holding onto
|
||||
/// </summary>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
protected override void Dispose(bool disposing) {
|
||||
Contract.Ensures(!disposing || m_key == null || m_key.IsClosed);
|
||||
Contract.Ensures(!disposing || m_cspHandle == null || m_cspHandle.IsClosed);
|
||||
|
||||
try {
|
||||
if (disposing) {
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
|
||||
if (m_cspHandle != null) {
|
||||
m_cspHandle.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the size of AES keys supported by the given CSP, and which size should be used by default.
|
||||
///
|
||||
/// We assume that the same CSP will always be used by all instances of the AesCryptoServiceProvider
|
||||
/// in the current AppDomain. If we add the ability for users to choose which CSP to use on a
|
||||
/// per-instance basis, we need to update the code to account for the CSP when checking the cached
|
||||
/// key size values.
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
private static KeySizes[] FindSupportedKeySizes(SafeCspHandle csp, out int defaultKeySize) {
|
||||
Contract.Requires(csp != null);
|
||||
Contract.Ensures(Contract.Result<KeySizes[]>() != null);
|
||||
|
||||
// If this platform has any supported algorithm sizes, then the default key size should be set to a
|
||||
// reasonable value.
|
||||
Contract.Ensures(Contract.Result<KeySizes[]>().Length == 0 ||
|
||||
(Contract.ValueAtReturn<int>(out defaultKeySize) > 0 && Contract.ValueAtReturn<int>(out defaultKeySize) % 8 == 0));
|
||||
|
||||
if (s_supportedKeySizes == null) {
|
||||
List<KeySizes> keySizes = new List<KeySizes>();
|
||||
int maxKeySize = 0;
|
||||
|
||||
//
|
||||
// Enumerate the CSP's supported algorithms to see what key sizes it supports for AES
|
||||
//
|
||||
|
||||
CapiNative.PROV_ENUMALGS algorithm =
|
||||
CapiNative.GetProviderParameterStruct<CapiNative.PROV_ENUMALGS>(csp,
|
||||
CapiNative.ProviderParameter.EnumerateAlgorithms,
|
||||
CapiNative.ProviderParameterFlags.RestartEnumeration);
|
||||
|
||||
// Translate between CAPI AES algorithm IDs and supported key sizes
|
||||
while (algorithm.aiAlgId != CapiNative.AlgorithmId.None) {
|
||||
switch (algorithm.aiAlgId) {
|
||||
case CapiNative.AlgorithmId.Aes128:
|
||||
keySizes.Add(new KeySizes(128, 128, 0));
|
||||
if (128 > maxKeySize) {
|
||||
maxKeySize = 128;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case CapiNative.AlgorithmId.Aes192:
|
||||
keySizes.Add(new KeySizes(192, 192, 0));
|
||||
if (192 > maxKeySize) {
|
||||
maxKeySize = 192;
|
||||
}
|
||||
break;
|
||||
|
||||
case CapiNative.AlgorithmId.Aes256:
|
||||
keySizes.Add(new KeySizes(256, 256, 0));
|
||||
if (256 > maxKeySize) {
|
||||
maxKeySize = 256;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
algorithm = CapiNative.GetProviderParameterStruct<CapiNative.PROV_ENUMALGS>(csp,
|
||||
CapiNative.ProviderParameter.EnumerateAlgorithms,
|
||||
CapiNative.ProviderParameterFlags.None);
|
||||
}
|
||||
|
||||
s_supportedKeySizes = keySizes.ToArray();
|
||||
s_defaultKeySize = maxKeySize;
|
||||
}
|
||||
|
||||
defaultKeySize = s_defaultKeySize;
|
||||
return s_supportedKeySizes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a new random key
|
||||
/// </summary>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
public override void GenerateKey() {
|
||||
Contract.Ensures(m_key != null && !m_key.IsInvalid & !m_key.IsClosed);
|
||||
Contract.Assert(m_cspHandle != null);
|
||||
|
||||
SafeCapiKeyHandle key = null;
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try {
|
||||
if (!CapiNative.UnsafeNativeMethods.CryptGenKey(m_cspHandle,
|
||||
GetAlgorithmId(KeySizeValue),
|
||||
CapiNative.KeyFlags.Exportable,
|
||||
out key)) {
|
||||
throw new CryptographicException(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (key != null && !key.IsInvalid) {
|
||||
key.SetParentCsp(m_cspHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a random initialization vector
|
||||
/// </summary>
|
||||
[System.Security.SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
public override void GenerateIV() {
|
||||
Contract.Ensures(IVValue != null && IVValue.Length == BlockSizeValue / 8);
|
||||
Contract.Assert(m_cspHandle != null);
|
||||
Contract.Assert(BlockSizeValue % 8 == 0);
|
||||
|
||||
byte[] iv = new byte[BlockSizeValue / 8];
|
||||
if (!CapiNative.UnsafeNativeMethods.CryptGenRandom(m_cspHandle, iv.Length, iv)) {
|
||||
throw new CryptographicException(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
IVValue = iv;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Map an AES key size to the corresponding CAPI algorithm ID
|
||||
/// </summary>
|
||||
private static CapiNative.AlgorithmId GetAlgorithmId(int keySize) {
|
||||
// We should always return either a data encryption algorithm ID or None if we don't recognize the key size
|
||||
Contract.Ensures(
|
||||
((((int)Contract.Result<CapiNative.AlgorithmId>()) & (int)CapiNative.AlgorithmClass.DataEncryption) == (int)CapiNative.AlgorithmClass.DataEncryption) ||
|
||||
Contract.Result<CapiNative.AlgorithmId>() == CapiNative.AlgorithmId.None);
|
||||
|
||||
switch (keySize) {
|
||||
case 128:
|
||||
return CapiNative.AlgorithmId.Aes128;
|
||||
|
||||
case 192:
|
||||
return CapiNative.AlgorithmId.Aes192;
|
||||
|
||||
case 256:
|
||||
return CapiNative.AlgorithmId.Aes256;
|
||||
|
||||
default:
|
||||
return CapiNative.AlgorithmId.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
#if !SILVERLIGHT
|
||||
using System.Diagnostics.Contracts;
|
||||
#endif // !SILVERLIGHT
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Managed implementation of the AES algorithm. AES is esentially Rijndael with a fixed block size
|
||||
/// and iteration count, so we just wrap the RijndaelManaged class and allow only 128 bit blocks to
|
||||
/// be used.
|
||||
/// </summary>
|
||||
public sealed class AesManaged : Aes {
|
||||
private RijndaelManaged m_rijndael;
|
||||
|
||||
public AesManaged() {
|
||||
#if !SILVERLIGHT
|
||||
Contract.Ensures(m_rijndael != null);
|
||||
|
||||
if (CryptoConfig.AllowOnlyFipsAlgorithms) {
|
||||
throw new InvalidOperationException(SR.GetString(SR.Cryptography_NonCompliantFIPSAlgorithm));
|
||||
}
|
||||
#endif // !SILVERLIGHT
|
||||
|
||||
m_rijndael = new RijndaelManaged();
|
||||
m_rijndael.BlockSize = BlockSize;
|
||||
m_rijndael.KeySize = KeySize;
|
||||
}
|
||||
|
||||
#if !SILVERLIGHT
|
||||
public override int FeedbackSize {
|
||||
get { return m_rijndael.FeedbackSize; }
|
||||
set { m_rijndael.FeedbackSize = value; }
|
||||
}
|
||||
#endif // !SILVERLIGHT
|
||||
|
||||
public override byte[] IV {
|
||||
get { return m_rijndael.IV; }
|
||||
set { m_rijndael.IV = value; }
|
||||
}
|
||||
|
||||
public override byte[] Key {
|
||||
get { return m_rijndael.Key; }
|
||||
set { m_rijndael.Key = value; }
|
||||
}
|
||||
|
||||
public override int KeySize {
|
||||
get { return m_rijndael.KeySize; }
|
||||
set { m_rijndael.KeySize = value; }
|
||||
}
|
||||
|
||||
#if !SILVERLIGHT
|
||||
public override CipherMode Mode {
|
||||
get { return m_rijndael.Mode; }
|
||||
|
||||
set {
|
||||
Contract.Ensures(m_rijndael.Mode != CipherMode.CFB && m_rijndael.Mode != CipherMode.OFB);
|
||||
|
||||
// RijndaelManaged will implicitly change the block size of an algorithm to match the number
|
||||
// of feedback bits being used. Since AES requires a block size of 128 bits, we cannot allow
|
||||
// the user to use the feedback modes, as this will end up breaking that invarient.
|
||||
if (value == CipherMode.CFB || value == CipherMode.OFB) {
|
||||
throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidCipherMode));
|
||||
}
|
||||
|
||||
m_rijndael.Mode = value;
|
||||
}
|
||||
}
|
||||
|
||||
public override PaddingMode Padding {
|
||||
get { return m_rijndael.Padding; }
|
||||
set { m_rijndael.Padding = value; }
|
||||
}
|
||||
#endif // !SILVERLIGHT
|
||||
|
||||
public override ICryptoTransform CreateDecryptor() {
|
||||
return m_rijndael.CreateDecryptor();
|
||||
}
|
||||
|
||||
public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) {
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
#if !SILVERLIGHT
|
||||
if (!ValidKeySize(key.Length * 8)) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
|
||||
}
|
||||
if (iv != null && iv.Length * 8 != BlockSizeValue) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
|
||||
}
|
||||
#endif
|
||||
|
||||
return m_rijndael.CreateDecryptor(key, iv);
|
||||
}
|
||||
|
||||
|
||||
public override ICryptoTransform CreateEncryptor() {
|
||||
return m_rijndael.CreateEncryptor();
|
||||
}
|
||||
|
||||
public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) {
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
#if !SILVERLIGHT
|
||||
if (!ValidKeySize(key.Length * 8)) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
|
||||
}
|
||||
if (iv != null && iv.Length * 8 != BlockSizeValue) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
|
||||
}
|
||||
#endif // SILVERLIGHT
|
||||
|
||||
return m_rijndael.CreateEncryptor(key, iv);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing) {
|
||||
try {
|
||||
if (disposing) {
|
||||
(m_rijndael as IDisposable).Dispose();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
public override void GenerateIV() {
|
||||
m_rijndael.GenerateIV();
|
||||
}
|
||||
|
||||
public override void GenerateKey() {
|
||||
m_rijndael.GenerateKey();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
|
||||
/// <summar>
|
||||
/// Implementation of SafeBCryptAlgorithmHandle Cache.
|
||||
/// </summary>
|
||||
internal sealed class BCryptAlgorithmHandleCache {
|
||||
[SecurityCritical]
|
||||
private Dictionary<string, WeakReference> m_algorithmHandles;
|
||||
|
||||
[SecurityCritical]
|
||||
public BCryptAlgorithmHandleCache()
|
||||
{
|
||||
m_algorithmHandles = new Dictionary<string, WeakReference>();
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public SafeBCryptAlgorithmHandle GetCachedAlgorithmHandle(string algorithm, string implementation) {
|
||||
string handleKey = algorithm + implementation;
|
||||
SafeBCryptAlgorithmHandle algorithmHandle = null;
|
||||
|
||||
if (m_algorithmHandles.ContainsKey(handleKey))
|
||||
{
|
||||
algorithmHandle = m_algorithmHandles[handleKey].Target as SafeBCryptAlgorithmHandle;
|
||||
if (algorithmHandle != null)
|
||||
{
|
||||
return algorithmHandle;
|
||||
}
|
||||
}
|
||||
|
||||
algorithmHandle = BCryptNative.OpenAlgorithm(algorithm, implementation);
|
||||
m_algorithmHandles[handleKey] = new WeakReference(algorithmHandle);
|
||||
return algorithmHandle;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of a generic BCrypt hashing algorithm, concrete HashAlgorithm classes
|
||||
/// implemented by BCrypt can contain an instance of this class and delegate the work to it.
|
||||
/// </summary>
|
||||
internal sealed class BCryptHashAlgorithm : IDisposable {
|
||||
[ThreadStatic]
|
||||
[SecurityCritical]
|
||||
private static BCryptAlgorithmHandleCache _algorithmCache;
|
||||
[SecurityCritical]
|
||||
private SafeBCryptAlgorithmHandle m_algorithmHandle;
|
||||
[SecurityCritical]
|
||||
private SafeBCryptHashHandle m_hashHandle;
|
||||
|
||||
// SafeCritical - we're not exposing out anything that we want to prevent untrusted code from getting at
|
||||
[SecuritySafeCritical]
|
||||
public BCryptHashAlgorithm(CngAlgorithm algorithm, string implementation) {
|
||||
Contract.Requires(algorithm != null);
|
||||
Contract.Requires(!String.IsNullOrEmpty(implementation));
|
||||
Contract.Ensures(m_algorithmHandle != null && !m_algorithmHandle.IsInvalid && !m_algorithmHandle.IsClosed);
|
||||
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
|
||||
|
||||
// Make sure CNG is supported on this platform
|
||||
if (!BCryptNative.BCryptSupported) {
|
||||
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
|
||||
}
|
||||
|
||||
if (_algorithmCache == null)
|
||||
{
|
||||
_algorithmCache = new BCryptAlgorithmHandleCache();
|
||||
}
|
||||
|
||||
m_algorithmHandle = _algorithmCache.GetCachedAlgorithmHandle(algorithm.Algorithm, implementation);
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up the hash algorithm
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public void Dispose() {
|
||||
Contract.Ensures(m_hashHandle == null || m_hashHandle.IsClosed);
|
||||
Contract.Ensures(m_algorithmHandle == null || m_algorithmHandle.IsClosed);
|
||||
|
||||
if (m_hashHandle != null) {
|
||||
m_hashHandle.Dispose();
|
||||
}
|
||||
|
||||
if (m_algorithmHandle != null) {
|
||||
m_algorithmHandle = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the hash algorithm to begin hashing a new set of data
|
||||
/// </summary>
|
||||
// SafeCritical - we're not exposing out anything that we want to prevent untrusted code from getting
|
||||
// at. We've also made sure not to leak any native resources out to partial trust code
|
||||
// and we control all native inputs.
|
||||
[SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
public void Initialize() {
|
||||
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
|
||||
Contract.Assert(m_algorithmHandle != null);
|
||||
|
||||
// Try to create a new hash algorithm to use
|
||||
SafeBCryptHashHandle newHashAlgorithm = null;
|
||||
IntPtr hashObjectBuffer = IntPtr.Zero;
|
||||
|
||||
// Creating a BCRYPT_HASH_HANDLE requires providing a buffer to hold the hash object in, which
|
||||
// is tied to the lifetime of the hash handle. Wrap this in a CER so we can tie the lifetimes together
|
||||
// safely.
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try {
|
||||
|
||||
int hashObjectSize = BCryptNative.GetInt32Property(m_algorithmHandle,
|
||||
BCryptNative.ObjectPropertyName.ObjectLength);
|
||||
Debug.Assert(hashObjectSize > 0, "hashObjectSize > 0");
|
||||
|
||||
// Allocate in a CER because we could fail between the alloc and the assignment
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try { }
|
||||
finally {
|
||||
hashObjectBuffer = Marshal.AllocCoTaskMem(hashObjectSize);
|
||||
}
|
||||
|
||||
BCryptNative.ErrorCode error = BCryptNative.UnsafeNativeMethods.BCryptCreateHash(m_algorithmHandle,
|
||||
out newHashAlgorithm,
|
||||
hashObjectBuffer,
|
||||
hashObjectSize,
|
||||
IntPtr.Zero,
|
||||
0,
|
||||
0);
|
||||
|
||||
if (error != BCryptNative.ErrorCode.Success) {
|
||||
throw new CryptographicException((int)error);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// Make sure we've successfully transfered ownership of the hash object buffer to the safe handle
|
||||
if (hashObjectBuffer != IntPtr.Zero) {
|
||||
// If we created the safe handle, it needs to own the buffer and free it in release
|
||||
if (newHashAlgorithm != null) {
|
||||
newHashAlgorithm.HashObject = hashObjectBuffer;
|
||||
}
|
||||
else {
|
||||
Marshal.FreeCoTaskMem(hashObjectBuffer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// If we could create it, dispose of any old hash handle we had and replace it with the new one
|
||||
if (m_hashHandle != null) {
|
||||
m_hashHandle.Dispose();
|
||||
}
|
||||
m_hashHandle = newHashAlgorithm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hash a block of data
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public void HashCore(byte[] array, int ibStart, int cbSize) {
|
||||
Contract.Assert(m_hashHandle != null);
|
||||
|
||||
if (array == null) {
|
||||
throw new ArgumentNullException("array");
|
||||
}
|
||||
if (ibStart < 0 || ibStart > array.Length - cbSize) {
|
||||
throw new ArgumentOutOfRangeException("ibStart");
|
||||
}
|
||||
if (cbSize < 0 || cbSize > array.Length) {
|
||||
throw new ArgumentOutOfRangeException("cbSize");
|
||||
}
|
||||
|
||||
byte[] hashData = new byte[cbSize];
|
||||
Buffer.BlockCopy(array, ibStart, hashData, 0, cbSize);
|
||||
|
||||
BCryptNative.ErrorCode error = BCryptNative.UnsafeNativeMethods.BCryptHashData(m_hashHandle,
|
||||
hashData,
|
||||
hashData.Length,
|
||||
0);
|
||||
|
||||
if (error != BCryptNative.ErrorCode.Success) {
|
||||
throw new CryptographicException((int)error);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete the hash, returning its value
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public byte[] HashFinal() {
|
||||
Contract.Ensures(Contract.Result<byte[]>() != null);
|
||||
Contract.Assert(m_hashHandle != null);
|
||||
|
||||
int hashSize = BCryptNative.GetInt32Property(m_hashHandle, BCryptNative.HashPropertyName.HashLength);
|
||||
|
||||
byte[] hashValue = new byte[hashSize];
|
||||
BCryptNative.ErrorCode error = BCryptNative.UnsafeNativeMethods.BCryptFinishHash(m_hashHandle,
|
||||
hashValue,
|
||||
hashValue.Length,
|
||||
0);
|
||||
|
||||
if (error != BCryptNative.ErrorCode.Success) {
|
||||
throw new CryptographicException((int)error);
|
||||
}
|
||||
|
||||
return hashValue;
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public void HashStream(Stream stream) {
|
||||
Contract.Requires(stream != null);
|
||||
|
||||
// Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm
|
||||
byte[] buffer = new byte[4096];
|
||||
int bytesRead = 0;
|
||||
do {
|
||||
bytesRead = stream.Read(buffer, 0, buffer.Length);
|
||||
if (bytesRead > 0) {
|
||||
HashCore(buffer, 0, bytesRead);
|
||||
}
|
||||
} while (bytesRead > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Win32;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
|
||||
internal enum AsymmetricPaddingMode {
|
||||
/// <summary>
|
||||
/// No padding
|
||||
/// </summary>
|
||||
None = 1, // BCRYPT_PAD_NONE
|
||||
|
||||
/// <summary>
|
||||
/// PKCS #1 padding
|
||||
/// </summary>
|
||||
Pkcs1 = 2, // BCRYPT_PAD_PKCS1
|
||||
|
||||
/// <summary>
|
||||
/// Optimal Asymmetric Encryption Padding
|
||||
/// </summary>
|
||||
Oaep = 4, // BCRYPT_PAD_OAEP
|
||||
|
||||
/// <summary>
|
||||
/// Probabilistic Signature Scheme padding
|
||||
/// </summary>
|
||||
Pss = 8 // BCRYPT_PAD_PSS
|
||||
}
|
||||
/// <summary>
|
||||
/// Native interop with CNG's BCrypt layer. Native definitions can be found in bcrypt.h
|
||||
/// </summary>
|
||||
internal static class BCryptNative {
|
||||
/// <summary>
|
||||
/// Well known algorithm names
|
||||
/// </summary>
|
||||
internal static class AlgorithmName {
|
||||
public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM
|
||||
public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM
|
||||
public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM
|
||||
public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM
|
||||
public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM
|
||||
public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM
|
||||
public const string MD5 = "MD5"; // BCRYPT_MD5_ALGORITHM
|
||||
public const string Sha1 = "SHA1"; // BCRYPT_SHA1_ALGORITHM
|
||||
public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM
|
||||
public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM
|
||||
public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM
|
||||
internal const string Rsa = "RSA"; // BCRYPT_RSA_ALGORITHM
|
||||
}
|
||||
#if !MONO
|
||||
/// <summary>
|
||||
/// Well known key blob tyes
|
||||
/// </summary>
|
||||
internal static class KeyBlobType {
|
||||
//During Win8 Windows introduced BCRYPT_PUBLIC_KEY_BLOB L"PUBLICBLOB"
|
||||
//and #define BCRYPT_PRIVATE_KEY_BLOB L"PRIVATEBLOB". We should use the
|
||||
//same on ProjectN and ProjectK
|
||||
internal const string RsaFullPrivateBlob = "RSAFULLPRIVATEBLOB"; // BCRYPT_RSAFULLPRIVATE_BLOB
|
||||
internal const string RsaPrivateBlob = "RSAPRIVATEBLOB"; // BCRYPT_RSAPRIVATE_BLOB
|
||||
internal const string RsaPublicBlob = "RSAPUBLICBLOB"; // BCRYPT_PUBLIC_KEY_BLOB
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct BCRYPT_RSAKEY_BLOB {
|
||||
internal KeyBlobMagicNumber Magic;
|
||||
internal int BitLength;
|
||||
internal int cbPublicExp;
|
||||
internal int cbModulus;
|
||||
internal int cbPrime1;
|
||||
internal int cbPrime2;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result codes from BCrypt APIs
|
||||
/// </summary>
|
||||
internal enum ErrorCode {
|
||||
Success = 0x00000000, // STATUS_SUCCESS
|
||||
BufferToSmall = unchecked((int)0xC0000023), // STATUS_BUFFER_TOO_SMALL
|
||||
ObjectNameNotFound = unchecked((int)0xC0000034) // SATUS_OBJECT_NAME_NOT_FOUND
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Well known BCrypt hash property names
|
||||
/// </summary>
|
||||
internal static class HashPropertyName {
|
||||
public const string HashLength = "HashDigestLength"; // BCRYPT_HASH_LENGTH
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Magic numbers identifying blob types
|
||||
/// </summary>
|
||||
internal enum KeyBlobMagicNumber {
|
||||
ECDHPublicP256 = 0x314B4345, // BCRYPT_ECDH_PUBLIC_P256_MAGIC
|
||||
ECDHPublicP384 = 0x334B4345, // BCRYPT_ECDH_PUBLIC_P384_MAGIC
|
||||
ECDHPublicP521 = 0x354B4345, // BCRYPT_ECDH_PUBLIC_P521_MAGIC
|
||||
ECDsaPublicP256 = 0x31534345, // BCRYPT_ECDSA_PUBLIC_P256_MAGIC
|
||||
ECDsaPublicP384 = 0x33534345, // BCRYPT_ECDSA_PUBLIC_P384_MAGIC
|
||||
ECDsaPublicP521 = 0x35534345, // BCRYPT_ECDSA_PUBLIC_P521_MAGIC
|
||||
RsaPublic = 0x31415352, // BCRYPT_RSAPUBLIC_MAGIC
|
||||
RsaPrivate = 0x32415352, // BCRYPT_RSAPRIVATE_MAGIC
|
||||
RsaFullPrivateMagic = 0x33415352, //BCRYPT_RSAFULLPRIVATE_MAGIC
|
||||
KeyDataBlob = 0x4d42444b // BCRYPT_KEY_DATA_BLOB_MAGIC
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct BCRYPT_OAEP_PADDING_INFO {
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
internal string pszAlgId;
|
||||
|
||||
internal IntPtr pbLabel;
|
||||
|
||||
internal int cbLabel;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct BCRYPT_PKCS1_PADDING_INFO {
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
internal string pszAlgId;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct BCRYPT_PSS_PADDING_INFO {
|
||||
[MarshalAs(UnmanagedType.LPWStr)]
|
||||
internal string pszAlgId;
|
||||
|
||||
internal int cbSalt;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Well known KDF names
|
||||
/// </summary>
|
||||
internal static class KeyDerivationFunction {
|
||||
public const string Hash = "HASH"; // BCRYPT_KDF_HASH
|
||||
public const string Hmac = "HMAC"; // BCRYPT_KDF_HMAC
|
||||
public const string Tls = "TLS_PRF"; // BCRYPT_KDF_TLS_PRF
|
||||
}
|
||||
|
||||
internal const string BCRYPT_ECCPUBLIC_BLOB = "ECCPUBLICBLOB";
|
||||
internal const string BCRYPT_ECCPRIVATE_BLOB = "ECCPRIVATEBLOB";
|
||||
|
||||
/// <summary>
|
||||
/// Well known BCrypt provider names
|
||||
/// </summary>
|
||||
internal static class ProviderName {
|
||||
public const string MicrosoftPrimitiveProvider = "Microsoft Primitive Provider"; // MS_PRIMITIVE_PROVIDER
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Well known BCrypt object property names
|
||||
/// </summary>
|
||||
internal static class ObjectPropertyName {
|
||||
public const string ObjectLength = "ObjectLength"; // BCRYPT_OBJECT_LENGTH
|
||||
}
|
||||
|
||||
#pragma warning disable 618 // Have not migrated to v4 transparency yet
|
||||
[SecurityCritical(SecurityCriticalScope.Everything)]
|
||||
#pragma warning restore 618
|
||||
[SuppressUnmanagedCodeSecurity]
|
||||
internal static class UnsafeNativeMethods {
|
||||
/// <summary>
|
||||
/// Create a hash object
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern ErrorCode BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm,
|
||||
[Out] out SafeBCryptHashHandle phHash,
|
||||
IntPtr pbHashObject,
|
||||
int cbHashObject,
|
||||
IntPtr pbSecret,
|
||||
int cbSecret,
|
||||
int dwFlags);
|
||||
|
||||
/// <summary>
|
||||
/// Get a property from a BCrypt algorithm object
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern ErrorCode BCryptGetProperty(SafeBCryptAlgorithmHandle hObject,
|
||||
string pszProperty,
|
||||
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
|
||||
int cbOutput,
|
||||
[In, Out] ref int pcbResult,
|
||||
int flags);
|
||||
|
||||
/// <summary>
|
||||
/// Get a property from a BCrypt algorithm object
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
|
||||
internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
|
||||
string pszProperty,
|
||||
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
|
||||
int cbOutput,
|
||||
[In, Out] ref int pcbResult,
|
||||
int flags);
|
||||
|
||||
/// <summary>
|
||||
/// Get a property from a BCrypt hash object
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
|
||||
internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
|
||||
string pszProperty,
|
||||
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
|
||||
int cbOutput,
|
||||
[In, Out] ref int pcbResult,
|
||||
int flags);
|
||||
|
||||
/// <summary>
|
||||
/// Get the hash value of the data
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll")]
|
||||
internal static extern ErrorCode BCryptFinishHash(SafeBCryptHashHandle hHash,
|
||||
[MarshalAs(UnmanagedType.LPArray), Out] byte[] pbInput,
|
||||
int cbInput,
|
||||
int dwFlags);
|
||||
|
||||
/// <summary>
|
||||
/// Hash a block of data
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll")]
|
||||
internal static extern ErrorCode BCryptHashData(SafeBCryptHashHandle hHash,
|
||||
[MarshalAs(UnmanagedType.LPArray), In] byte[] pbInput,
|
||||
int cbInput,
|
||||
int dwFlags);
|
||||
|
||||
/// <summary>
|
||||
/// Get a handle to an algorithm provider
|
||||
/// </summary>
|
||||
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
|
||||
internal static extern ErrorCode BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm,
|
||||
string pszAlgId, // BCryptAlgorithm
|
||||
string pszImplementation, // ProviderNames
|
||||
int dwFlags);
|
||||
|
||||
[DllImport("bcrypt.dll", SetLastError = true)]
|
||||
internal static extern ErrorCode BCryptExportKey([In]SafeBCryptKeyHandle hKey,
|
||||
[In]IntPtr hExportKey,
|
||||
[In][MarshalAs(UnmanagedType.LPWStr)] string pszBlobType,
|
||||
[Out, MarshalAs(UnmanagedType.LPArray)] byte[] pbOutput,
|
||||
[In]int cbOutput,
|
||||
[In]ref int pcbResult,
|
||||
[In] int dwFlags);
|
||||
|
||||
[DllImport("Crypt32.dll", SetLastError = true)]
|
||||
internal static extern int CryptImportPublicKeyInfoEx2([In] uint dwCertEncodingType,
|
||||
[In] ref X509Native.CERT_PUBLIC_KEY_INFO pInfo,
|
||||
[In] int dwFlags,
|
||||
[In] IntPtr pvAuxInfo,
|
||||
[Out] out SafeBCryptKeyHandle phKey);
|
||||
}
|
||||
|
||||
//
|
||||
// Wrapper and utility functions
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// Adapter to wrap specific BCryptGetProperty P/Invokes with a generic handle type
|
||||
/// </summary>
|
||||
#pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
|
||||
[SecurityCritical(SecurityCriticalScope.Everything)]
|
||||
#pragma warning restore 618
|
||||
private delegate ErrorCode BCryptPropertyGetter<T>(T hObject,
|
||||
string pszProperty,
|
||||
byte[] pbOutput,
|
||||
int cbOutput,
|
||||
ref int pcbResult,
|
||||
int dwFlags) where T : SafeHandle;
|
||||
|
||||
private static volatile bool s_haveBcryptSupported;
|
||||
private static volatile bool s_bcryptSupported;
|
||||
|
||||
/// <summary>
|
||||
/// Determine if BCrypt is supported on the current machine
|
||||
/// </summary>
|
||||
internal static bool BCryptSupported {
|
||||
[SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
get {
|
||||
if (!s_haveBcryptSupported)
|
||||
{
|
||||
// Attempt to load bcrypt.dll to see if the BCrypt CNG APIs are available on the machine
|
||||
using (SafeLibraryHandle bcrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("bcrypt", IntPtr.Zero, 0)) {
|
||||
s_bcryptSupported = !bcrypt.IsInvalid;
|
||||
s_haveBcryptSupported = true;
|
||||
}
|
||||
}
|
||||
|
||||
return s_bcryptSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a DWORD property of a BCrypt object
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle {
|
||||
Contract.Requires(algorithm != null);
|
||||
Contract.Requires(property == HashPropertyName.HashLength ||
|
||||
property == ObjectPropertyName.ObjectLength);
|
||||
|
||||
return BitConverter.ToInt32(GetProperty(algorithm, property), 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the value of a property of a BCrypt object
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
internal static byte[] GetProperty<T>(T algorithm, string property) where T : SafeHandle {
|
||||
Contract.Requires(algorithm != null);
|
||||
Contract.Requires(!String.IsNullOrEmpty(property));
|
||||
Contract.Ensures(Contract.Result<byte[]>() != null);
|
||||
|
||||
BCryptPropertyGetter<T> getter = null;
|
||||
if (typeof(T) == typeof(SafeBCryptAlgorithmHandle)) {
|
||||
getter = new BCryptPropertyGetter<SafeBCryptAlgorithmHandle>(UnsafeNativeMethods.BCryptGetAlgorithmProperty)
|
||||
as BCryptPropertyGetter<T>;
|
||||
}
|
||||
else if (typeof(T) == typeof(SafeBCryptHashHandle)) {
|
||||
getter = new BCryptPropertyGetter<SafeBCryptHashHandle>(UnsafeNativeMethods.BCryptGetHashProperty)
|
||||
as BCryptPropertyGetter<T>;
|
||||
}
|
||||
|
||||
Debug.Assert(getter != null, "Unknown handle type");
|
||||
|
||||
// Figure out how big the property is
|
||||
int bufferSize = 0;
|
||||
ErrorCode error = getter(algorithm, property, null, 0, ref bufferSize, 0);
|
||||
|
||||
if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success) {
|
||||
throw new CryptographicException((int)error);
|
||||
}
|
||||
|
||||
// Allocate the buffer, and return the property
|
||||
Debug.Assert(bufferSize > 0, "bufferSize > 0");
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
error = getter(algorithm, property, buffer, buffer.Length, ref bufferSize, 0);
|
||||
|
||||
if (error != ErrorCode.Success) {
|
||||
throw new CryptographicException((int)error);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Map an algorithm identifier to a key size and magic number
|
||||
/// </summary>
|
||||
internal static void MapAlgorithmIdToMagic(string algorithm,
|
||||
out KeyBlobMagicNumber algorithmMagic,
|
||||
out int keySize) {
|
||||
Contract.Requires(!String.IsNullOrEmpty(algorithm));
|
||||
|
||||
switch (algorithm) {
|
||||
case AlgorithmName.ECDHP256:
|
||||
algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256;
|
||||
keySize = 256;
|
||||
break;
|
||||
|
||||
case AlgorithmName.ECDHP384:
|
||||
algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384;
|
||||
keySize = 384;
|
||||
break;
|
||||
|
||||
case AlgorithmName.ECDHP521:
|
||||
algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521;
|
||||
keySize = 521;
|
||||
break;
|
||||
|
||||
case AlgorithmName.ECDsaP256:
|
||||
algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256;
|
||||
keySize = 256;
|
||||
break;
|
||||
|
||||
case AlgorithmName.ECDsaP384:
|
||||
algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384;
|
||||
keySize = 384;
|
||||
break;
|
||||
|
||||
case AlgorithmName.ECDsaP521:
|
||||
algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521;
|
||||
keySize = 521;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open a handle to an algorithm provider
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm, string implementation) {
|
||||
Contract.Requires(!String.IsNullOrEmpty(algorithm));
|
||||
Contract.Requires(!String.IsNullOrEmpty(implementation));
|
||||
Contract.Ensures(Contract.Result<SafeBCryptAlgorithmHandle>() != null &&
|
||||
!Contract.Result<SafeBCryptAlgorithmHandle>().IsInvalid &&
|
||||
!Contract.Result<SafeBCryptAlgorithmHandle>().IsClosed);
|
||||
|
||||
SafeBCryptAlgorithmHandle algorithmHandle = null;
|
||||
ErrorCode error = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algorithmHandle,
|
||||
algorithm,
|
||||
implementation,
|
||||
0);
|
||||
|
||||
if (error != ErrorCode.Success) {
|
||||
throw new CryptographicException((int)error);
|
||||
}
|
||||
|
||||
return algorithmHandle;
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
internal static SafeBCryptKeyHandle ImportAsymmetricPublicKey(X509Native.CERT_PUBLIC_KEY_INFO certPublicKeyInfo, int dwFlag) {
|
||||
SafeBCryptKeyHandle keyHandle = null;
|
||||
int error = UnsafeNativeMethods.CryptImportPublicKeyInfoEx2(
|
||||
X509Native.X509_ASN_ENCODING,
|
||||
ref certPublicKeyInfo,
|
||||
dwFlag,
|
||||
IntPtr.Zero,
|
||||
out keyHandle);
|
||||
if (error == 0) {
|
||||
throw new CryptographicException(Marshal.GetLastWin32Error());
|
||||
}
|
||||
return keyHandle;
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
internal static byte[] ExportBCryptKey(SafeBCryptKeyHandle hKey, string blobType) {
|
||||
byte[] keyBlob = null;
|
||||
int length = 0;
|
||||
|
||||
ErrorCode error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, null, 0, ref length, 0);
|
||||
|
||||
if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success)
|
||||
{
|
||||
throw new CryptographicException(Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
keyBlob = new byte[length];
|
||||
error = UnsafeNativeMethods.BCryptExportKey(hKey, IntPtr.Zero, blobType, keyBlob, length, ref length, 0);
|
||||
if (error != ErrorCode.Success) {
|
||||
throw new CryptographicException(Marshal.GetLastWin32Error());
|
||||
}
|
||||
return keyBlob;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Implementation of a generic CAPI hashing algorithm, concrete HashAlgorithm classes
|
||||
/// implemented by CAPI can contain an instance of this class and delegate the work to it
|
||||
/// </summary>
|
||||
internal sealed class CapiHashAlgorithm : IDisposable {
|
||||
private CapiNative.AlgorithmId m_algorithmId;
|
||||
[SecurityCritical]
|
||||
private SafeCspHandle m_cspHandle;
|
||||
[SecurityCritical]
|
||||
private SafeCapiHashHandle m_hashHandle;
|
||||
|
||||
[SecuritySafeCritical]
|
||||
// SafeCritical - we're not exposing out anything that we want to prevent untrusted code from getting at
|
||||
public CapiHashAlgorithm(string provider,
|
||||
CapiNative.ProviderType providerType,
|
||||
CapiNative.AlgorithmId algorithm) {
|
||||
Contract.Requires(!String.IsNullOrEmpty(provider));
|
||||
Contract.Requires((CapiNative.AlgorithmClass)((uint)algorithm & (uint)CapiNative.AlgorithmClass.Hash) == CapiNative.AlgorithmClass.Hash);
|
||||
Contract.Ensures(m_cspHandle != null && !m_cspHandle.IsInvalid && !m_cspHandle.IsClosed);
|
||||
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
|
||||
|
||||
m_algorithmId = algorithm;
|
||||
m_cspHandle = CapiNative.AcquireCsp(null,
|
||||
provider,
|
||||
providerType,
|
||||
CapiNative.CryptAcquireContextFlags.VerifyContext,
|
||||
true);
|
||||
Initialize();
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public void Dispose() {
|
||||
Contract.Ensures(m_hashHandle == null || m_hashHandle.IsClosed);
|
||||
Contract.Ensures(m_cspHandle == null || m_cspHandle.IsClosed);
|
||||
|
||||
if (m_hashHandle != null) {
|
||||
m_hashHandle.Dispose();
|
||||
}
|
||||
|
||||
if (m_cspHandle != null) {
|
||||
m_cspHandle.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the hash algorithm to begin hashing a new set of data
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
|
||||
public void Initialize() {
|
||||
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
|
||||
Contract.Assert(m_cspHandle != null);
|
||||
|
||||
// Try to create a new hash algorithm to use
|
||||
SafeCapiHashHandle newHashAlgorithm = null;
|
||||
|
||||
RuntimeHelpers.PrepareConstrainedRegions();
|
||||
try {
|
||||
if (!CapiNative.UnsafeNativeMethods.CryptCreateHash(m_cspHandle,
|
||||
m_algorithmId,
|
||||
SafeCapiKeyHandle.InvalidHandle,
|
||||
0,
|
||||
out newHashAlgorithm)) {
|
||||
// BadAlgorithmId means that this CSP does not support the specified algorithm, which means
|
||||
// that we're on a platform that does not support the given hash function.
|
||||
int error = Marshal.GetLastWin32Error();
|
||||
if (error == (int)CapiNative.ErrorCode.BadAlgorithmId) {
|
||||
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
|
||||
}
|
||||
else {
|
||||
throw new CryptographicException(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (newHashAlgorithm != null && !newHashAlgorithm.IsInvalid) {
|
||||
newHashAlgorithm.SetParentCsp(m_cspHandle);
|
||||
}
|
||||
}
|
||||
|
||||
// If we created a new algorithm, dispose of the old one and use the new one
|
||||
Debug.Assert(newHashAlgorithm != null, "newHashAlgorithm != null");
|
||||
if (m_hashHandle != null) {
|
||||
m_hashHandle.Dispose();
|
||||
}
|
||||
m_hashHandle = newHashAlgorithm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hash a block of data
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public void HashCore(byte[] array, int ibStart, int cbSize) {
|
||||
Contract.Assert(m_hashHandle != null);
|
||||
|
||||
if (array == null) {
|
||||
throw new ArgumentNullException("array");
|
||||
}
|
||||
if (ibStart < 0 || ibStart > array.Length - cbSize) {
|
||||
throw new ArgumentOutOfRangeException("ibStart");
|
||||
}
|
||||
if (cbSize < 0 || cbSize > array.Length) {
|
||||
throw new ArgumentOutOfRangeException("cbSize");
|
||||
}
|
||||
|
||||
byte[] hashData = new byte[cbSize];
|
||||
Buffer.BlockCopy(array, ibStart, hashData, 0, cbSize);
|
||||
|
||||
if (!CapiNative.UnsafeNativeMethods.CryptHashData(m_hashHandle, hashData, cbSize, 0)) {
|
||||
throw new CryptographicException(Marshal.GetLastWin32Error());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete the hash, returning its value
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public byte[] HashFinal() {
|
||||
Contract.Ensures(Contract.Result<byte[]>() != null);
|
||||
Contract.Assert(m_hashHandle != null);
|
||||
|
||||
return CapiNative.GetHashParameter(m_hashHandle, CapiNative.HashParameter.HashValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,245 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Utility class to strongly type algorithms used with CNG. Since all CNG APIs which require an
|
||||
/// algorithm name take the name as a string, we use this string wrapper class to specifically mark
|
||||
/// which parameters are expected to be algorithms. We also provide a list of well known algorithm
|
||||
/// names, which helps Intellisense users find a set of good algorithm names to use.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngAlgorithm : IEquatable<CngAlgorithm> {
|
||||
private static volatile CngAlgorithm s_ecdhp256;
|
||||
private static volatile CngAlgorithm s_ecdhp384;
|
||||
private static volatile CngAlgorithm s_ecdhp521;
|
||||
private static volatile CngAlgorithm s_ecdsap256;
|
||||
private static volatile CngAlgorithm s_ecdsap384;
|
||||
private static volatile CngAlgorithm s_ecdsap521;
|
||||
private static volatile CngAlgorithm s_md5;
|
||||
private static volatile CngAlgorithm s_sha1;
|
||||
private static volatile CngAlgorithm s_sha256;
|
||||
private static volatile CngAlgorithm s_sha384;
|
||||
private static volatile CngAlgorithm s_sha512;
|
||||
private static volatile CngAlgorithm s_rsa;
|
||||
|
||||
private string m_algorithm;
|
||||
|
||||
public CngAlgorithm(string algorithm) {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(m_algorithm));
|
||||
|
||||
if (algorithm == null) {
|
||||
throw new ArgumentNullException("algorithm");
|
||||
}
|
||||
if (algorithm.Length == 0) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidAlgorithmName, algorithm), "algorithm");
|
||||
}
|
||||
|
||||
m_algorithm = algorithm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the algorithm
|
||||
/// </summary>
|
||||
public string Algorithm {
|
||||
get {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
|
||||
return m_algorithm;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator==(CngAlgorithm left, CngAlgorithm right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public static bool operator !=(CngAlgorithm left, CngAlgorithm right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return !Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
Contract.Assert(m_algorithm != null);
|
||||
|
||||
return Equals(obj as CngAlgorithm);
|
||||
}
|
||||
|
||||
public bool Equals(CngAlgorithm other) {
|
||||
if (Object.ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_algorithm.Equals(other.Algorithm);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
Contract.Assert(m_algorithm != null);
|
||||
return m_algorithm.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
Contract.Assert(m_algorithm != null);
|
||||
return m_algorithm;
|
||||
}
|
||||
|
||||
//
|
||||
// Well known algorithms
|
||||
//
|
||||
|
||||
public static CngAlgorithm Rsa {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
if (s_rsa == null) {
|
||||
s_rsa = new CngAlgorithm(BCryptNative.AlgorithmName.Rsa);
|
||||
}
|
||||
return s_rsa;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm ECDiffieHellmanP256 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_ecdhp256 == null) {
|
||||
s_ecdhp256 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDHP256);
|
||||
}
|
||||
|
||||
return s_ecdhp256;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm ECDiffieHellmanP384 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_ecdhp384 == null) {
|
||||
s_ecdhp384 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDHP384);
|
||||
}
|
||||
|
||||
return s_ecdhp384;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm ECDiffieHellmanP521 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_ecdhp521 == null) {
|
||||
s_ecdhp521 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDHP521);
|
||||
}
|
||||
|
||||
return s_ecdhp521;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm ECDsaP256 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_ecdsap256 == null) {
|
||||
s_ecdsap256 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsaP256);
|
||||
}
|
||||
|
||||
return s_ecdsap256;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm ECDsaP384 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_ecdsap384 == null) {
|
||||
s_ecdsap384 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsaP384);
|
||||
}
|
||||
|
||||
return s_ecdsap384;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm ECDsaP521 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_ecdsap521 == null) {
|
||||
s_ecdsap521 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsaP521);
|
||||
}
|
||||
|
||||
return s_ecdsap521;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm MD5 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_md5 == null) {
|
||||
s_md5 = new CngAlgorithm(BCryptNative.AlgorithmName.MD5);
|
||||
}
|
||||
|
||||
return s_md5;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm Sha1 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_sha1 == null) {
|
||||
s_sha1 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha1);
|
||||
}
|
||||
|
||||
return s_sha1;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm Sha256 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_sha256 == null) {
|
||||
s_sha256 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha256);
|
||||
}
|
||||
|
||||
return s_sha256;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm Sha384 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_sha384 == null) {
|
||||
s_sha384 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha384);
|
||||
}
|
||||
|
||||
return s_sha384;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithm Sha512 {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
|
||||
if (s_sha512 == null) {
|
||||
s_sha512 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha512);
|
||||
}
|
||||
|
||||
return s_sha512;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Utility class to strongly type algorithm groups used with CNG. Since all CNG APIs which require or
|
||||
/// return an algorithm group name take the name as a string, we use this string wrapper class to
|
||||
/// specifically mark which parameters and return values are expected to be algorithm groups. We also
|
||||
/// provide a list of well known algorithm group names, which helps Intellisense users find a set of
|
||||
/// good algorithm group names to use.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngAlgorithmGroup : IEquatable<CngAlgorithmGroup> {
|
||||
private static volatile CngAlgorithmGroup s_dh;
|
||||
private static volatile CngAlgorithmGroup s_dsa;
|
||||
private static volatile CngAlgorithmGroup s_ecdh;
|
||||
private static volatile CngAlgorithmGroup s_ecdsa;
|
||||
private static volatile CngAlgorithmGroup s_rsa;
|
||||
|
||||
private string m_algorithmGroup;
|
||||
|
||||
public CngAlgorithmGroup(string algorithmGroup) {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(m_algorithmGroup));
|
||||
|
||||
if (algorithmGroup == null) {
|
||||
throw new ArgumentNullException("algorithmGroup");
|
||||
}
|
||||
if (algorithmGroup.Length == 0) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidAlgorithmGroup, algorithmGroup), "algorithmGroup");
|
||||
}
|
||||
|
||||
m_algorithmGroup = algorithmGroup;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the algorithm group
|
||||
/// </summary>
|
||||
public string AlgorithmGroup {
|
||||
get {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
|
||||
return m_algorithmGroup;
|
||||
}
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public static bool operator ==(CngAlgorithmGroup left, CngAlgorithmGroup right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public static bool operator !=(CngAlgorithmGroup left, CngAlgorithmGroup right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return !Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
Contract.Assert(m_algorithmGroup != null);
|
||||
|
||||
return Equals(obj as CngAlgorithmGroup);
|
||||
}
|
||||
|
||||
public bool Equals(CngAlgorithmGroup other) {
|
||||
if (Object.ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_algorithmGroup.Equals(other.AlgorithmGroup);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
Contract.Assert(m_algorithmGroup != null);
|
||||
return m_algorithmGroup.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
Contract.Assert(m_algorithmGroup != null);
|
||||
return m_algorithmGroup;
|
||||
}
|
||||
|
||||
//
|
||||
// Well known algorithm groups
|
||||
//
|
||||
|
||||
public static CngAlgorithmGroup DiffieHellman {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
|
||||
|
||||
if (s_dh == null) {
|
||||
s_dh = new CngAlgorithmGroup("DH"); // NCRYPT_DH_ALGORITHM_GROUP
|
||||
}
|
||||
|
||||
return s_dh;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithmGroup Dsa {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
|
||||
|
||||
if (s_dsa == null) {
|
||||
s_dsa = new CngAlgorithmGroup("DSA"); // NCRYPT_DSA_ALGORITHM_GROUP
|
||||
}
|
||||
|
||||
return s_dsa;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithmGroup ECDiffieHellman {
|
||||
[Pure]
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
|
||||
|
||||
if (s_ecdh == null) {
|
||||
s_ecdh = new CngAlgorithmGroup("ECDH"); // NCRYPT_ECDH_ALGORITHM_GROUP
|
||||
}
|
||||
|
||||
return s_ecdh;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithmGroup ECDsa {
|
||||
[Pure]
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
|
||||
|
||||
if (s_ecdsa == null) {
|
||||
s_ecdsa = new CngAlgorithmGroup("ECDSA"); // NCRYPT_ECDSA_ALGORITHM_GROUP
|
||||
}
|
||||
|
||||
return s_ecdsa;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngAlgorithmGroup Rsa {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
|
||||
|
||||
if (s_rsa == null) {
|
||||
s_rsa = new CngAlgorithmGroup("RSA"); // NCRYPT_RSA_ALGORITHM_GROUP
|
||||
}
|
||||
|
||||
return s_rsa;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,170 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Utility class to strongly type the format of key blobs used with CNG. Since all CNG APIs which
|
||||
/// require or return a key blob format take the name as a string, we use this string wrapper class to
|
||||
/// specifically mark which parameters and return values are expected to be key blob formats. We also
|
||||
/// provide a list of well known blob formats, which helps Intellisense users find a set of good blob
|
||||
/// formats to use.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngKeyBlobFormat : IEquatable<CngKeyBlobFormat> {
|
||||
private static volatile CngKeyBlobFormat s_eccPrivate;
|
||||
private static volatile CngKeyBlobFormat s_eccPublic;
|
||||
private static volatile CngKeyBlobFormat s_genericPrivate;
|
||||
private static volatile CngKeyBlobFormat s_genericPublic;
|
||||
private static volatile CngKeyBlobFormat s_opaqueTransport;
|
||||
private static volatile CngKeyBlobFormat s_pkcs8Private;
|
||||
|
||||
private string m_format;
|
||||
|
||||
public CngKeyBlobFormat(string format) {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(m_format));
|
||||
|
||||
if (format == null) {
|
||||
throw new ArgumentNullException("format");
|
||||
}
|
||||
if (format.Length == 0) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeyBlobFormat, format), "format");
|
||||
}
|
||||
|
||||
m_format = format;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the blob format
|
||||
/// </summary>
|
||||
public string Format {
|
||||
get {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
|
||||
return m_format;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(CngKeyBlobFormat left, CngKeyBlobFormat right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public static bool operator !=(CngKeyBlobFormat left, CngKeyBlobFormat right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return !Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
Contract.Assert(m_format != null);
|
||||
|
||||
return Equals(obj as CngKeyBlobFormat);
|
||||
}
|
||||
|
||||
public bool Equals(CngKeyBlobFormat other) {
|
||||
if (Object.ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_format.Equals(other.Format);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
Contract.Assert(m_format != null);
|
||||
return m_format.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
Contract.Assert(m_format != null);
|
||||
return m_format;
|
||||
}
|
||||
|
||||
//
|
||||
// Well known key blob formats
|
||||
//
|
||||
|
||||
public static CngKeyBlobFormat EccPrivateBlob {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
|
||||
if (s_eccPrivate == null) {
|
||||
s_eccPrivate = new CngKeyBlobFormat("ECCPRIVATEBLOB"); // BCRYPT_ECCPRIVATE_BLOB
|
||||
}
|
||||
|
||||
return s_eccPrivate;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngKeyBlobFormat EccPublicBlob {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
|
||||
if (s_eccPublic == null) {
|
||||
s_eccPublic = new CngKeyBlobFormat("ECCPUBLICBLOB"); // BCRYPT_ECCPUBLIC_BLOB
|
||||
}
|
||||
|
||||
return s_eccPublic;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngKeyBlobFormat GenericPrivateBlob {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
|
||||
if (s_genericPrivate == null) {
|
||||
s_genericPrivate = new CngKeyBlobFormat("PRIVATEBLOB"); // BCRYPT_PRIVATE_KEY_BLOB
|
||||
}
|
||||
|
||||
return s_genericPrivate;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngKeyBlobFormat GenericPublicBlob {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
|
||||
if (s_genericPublic == null) {
|
||||
s_genericPublic = new CngKeyBlobFormat("PUBLICBLOB"); // BCRYPT_PUBLIC_KEY_BLOB
|
||||
}
|
||||
|
||||
return s_genericPublic;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngKeyBlobFormat OpaqueTransportBlob {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
|
||||
if (s_opaqueTransport == null) {
|
||||
s_opaqueTransport = new CngKeyBlobFormat("OpaqueTransport"); // NCRYPT_OPAQUETRANSPORT_BLOB
|
||||
}
|
||||
|
||||
return s_opaqueTransport;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngKeyBlobFormat Pkcs8PrivateBlob {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
|
||||
if (s_pkcs8Private == null) {
|
||||
s_pkcs8Private = new CngKeyBlobFormat("PKCS8_PRIVATEKEY"); // NCRYPT_PKCS8_PRIVATE_KEY_BLOB
|
||||
}
|
||||
|
||||
return s_pkcs8Private;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Settings to be applied to a CNG key before it is finalized.
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngKeyCreationParameters {
|
||||
private CngExportPolicies? m_exportPolicy;
|
||||
private CngKeyCreationOptions m_keyCreationOptions;
|
||||
private CngKeyUsages? m_keyUsage;
|
||||
private CngPropertyCollection m_parameters = new CngPropertyCollection();
|
||||
private IntPtr m_parentWindowHandle;
|
||||
private CngProvider m_provider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
|
||||
private CngUIPolicy m_uiPolicy;
|
||||
|
||||
/// <summary>
|
||||
/// How many times can this key be exported from the KSP
|
||||
/// </summary>
|
||||
public CngExportPolicies? ExportPolicy {
|
||||
get { return m_exportPolicy; }
|
||||
set { m_exportPolicy = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags controlling how to create the key
|
||||
/// </summary>
|
||||
public CngKeyCreationOptions KeyCreationOptions {
|
||||
get { return m_keyCreationOptions; }
|
||||
set { m_keyCreationOptions = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Which cryptographic operations are valid for use with this key
|
||||
/// </summary>
|
||||
public CngKeyUsages? KeyUsage {
|
||||
get { return m_keyUsage; }
|
||||
set { m_keyUsage = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Window handle to use as the parent for the dialog shown when the key is created
|
||||
/// </summary>
|
||||
public IntPtr ParentWindowHandle {
|
||||
get { return m_parentWindowHandle; }
|
||||
|
||||
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||||
[SecuritySafeCritical]
|
||||
set { m_parentWindowHandle = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extra parameter values to set before the key is finalized
|
||||
/// </summary>
|
||||
public CngPropertyCollection Parameters {
|
||||
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||||
[SecuritySafeCritical]
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngPropertyCollection>() != null);
|
||||
return m_parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal access to the parameters method without a demand
|
||||
/// </summary>
|
||||
internal CngPropertyCollection ParametersNoDemand {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngPropertyCollection>() != null);
|
||||
return m_parameters;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KSP to create the key in
|
||||
/// </summary>
|
||||
public CngProvider Provider {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngProvider>() != null);
|
||||
return m_provider;
|
||||
}
|
||||
|
||||
set {
|
||||
if (value == null) {
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
m_provider = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Settings for UI shown on access to the key
|
||||
/// </summary>
|
||||
public CngUIPolicy UIPolicy {
|
||||
get { return m_uiPolicy; }
|
||||
|
||||
[HostProtection(UI = true)]
|
||||
[UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.SafeSubWindows)]
|
||||
[SecuritySafeCritical]
|
||||
set { m_uiPolicy = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Wrapper represeting an arbitrary property of a CNG key or provider
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public struct CngProperty : IEquatable<CngProperty> {
|
||||
private string m_name;
|
||||
private CngPropertyOptions m_propertyOptions;
|
||||
private byte[] m_value;
|
||||
private int? m_hashCode;
|
||||
|
||||
public CngProperty(string name, byte[] value, CngPropertyOptions options) {
|
||||
if (name == null)
|
||||
throw new ArgumentNullException("name");
|
||||
// @
|
||||
|
||||
|
||||
m_name = name;
|
||||
m_propertyOptions = options;
|
||||
m_hashCode = null;
|
||||
|
||||
if (value != null) {
|
||||
m_value = value.Clone() as byte[];
|
||||
}
|
||||
else {
|
||||
m_value = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the property
|
||||
/// </summary>
|
||||
public string Name {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<string>() != null);
|
||||
return m_name;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Options used to set / get the property
|
||||
/// </summary>
|
||||
public CngPropertyOptions Options {
|
||||
get { return m_propertyOptions; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Direct value of the property -- if the value will be returned to user code or modified, use
|
||||
/// GetValue() instead.
|
||||
/// </summary>
|
||||
internal byte[] Value {
|
||||
get { return m_value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contents of the property
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public byte[] GetValue() {
|
||||
byte[] value = null;
|
||||
|
||||
if (m_value != null) {
|
||||
value = m_value.Clone() as byte[];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static bool operator ==(CngProperty left, CngProperty right) {
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
public static bool operator !=(CngProperty left, CngProperty right) {
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
if (obj == null || !(obj is CngProperty)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((CngProperty)obj);
|
||||
}
|
||||
|
||||
public bool Equals(CngProperty other) {
|
||||
//
|
||||
// We will consider CNG properties equal only if the name, options and value are all also equal
|
||||
//
|
||||
|
||||
if (!String.Equals(Name, other.Name, StringComparison.Ordinal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Options != other.Options) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_value == null) {
|
||||
return other.m_value == null;
|
||||
}
|
||||
if (other.m_value == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_value.Length != other.m_value.Length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_value.Length; i++) {
|
||||
if (m_value[i] != other.m_value[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
if (!m_hashCode.HasValue) {
|
||||
int hashCode = Name.GetHashCode() ^ Options.GetHashCode();
|
||||
|
||||
// The hash code for a byte is just the value of that byte. Since this will only modify the
|
||||
// lower bits of the hash code, we'll xor each byte into different sections of the hash code
|
||||
if (m_value != null) {
|
||||
for (int i = 0; i < m_value.Length; i++) {
|
||||
// Shift each byte forward by one byte, so that every 4 bytes has to potential to update
|
||||
// each of the calculated hash code's bytes.
|
||||
int shifted = (int)(m_value[i] << ((i % 4) * 8));
|
||||
hashCode ^= shifted;
|
||||
}
|
||||
}
|
||||
|
||||
m_hashCode = hashCode;
|
||||
}
|
||||
|
||||
return m_hashCode.Value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Strongly typed collection of CNG properties
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngPropertyCollection : Collection<CngProperty> {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Utility class to strongly type providers used with CNG. Since all CNG APIs which require a
|
||||
/// provider name take the name as a string, we use this string wrapper class to specifically mark
|
||||
/// which parameters are expected to be providers. We also provide a list of well known provider
|
||||
/// names, which helps Intellisense users find a set of good providernames to use.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngProvider : IEquatable<CngProvider> {
|
||||
private static volatile CngProvider s_msSmartCardKsp;
|
||||
private static volatile CngProvider s_msSoftwareKsp;
|
||||
|
||||
private string m_provider;
|
||||
|
||||
public CngProvider(string provider) {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(m_provider));
|
||||
|
||||
if (provider == null) {
|
||||
throw new ArgumentNullException("provider");
|
||||
}
|
||||
if (provider.Length == 0) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidProviderName, provider), "provider");
|
||||
}
|
||||
|
||||
m_provider = provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Name of the CNG provider
|
||||
/// </summary>
|
||||
public string Provider {
|
||||
get {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
|
||||
return m_provider;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool operator ==(CngProvider left, CngProvider right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
[Pure]
|
||||
public static bool operator !=(CngProvider left, CngProvider right) {
|
||||
if (Object.ReferenceEquals(left, null)) {
|
||||
return !Object.ReferenceEquals(right, null);
|
||||
}
|
||||
|
||||
return !left.Equals(right);
|
||||
}
|
||||
|
||||
public override bool Equals(object obj) {
|
||||
Contract.Assert(m_provider != null);
|
||||
|
||||
return Equals(obj as CngProvider);
|
||||
}
|
||||
|
||||
public bool Equals(CngProvider other) {
|
||||
if (Object.ReferenceEquals(other, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_provider.Equals(other.Provider);
|
||||
}
|
||||
|
||||
public override int GetHashCode() {
|
||||
Contract.Assert(m_provider != null);
|
||||
return m_provider.GetHashCode();
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
Contract.Assert(m_provider != null);
|
||||
return m_provider.ToString();
|
||||
}
|
||||
|
||||
//
|
||||
// Well known NCrypt KSPs
|
||||
//
|
||||
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "CardKey", Justification = "This is not 'Smart Cardkey', but 'Smart Card Key'")]
|
||||
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SmartCard", Justification = "Smart Card is two words in the ncrypt usage")]
|
||||
public static CngProvider MicrosoftSmartCardKeyStorageProvider {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngProvider>() != null);
|
||||
|
||||
if (s_msSmartCardKsp == null) {
|
||||
s_msSmartCardKsp = new CngProvider("Microsoft Smart Card Key Storage Provider"); // MS_SMART_CARD_KEY_STORAGE_PROVIDER
|
||||
}
|
||||
|
||||
return s_msSmartCardKsp;
|
||||
}
|
||||
}
|
||||
|
||||
public static CngProvider MicrosoftSoftwareKeyStorageProvider {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngProvider>() != null);
|
||||
|
||||
if (s_msSoftwareKsp == null) {
|
||||
s_msSoftwareKsp = new CngProvider("Microsoft Software Key Storage Provider"); // MS_KEY_STORAGE_PROVIDER
|
||||
}
|
||||
|
||||
return s_msSoftwareKsp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Configuration parameters for the UI displayed by CNG when accessing a protected key
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class CngUIPolicy {
|
||||
private string m_creationTitle;
|
||||
private string m_description;
|
||||
private string m_friendlyName;
|
||||
private CngUIProtectionLevels m_protectionLevel;
|
||||
private string m_useContext;
|
||||
|
||||
public CngUIPolicy(CngUIProtectionLevels protectionLevel) :
|
||||
this(protectionLevel, null) {
|
||||
}
|
||||
|
||||
public CngUIPolicy(CngUIProtectionLevels protectionLevel, string friendlyName) :
|
||||
this(protectionLevel, friendlyName, null) {
|
||||
}
|
||||
|
||||
public CngUIPolicy(CngUIProtectionLevels protectionLevel, string friendlyName, string description) :
|
||||
this(protectionLevel, friendlyName, description, null) {
|
||||
}
|
||||
|
||||
public CngUIPolicy(CngUIProtectionLevels protectionLevel,
|
||||
string friendlyName,
|
||||
string description,
|
||||
string useContext) :
|
||||
this(protectionLevel, friendlyName, description, useContext, null) {
|
||||
}
|
||||
|
||||
public CngUIPolicy(CngUIProtectionLevels protectionLevel,
|
||||
string friendlyName,
|
||||
string description,
|
||||
string useContext,
|
||||
string creationTitle) {
|
||||
m_creationTitle = creationTitle;
|
||||
m_description = description;
|
||||
m_friendlyName = friendlyName;
|
||||
m_protectionLevel = protectionLevel;
|
||||
m_useContext = useContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Title of the dialog box displaed when a newly created key is finalized, null for the default title
|
||||
/// </summary>
|
||||
public string CreationTitle {
|
||||
get { return m_creationTitle; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description text displayed in the dialog box when the key is accessed, null for the default text
|
||||
/// </summary>
|
||||
public string Description {
|
||||
get { return m_description; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Friendly name to describe the key with in the dialog box that appears when the key is accessed,
|
||||
/// null for default name
|
||||
/// </summary>
|
||||
public string FriendlyName {
|
||||
get { return m_friendlyName; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Level of UI protection to apply to the key
|
||||
/// </summary>
|
||||
public CngUIProtectionLevels ProtectionLevel {
|
||||
get { return m_protectionLevel; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Description of how the key will be used
|
||||
/// </summary>
|
||||
public string UseContext {
|
||||
get { return m_useContext; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Abstract base class for implementations of elliptic curve Diffie-Hellman to derive from
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public abstract class ECDiffieHellman : AsymmetricAlgorithm {
|
||||
public override string KeyExchangeAlgorithm {
|
||||
get { return "ECDiffieHellman"; }
|
||||
}
|
||||
|
||||
public override string SignatureAlgorithm {
|
||||
get { return null; }
|
||||
}
|
||||
|
||||
//
|
||||
// Creation factory methods
|
||||
//
|
||||
|
||||
public static new ECDiffieHellman Create() {
|
||||
#if MONO
|
||||
throw new NotImplementedException ();
|
||||
#else
|
||||
return Create(typeof(ECDiffieHellmanCng).FullName);
|
||||
#endif
|
||||
}
|
||||
|
||||
public static new ECDiffieHellman Create(string algorithm) {
|
||||
if (algorithm == null) {
|
||||
throw new ArgumentNullException("algorithm");
|
||||
}
|
||||
|
||||
return CryptoConfig.CreateFromName(algorithm) as ECDiffieHellman;
|
||||
}
|
||||
|
||||
//
|
||||
// Key derivation
|
||||
//
|
||||
|
||||
public abstract ECDiffieHellmanPublicKey PublicKey { get; }
|
||||
public abstract byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Key derivation functions used to transform the raw secret agreement into key material
|
||||
/// </summary>
|
||||
public enum ECDiffieHellmanKeyDerivationFunction {
|
||||
Hash,
|
||||
Hmac,
|
||||
Tls
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper for CNG's implementation of elliptic curve Diffie-Hellman key exchange
|
||||
/// </summary>
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class ECDiffieHellmanCng : ECDiffieHellman {
|
||||
private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
|
||||
|
||||
private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
|
||||
private byte[] m_hmacKey;
|
||||
private CngKey m_key;
|
||||
private ECDiffieHellmanKeyDerivationFunction m_kdf = ECDiffieHellmanKeyDerivationFunction.Hash;
|
||||
private byte[] m_label;
|
||||
private byte[] m_secretAppend;
|
||||
private byte[] m_secretPrepend;
|
||||
private byte[] m_seed;
|
||||
|
||||
//
|
||||
// Constructors
|
||||
//
|
||||
|
||||
public ECDiffieHellmanCng() : this(521) {
|
||||
Contract.Ensures(LegalKeySizesValue != null);
|
||||
}
|
||||
|
||||
public ECDiffieHellmanCng(int keySize) {
|
||||
Contract.Ensures(LegalKeySizesValue != null);
|
||||
|
||||
if (!NCryptNative.NCryptSupported) {
|
||||
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
|
||||
}
|
||||
|
||||
LegalKeySizesValue = s_legalKeySizes;
|
||||
KeySize = keySize;
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
public ECDiffieHellmanCng(CngKey key) {
|
||||
Contract.Ensures(LegalKeySizesValue != null);
|
||||
Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
|
||||
|
||||
if (key == null) {
|
||||
throw new ArgumentNullException("key");
|
||||
}
|
||||
if (key.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "key");
|
||||
}
|
||||
|
||||
if (!NCryptNative.NCryptSupported) {
|
||||
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
|
||||
}
|
||||
|
||||
LegalKeySizesValue = s_legalKeySizes;
|
||||
|
||||
// Make a copy of the key so that we continue to work if it gets disposed before this algorithm
|
||||
//
|
||||
// This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
|
||||
// and the handle constructor of CngKey. The assert is safe since ECDiffieHellmanCng will never
|
||||
// expose the key handles to calling code (without first demanding UnmanagedCode via the Handle
|
||||
// property of CngKey).
|
||||
//
|
||||
// The bizzare looking disposal of the key.Handle property is intentional - Handle returns a
|
||||
// duplicate - without disposing it, we keep the key alive until the GC runs.
|
||||
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
|
||||
using (SafeNCryptKeyHandle importHandle = key.Handle) {
|
||||
Key = CngKey.Open(importHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
|
||||
}
|
||||
CodeAccessPermission.RevertAssert();
|
||||
|
||||
KeySize = m_key.KeySize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hash algorithm used with the Hash and HMAC KDFs
|
||||
/// </summary>
|
||||
public CngAlgorithm HashAlgorithm {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
|
||||
return m_hashAlgorithm;
|
||||
}
|
||||
|
||||
set {
|
||||
Contract.Ensures(m_hashAlgorithm != null);
|
||||
|
||||
if (m_hashAlgorithm == null) {
|
||||
throw new ArgumentNullException("value");
|
||||
}
|
||||
|
||||
m_hashAlgorithm = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Key used with the HMAC KDF
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
|
||||
public byte[] HmacKey {
|
||||
get { return m_hmacKey; }
|
||||
set { m_hmacKey = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KDF used to transform the secret agreement into key material
|
||||
/// </summary>
|
||||
public ECDiffieHellmanKeyDerivationFunction KeyDerivationFunction {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<ECDiffieHellmanKeyDerivationFunction>() >= ECDiffieHellmanKeyDerivationFunction.Hash &&
|
||||
Contract.Result<ECDiffieHellmanKeyDerivationFunction>() <= ECDiffieHellmanKeyDerivationFunction.Tls);
|
||||
|
||||
return m_kdf;
|
||||
}
|
||||
|
||||
set {
|
||||
Contract.Ensures(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
|
||||
m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
|
||||
|
||||
if (value < ECDiffieHellmanKeyDerivationFunction.Hash || value > ECDiffieHellmanKeyDerivationFunction.Tls) {
|
||||
throw new ArgumentOutOfRangeException("value");
|
||||
}
|
||||
|
||||
m_kdf = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Label bytes used for the TLS KDF
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
|
||||
public byte[] Label {
|
||||
get { return m_label; }
|
||||
set { m_label = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bytes to append to the raw secret agreement before processing by the KDF
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
|
||||
public byte[] SecretAppend {
|
||||
get { return m_secretAppend; }
|
||||
set { m_secretAppend = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bytes to prepend to the raw secret agreement before processing by the KDF
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
|
||||
public byte[] SecretPrepend {
|
||||
get { return m_secretPrepend; }
|
||||
set { m_secretPrepend = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seed bytes used for the TLS KDF
|
||||
/// </summary>
|
||||
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
|
||||
public byte[] Seed {
|
||||
get { return m_seed; }
|
||||
set { m_seed = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Full key pair being used for key generation
|
||||
/// </summary>
|
||||
public CngKey Key {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKey>() != null);
|
||||
Contract.Ensures(Contract.Result<CngKey>().AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
|
||||
Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
|
||||
|
||||
// If the size of the key no longer matches our stored value, then we need to replace it with
|
||||
// a new key of the correct size.
|
||||
if (m_key != null && m_key.KeySize != KeySize) {
|
||||
m_key.Dispose();
|
||||
m_key = null;
|
||||
}
|
||||
|
||||
if (m_key == null) {
|
||||
// Map the current key size to a CNG algorithm name
|
||||
CngAlgorithm algorithm = null;
|
||||
switch (KeySize) {
|
||||
case 256:
|
||||
algorithm = CngAlgorithm.ECDiffieHellmanP256;
|
||||
break;
|
||||
|
||||
case 384:
|
||||
algorithm = CngAlgorithm.ECDiffieHellmanP384;
|
||||
break;
|
||||
|
||||
case 521:
|
||||
algorithm = CngAlgorithm.ECDiffieHellmanP521;
|
||||
break;
|
||||
|
||||
default:
|
||||
Debug.Assert(false, "Illegal key size set");
|
||||
break;
|
||||
}
|
||||
|
||||
m_key = CngKey.Create(algorithm);
|
||||
}
|
||||
|
||||
return m_key;
|
||||
}
|
||||
|
||||
private set {
|
||||
Contract.Requires(value != null);
|
||||
Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
|
||||
|
||||
if (value.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey));
|
||||
}
|
||||
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
|
||||
//
|
||||
// We do not duplicate the handle because the only time the user has access to the key itself
|
||||
// to dispose underneath us is when they construct via the CngKey constructor, which does a
|
||||
// duplication. Otherwise all key lifetimes are controlled directly by the ECDiffieHellmanCng
|
||||
// class.
|
||||
//
|
||||
|
||||
m_key = value;
|
||||
KeySize = m_key.KeySize;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public key used to generate key material with the second party
|
||||
/// </summary>
|
||||
public override ECDiffieHellmanPublicKey PublicKey {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<ECDiffieHellmanPublicKey>() != null);
|
||||
return new ECDiffieHellmanCngPublicKey(Key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use the secret agreement as the HMAC key rather than supplying a seperate one
|
||||
/// </summary>
|
||||
public bool UseSecretAgreementAsHmacKey {
|
||||
get { return HmacKey == null; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a second party's public key, derive shared key material
|
||||
/// </summary>
|
||||
public override byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey) {
|
||||
Contract.Ensures(Contract.Result<byte[]>() != null);
|
||||
Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
|
||||
m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
|
||||
|
||||
if (otherPartyPublicKey == null) {
|
||||
throw new ArgumentNullException("otherPartyPublicKey");
|
||||
}
|
||||
|
||||
// We can only work with ECDiffieHellmanCngPublicKeys
|
||||
ECDiffieHellmanCngPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanCngPublicKey;
|
||||
if (otherPartyPublicKey == null) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgExpectedECDiffieHellmanCngPublicKey));
|
||||
}
|
||||
|
||||
using (CngKey import = otherKey.Import()) {
|
||||
return DeriveKeyMaterial(import);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Given a second party's public key, derive shared key material
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) {
|
||||
Contract.Ensures(Contract.Result<byte[]>() != null);
|
||||
Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
|
||||
m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
|
||||
|
||||
if (otherPartyPublicKey == null) {
|
||||
throw new ArgumentNullException("otherPartyPublicKey");
|
||||
}
|
||||
if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey");
|
||||
}
|
||||
if (otherPartyPublicKey.KeySize != KeySize) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey");
|
||||
}
|
||||
|
||||
NCryptNative.SecretAgreementFlags flags =
|
||||
UseSecretAgreementAsHmacKey ? NCryptNative.SecretAgreementFlags.UseSecretAsHmacKey : NCryptNative.SecretAgreementFlags.None;
|
||||
|
||||
// We require access to the handles for generating key material. This is safe since we will never
|
||||
// expose these handles to user code
|
||||
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
|
||||
|
||||
// This looks horribly wrong - but accessing the handle property actually returns a duplicate handle, which
|
||||
// we need to dispose of - otherwise, we're stuck keepign the resource alive until the GC runs. This explicitly
|
||||
// is not disposing of the handle underlying the key dispite what the syntax looks like.
|
||||
using (SafeNCryptKeyHandle localKey = Key.Handle)
|
||||
using (SafeNCryptKeyHandle otherKey = otherPartyPublicKey.Handle) {
|
||||
CodeAccessPermission.RevertAssert();
|
||||
|
||||
//
|
||||
// Generating key material is a two phase process.
|
||||
// 1. Generate the secret agreement
|
||||
// 2. Pass the secret agreement through a KDF to get key material
|
||||
//
|
||||
|
||||
using (SafeNCryptSecretHandle secretAgreement = NCryptNative.DeriveSecretAgreement(localKey, otherKey)) {
|
||||
if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hash) {
|
||||
byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[];
|
||||
byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[];
|
||||
|
||||
return NCryptNative.DeriveKeyMaterialHash(secretAgreement,
|
||||
HashAlgorithm.Algorithm,
|
||||
secretPrepend,
|
||||
secretAppend,
|
||||
flags);
|
||||
}
|
||||
else if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hmac) {
|
||||
byte[] hmacKey = HmacKey == null ? null : HmacKey.Clone() as byte[];
|
||||
byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[];
|
||||
byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[];
|
||||
|
||||
return NCryptNative.DeriveKeyMaterialHmac(secretAgreement,
|
||||
HashAlgorithm.Algorithm,
|
||||
hmacKey,
|
||||
secretPrepend,
|
||||
secretAppend,
|
||||
flags);
|
||||
}
|
||||
else {
|
||||
Debug.Assert(KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Tls, "Unknown KDF");
|
||||
|
||||
byte[] label = Label == null ? null : Label.Clone() as byte[];
|
||||
byte[] seed = Seed == null ? null : Seed.Clone() as byte[];
|
||||
|
||||
if (label == null || seed == null) {
|
||||
throw new InvalidOperationException(SR.GetString(SR.Cryptography_TlsRequiresLabelAndSeed));
|
||||
}
|
||||
|
||||
return NCryptNative.DeriveKeyMaterialTls(secretAgreement, label, seed, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a handle to the secret agreement generated between two parties
|
||||
/// </summary>
|
||||
public SafeNCryptSecretHandle DeriveSecretAgreementHandle(ECDiffieHellmanPublicKey otherPartyPublicKey) {
|
||||
if (otherPartyPublicKey == null) {
|
||||
throw new ArgumentNullException("otherPartyPublicKey");
|
||||
}
|
||||
|
||||
// We can only work with ECDiffieHellmanCngPublicKeys
|
||||
ECDiffieHellmanCngPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanCngPublicKey;
|
||||
if (otherPartyPublicKey == null) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgExpectedECDiffieHellmanCngPublicKey));
|
||||
}
|
||||
|
||||
using (CngKey importedKey = otherKey.Import()) {
|
||||
return DeriveSecretAgreementHandle(importedKey);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a handle to the secret agreement between two parties
|
||||
/// </summary>
|
||||
[System.Security.SecurityCritical]
|
||||
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
|
||||
public SafeNCryptSecretHandle DeriveSecretAgreementHandle(CngKey otherPartyPublicKey) {
|
||||
if (otherPartyPublicKey == null) {
|
||||
throw new ArgumentNullException("otherPartyPublicKey");
|
||||
}
|
||||
if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey");
|
||||
}
|
||||
if (otherPartyPublicKey.KeySize != KeySize) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey");
|
||||
}
|
||||
|
||||
// This looks strange, but the Handle property returns a duplicate so we need to dispose of it when we're done
|
||||
using (SafeNCryptKeyHandle localHandle = Key.Handle)
|
||||
using (SafeNCryptKeyHandle otherPartyHandle = otherPartyPublicKey.Handle) {
|
||||
return NCryptNative.DeriveSecretAgreement(localHandle, otherPartyHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up the algorithm
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing) {
|
||||
try {
|
||||
if (disposing) {
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// XML Import
|
||||
//
|
||||
// See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
|
||||
// code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
|
||||
// elliptic curve XML formats.
|
||||
//
|
||||
|
||||
public override void FromXmlString(string xmlString) {
|
||||
throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
|
||||
}
|
||||
|
||||
public void FromXmlString(string xml, ECKeyXmlFormat format) {
|
||||
if (xml == null) {
|
||||
throw new ArgumentNullException("xml");
|
||||
}
|
||||
if (format != ECKeyXmlFormat.Rfc4050) {
|
||||
throw new ArgumentOutOfRangeException("format");
|
||||
}
|
||||
|
||||
Key = Rfc4050KeyFormatter.FromXml(xml);
|
||||
}
|
||||
|
||||
//
|
||||
// XML Export
|
||||
//
|
||||
// See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
|
||||
// code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
|
||||
// elliptic curve XML formats.
|
||||
//
|
||||
|
||||
public override string ToXmlString(bool includePrivateParameters) {
|
||||
throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
|
||||
}
|
||||
|
||||
public string ToXmlString(ECKeyXmlFormat format) {
|
||||
Contract.Ensures(Contract.Result<string>() != null);
|
||||
|
||||
if (format != ECKeyXmlFormat.Rfc4050) {
|
||||
throw new ArgumentOutOfRangeException("format");
|
||||
}
|
||||
|
||||
return Rfc4050KeyFormatter.ToXml(Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Diagnostics.Contracts;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Public key used to do key exchange with the ECDiffieHellmanCng algorithm
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public sealed class ECDiffieHellmanCngPublicKey : ECDiffieHellmanPublicKey {
|
||||
[NonSerialized]
|
||||
private CngKey m_key;
|
||||
private CngKeyBlobFormat m_format;
|
||||
|
||||
/// <summary>
|
||||
/// Wrap a CNG key
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
internal ECDiffieHellmanCngPublicKey(CngKey key) : base(key.Export(CngKeyBlobFormat.EccPublicBlob)) {
|
||||
Contract.Requires(key != null && key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
|
||||
Contract.Ensures(m_format != null);
|
||||
|
||||
m_format = CngKeyBlobFormat.EccPublicBlob;
|
||||
|
||||
//
|
||||
// We need to make a copy of the key to prevent the situation where the ECDiffieHellmanCng algorithm
|
||||
// object is disposed (this disposing its key) before the ECDiffieHellmanCngPublic key is disposed.
|
||||
//
|
||||
// Accessing the handle in partial trust is safe because we're not exposing it back out to user code
|
||||
//
|
||||
|
||||
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
|
||||
|
||||
// This looks odd, but .Handle returns a duplicate, so we need to dispose it
|
||||
using (SafeNCryptKeyHandle importKey = key.Handle) {
|
||||
m_key = CngKey.Open(importKey, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
|
||||
}
|
||||
|
||||
CodeAccessPermission.RevertAssert();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Format the key blob is expressed in
|
||||
/// </summary>
|
||||
public CngKeyBlobFormat BlobFormat {
|
||||
get {
|
||||
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
|
||||
Contract.Assert(m_format != null);
|
||||
|
||||
return m_format;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up the key
|
||||
/// </summary>
|
||||
protected override void Dispose(bool disposing) {
|
||||
try {
|
||||
if (disposing) {
|
||||
if (m_key != null) {
|
||||
m_key.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hydrate a public key from a blob
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public static ECDiffieHellmanPublicKey FromByteArray(byte[] publicKeyBlob, CngKeyBlobFormat format) {
|
||||
if (publicKeyBlob == null) {
|
||||
throw new ArgumentNullException("publicKeyBlob");
|
||||
}
|
||||
if (format == null) {
|
||||
throw new ArgumentNullException("format");
|
||||
}
|
||||
|
||||
using (CngKey imported = CngKey.Import(publicKeyBlob, format)) {
|
||||
if (imported.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey));
|
||||
}
|
||||
|
||||
return new ECDiffieHellmanCngPublicKey(imported);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hydrate a public key from XML
|
||||
///
|
||||
/// See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information
|
||||
/// about the XML format used.
|
||||
/// </summary>
|
||||
[SecuritySafeCritical]
|
||||
public static ECDiffieHellmanCngPublicKey FromXmlString(string xml) {
|
||||
if (xml == null) {
|
||||
throw new ArgumentNullException("xml");
|
||||
}
|
||||
|
||||
using (CngKey imported = Rfc4050KeyFormatter.FromXml(xml)) {
|
||||
if (imported.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
|
||||
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "xml");
|
||||
}
|
||||
|
||||
return new ECDiffieHellmanCngPublicKey(imported);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Import the public key into CNG
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public CngKey Import() {
|
||||
Contract.Ensures(Contract.Result<CngKey>() != null);
|
||||
Contract.Assert(m_format != null);
|
||||
|
||||
return CngKey.Import(ToByteArray(), BlobFormat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the key blob to XML
|
||||
///
|
||||
/// See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information
|
||||
/// about the XML format used.
|
||||
/// </summary>
|
||||
public override string ToXmlString() {
|
||||
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
|
||||
|
||||
if (m_key == null) {
|
||||
m_key = Import();
|
||||
}
|
||||
|
||||
return Rfc4050KeyFormatter.ToXml(m_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
// ==++==
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
//
|
||||
// ==--==
|
||||
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace System.Security.Cryptography {
|
||||
/// <summary>
|
||||
/// Wrapper for public key material passed between parties during Diffie-Hellman key material generation
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
|
||||
public abstract class ECDiffieHellmanPublicKey : IDisposable {
|
||||
private byte[] m_keyBlob;
|
||||
|
||||
protected ECDiffieHellmanPublicKey(byte[] keyBlob) {
|
||||
Contract.Ensures(m_keyBlob != null);
|
||||
|
||||
if (keyBlob == null) {
|
||||
throw new ArgumentNullException("keyBlob");
|
||||
}
|
||||
|
||||
m_keyBlob = keyBlob.Clone() as byte[];
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing) {
|
||||
return;
|
||||
}
|
||||
|
||||
public virtual byte[] ToByteArray() {
|
||||
Contract.Assert(m_keyBlob != null);
|
||||
return m_keyBlob.Clone() as byte[];
|
||||
}
|
||||
|
||||
public abstract string ToXmlString();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user