/* * Mentalis.org Security Library * * Copyright © 2002-2005, The KPD-Team * All rights reserved. * http://www.mentalis.org/ * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Neither the name of the KPD-Team, nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Security.Cryptography; using System.Runtime.InteropServices; using Org.Mentalis.Security; namespace Org.Mentalis.Security.Cryptography { /// /// The DataProtectionCryptoServiceProvider is a class that performs encryption and decryption on data without explicitly requiring a password. ///
There are two different types of encryption. The first type will associate the encrypted data with the logon credentials of the current user. Hence only a user with matching logon credentials can decrypt the data.
///
The second type will associate the encrypted data with the local machine. Any user of a specific machine will be able to decrypt encrypted data under this scheme.
///
This class cannot be inherited.
///
public sealed class DataProtectionCryptoServiceProvider : IDisposable { /// /// Initializes a new instance. /// public DataProtectionCryptoServiceProvider() : this(null) {} /// /// Initializes a new instance. /// /// A buffer holding any additional entropy that can be used during encryption and decryption. /// The same entropy must be provided during the encryption and decryption process. Otherwise, the decryption will fail. public DataProtectionCryptoServiceProvider(byte[] optionalEntropy) { if (optionalEntropy != null) m_OptionalEntropy = (byte[])optionalEntropy.Clone(); m_Disposed = false; } /// /// Encrypts data according to a specified protection type. /// /// One of the values. /// The data to encrypt. /// data is a null reference (Nothing in Visual Basic). /// An error occurs during the encryption process. Under some circumstances, Microsoft cryptographic service providers may not allow encryption when used in France. This may occur on down-level platforms such as Windows 98 and Windows NT 4.0, depending on the system's configuration and the version of the CSPs. /// An array of encrypted bytes. /// /// The number of the returned bytes will be larger than the number of input bytes. /// The method will use the entropy from the property. /// public byte[] ProtectData(ProtectionType type, byte[] data) { return ProtectData(type, data, this.Entropy); } /// /// Encrypts data according to a specified protection type. /// /// One of the values. /// The data to encrypt. /// Additional entropy to use during the encyption process. This parameter can be set to null. /// data is a null reference (Nothing in Visual Basic). /// An error occurs during the encryption process. Under some circumstances, Microsoft cryptographic service providers may not allow encryption when used in France. This may occur on down-level platforms such as Windows 98 and Windows NT 4.0, depending on the system's configuration and the version of the CSPs. /// An array of encrypted bytes. /// The number of the returned bytes will be larger than the number of input bytes. public byte[] ProtectData(ProtectionType type, byte[] data, byte[] entropy) { if (data == null) throw new ArgumentNullException(); return ProtectData(type, data, 0, data.Length, entropy); } /// /// Encrypts data according to a specified protection type. /// /// One of the values. /// The data to encrypt. /// The zero-based position in the data parameter at which to begin encrypting. /// The number of bytes to encrypt. /// Additional entropy to use during the encryption process. This parameter can be set to null. /// data is a null reference (Nothing in Visual Basic). /// The specified offset or size exceeds the size of buffer. /// An error occurs during the encryption process. Under some circumstances, Microsoft cryptographic service providers may not allow encryption when used in France. This may occur on down-level platforms such as Windows 98 and Windows NT 4.0, depending on the system's configuration and the version of the CSPs. /// An array of encrypted bytes. /// The number of the returned bytes will be larger than the number of input bytes. public byte[] ProtectData(ProtectionType type, byte[] data, int offset, int size, byte[] entropy) { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); if (data == null) throw new ArgumentNullException(); if (offset < 0 || offset + size > data.Length || !Enum.IsDefined(typeof(ProtectionType), type)) throw new ArgumentException(); DataBlob input = new DataBlob(); DataBlob entr = new DataBlob(); DataBlob output = new DataBlob(); try { // initialize input structure input.cbData = size; input.pbData = Marshal.AllocHGlobal(size); Marshal.Copy(data, offset, input.pbData, size); // initialize entropy structure if (entropy == null) { entr.cbData = 0; entr.pbData = IntPtr.Zero; } else { entr.cbData = entropy.Length; entr.pbData = Marshal.AllocHGlobal(entr.cbData); Marshal.Copy(entropy, 0, entr.pbData, entr.cbData); } // initialize output structure output.cbData = 0; output.pbData = IntPtr.Zero; // call the function and check for errors int flags = 0; if (type == ProtectionType.LocalMachine) flags = flags | SecurityConstants.CRYPTPROTECT_LOCAL_MACHINE; if (!Environment.UserInteractive) flags = flags | SecurityConstants.CRYPTPROTECT_UI_FORBIDDEN; if (SspiProvider.CryptProtectData(ref input, "", ref entr, IntPtr.Zero, IntPtr.Zero, flags, ref output) == 0 || output.pbData == IntPtr.Zero) throw new CryptographicException("The data could not be protected."); byte[] ret = new byte[output.cbData]; Marshal.Copy(output.pbData, ret, 0, output.cbData); return ret; } finally { if (input.pbData != IntPtr.Zero) Marshal.FreeHGlobal(input.pbData); if (entr.pbData != IntPtr.Zero) Marshal.FreeHGlobal(entr.pbData); if (output.pbData != IntPtr.Zero) Marshal.FreeHGlobal(output.pbData); } } /// /// Decrypts data that has been encrypted with the method. /// /// The data to decrypt. /// data is a null reference (Nothing in Visual Basic). /// The specified offset or size exceeds the size of buffer. /// An error occurs during the encryption process. Under some circumstances, Microsoft cryptographic service providers may not allow encryption when used in France. This may occur on down-level platforms such as Windows 98 and Windows NT 4.0, depending on the system's configuration and the version of the CSPs. /// The decrypted data. /// /// The method will use the entropy from the property. /// The entropy used during decryption must be the same as the entropy used during encryption. /// public byte[] UnprotectData(byte[] data) { return UnprotectData(data, this.Entropy); } /// /// Decrypts data that has been encrypted with the method. /// /// The data to decrypt. /// Additional entropy to use during the encyption process. This parameter can be set to null. /// data is a null reference (Nothing in Visual Basic). /// An error occurs during the encryption process. Under some circumstances, Microsoft cryptographic service providers may not allow encryption when used in France. This may occur on down-level platforms such as Windows 98 and Windows NT 4.0, depending on the system's configuration and the version of the CSPs. /// The decrypted data. /// The entropy used during decryption must be the same as the entropy used during encryption. public byte[] UnprotectData(byte[] data, byte[] entropy) { if (data == null) throw new ArgumentNullException(); return UnprotectData(data, 0, data.Length, entropy); } /// /// Decrypts data that has been encrypted with the method. /// /// The data to decrypt. /// The zero-based position in the data parameter at which to begin decrypting. /// The number of bytes to decrypt. /// Additional entropy to use during the decryption process. This parameter can be set to null. /// data is a null reference (Nothing in Visual Basic). /// The specified offset or size exceeds the size of buffer. /// An error occurs during the encryption process. Under some circumstances, Microsoft cryptographic service providers may not allow encryption when used in France. This may occur on down-level platforms such as Windows 98 and Windows NT 4.0, depending on the system's configuration and the version of the CSPs. /// The decrypted data. /// The entropy used during decryption must be the same as the entropy used during encryption. public byte[] UnprotectData(byte[] data, int offset, int size, byte[] entropy) { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); if (data == null) throw new ArgumentNullException(); if (offset < 0 || offset + size > data.Length) throw new ArgumentException(); DataBlob input = new DataBlob(); DataBlob entr = new DataBlob(); DataBlob output = new DataBlob(); try { // initialize input structure input.cbData = size; input.pbData = Marshal.AllocHGlobal(size); Marshal.Copy(data, offset, input.pbData, size); // initialize entropy structure if (entropy == null) { entr.cbData = 0; entr.pbData = IntPtr.Zero; } else { entr.cbData = entropy.Length; entr.pbData = Marshal.AllocHGlobal(entr.cbData); Marshal.Copy(entropy, 0, entr.pbData, entr.cbData); } // initialize output structure output.cbData = 0; output.pbData = IntPtr.Zero; // call the function and check for errors int flags = 0; if (!Environment.UserInteractive) flags = flags | SecurityConstants.CRYPTPROTECT_UI_FORBIDDEN; if (SspiProvider.CryptUnprotectData(ref input, IntPtr.Zero, ref entr, IntPtr.Zero, IntPtr.Zero, flags, ref output) == 0 || output.pbData == IntPtr.Zero) throw new CryptographicException("The data could not be unprotected."); byte[] ret = new byte[output.cbData]; Marshal.Copy(output.pbData, ret, 0, output.cbData); return ret; } finally { if (input.pbData != IntPtr.Zero) Marshal.FreeHGlobal(input.pbData); if (entr.pbData != IntPtr.Zero) Marshal.FreeHGlobal(entr.pbData); if (output.pbData != IntPtr.Zero) Marshal.FreeHGlobal(output.pbData); } } /// /// Holds additional entropy that can be used during the encryption and decryption process. /// /// An array en entropy bytes. public byte[] Entropy { get { return m_OptionalEntropy; } set { m_OptionalEntropy = value; } } /// /// Releases the unmanaged and managed resources used by the class. /// public void Dispose() { if (m_OptionalEntropy != null) Array.Clear(m_OptionalEntropy, 0, m_OptionalEntropy.Length); m_Disposed = true; } /// /// Finalizes the class. /// ~DataProtectionCryptoServiceProvider() { Dispose(); } /// /// Holds the entropy. /// private byte[] m_OptionalEntropy; /// /// Holds a value that indicates whether the class has been disposed of or not. /// private bool m_Disposed; } }