/* * 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 { /// /// Accesses the unmanaged version of the algorithm. This class cannot be inherited. /// /// /// This class will use the unmanaged implementation of the Rijndael algorithm, when possible. If the unmanaged Rijndael algorithm is not available, it will fall back to the implementation. /// public sealed class RijndaelCryptoServiceProvider : Rijndael { /// /// Initializes a new instance of the class. /// public RijndaelCryptoServiceProvider() { // acquire an AES context try { m_Provider = CAPIProvider.Handle; if (CAPIProvider.HandleProviderType != SecurityConstants.PROV_RSA_AES) m_Provider = 0; } catch { m_Provider = 0; } /* if (SspiProvider.CryptAcquireContext(ref m_Provider, IntPtr.Zero, null, SecurityConstants.PROV_RSA_AES, 0) == 0) { if (Marshal.GetLastWin32Error() == SecurityConstants.NTE_BAD_KEYSET) SspiProvider.CryptAcquireContext(ref m_Provider, IntPtr.Zero, null, SecurityConstants.PROV_RSA_AES, SecurityConstants.CRYPT_NEWKEYSET); }*/ m_Managed = new RijndaelManaged(); } /// /// Releases all unmanaged resources. /// ~RijndaelCryptoServiceProvider() { Dispose(true); } /// /// Releases all unmanaged resources. /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected override void Dispose(bool disposing) { if (m_Managed != null) { m_Managed.Clear(); m_Managed = null; } /* if (m_Provider != 0) { SspiProvider.CryptReleaseContext(m_Provider, 0); m_Provider = 0; }*/ try { GC.SuppressFinalize(this); } catch {} m_Disposed = true; } /// /// Gets or sets the block size of the cryptographic operation in bits. /// /// The block size in bits. /// The block size is invalid. /// The block size is the basic unit of data that can be encrypted or decrypted in one operation. Messages longer than the block size are handled as successive blocks; messages shorter than the block size must be padded with extra bits to reach the size of a block. Valid block sizes are determined by the symmetric algorithm used. public override int BlockSize { get { return m_Managed.BlockSize; } set { m_Managed.BlockSize = value; } } /// /// Gets or sets the feedback size of the cryptographic operation in bits. /// /// The feedback size in bits. /// The feedback size is larger than the block size. /// The feedback size determines the amount of data that is fed back to successive encryption or decryption operations. The feedback size cannot be greater than the block size. public override int FeedbackSize { get { return m_Managed.FeedbackSize; } set { m_Managed.FeedbackSize = value; } } /// /// Gets or sets the initialization vector (IV) for the symmetric algorithm. /// /// The initialization vector. /// An attempt is made to set the IV to a null reference (Nothing in Visual Basic). /// An attempt is made to set the IV to an invalid size. /// If this property is a null reference (Nothing in Visual Basic) when it is used, is called to create a new random value. public override byte[] IV { get { return m_Managed.IV; } set { m_Managed.IV = value; } } /// /// Gets or sets the secret key for the symmetric algorithm. /// /// The secret key to be used for the symmetric algorithm. /// An attempt is made to set the key to a null reference (Nothing in Visual Basic). /// ///

The secret key is used both for encryption and for decryption. For a symmetric algorithm to be secure, the secret key must be known only to the sender and the receiver. The valid key sizes are specified by the particular symmetric algorithm implementation and are listed in .

///

If this property is a null reference (Nothing in Visual Basic) when it is used, is called to create a new random value.

///
public override byte[] Key { get { return m_Managed.Key; } set { m_Managed.Key = value; } } /// /// Gets or sets the size of the secret key used by the symmetric algorithm in bits. /// /// The size of the secret key used by the symmetric algorithm. /// The key size is not valid. /// The valid key sizes are specified by the particular symmetric algorithm implementation and are listed in . public override int KeySize { get { return m_Managed.KeySize; } set { m_Managed.KeySize = value; } } /// /// Gets the block sizes that are supported by the symmetric algorithm. /// /// An array containing the block sizes supported by the algorithm. /// Only block sizes that match an entry in this array are supported by the symmetric algorithm. public override KeySizes[] LegalBlockSizes { get { return m_Managed.LegalBlockSizes; } } /// /// Gets the key sizes that are supported by the symmetric algorithm. /// /// An array containing the key sizes supported by the algorithm. /// Only key sizes that match an entry in this array are supported by the symmetric algorithm. public override KeySizes[] LegalKeySizes { get { return m_Managed.LegalKeySizes; } } /// /// Gets or sets the mode for operation of the symmetric algorithm. /// /// The mode for operation of the symmetric algorithm. /// The cipher mode is not one of the CipherMode values. /// See CipherMode for a description of specific modes. public override CipherMode Mode { get { return m_Managed.Mode; } set { m_Managed.Mode = value; } } /// /// Gets or sets the padding mode used in the symmetric algorithm. /// /// The padding mode used in the symmetric algorithm. /// The padding mode is not one of the PaddingMode values. /// Most plain text messages do not consist of a number of bytes that completely fill blocks. Often, there are not enough bytes to fill the last block. When this happens, a padding string is added to the text. For example, if the block length is 64 bits and the last block contains only 40 bits, 24 bits of padding are added. See for a description of specific modes. public override PaddingMode Padding { get { return m_Managed.Padding; } set { m_Managed.Padding = value; } } /// /// Generates a random initialization vector (IV) to be used for the algorithm. /// /// Use this method to generate a random IV when none is specified. public override void GenerateIV() { m_Managed.GenerateIV(); } /// /// Generates a random Key to be used for the algorithm. /// /// Use this method to generate a random key when none is specified. public override void GenerateKey() { m_Managed.GenerateKey(); } /// /// Creates a symmetric decryptor object with the specified and initialization vector (). /// /// The secret key to be used for the symmetric algorithm. /// The IV to be used for the symmetric algorithm. /// A symmetric Rijndael decryptor object. /// This method decrypts an encrypted message created using the overload with the same signature. public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[] rgbIV) { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); if (rgbKey == null || rgbIV == null) throw new ArgumentNullException(); if (this.Mode == CipherMode.CTS || this.Mode == CipherMode.OFB || this.Mode == CipherMode.CFB) throw new CryptographicException(this.Mode.ToString() + " is not supported by this implementation."); try { if (CanUseUnmanaged(rgbKey.Length * 8, rgbIV.Length * 8, this.Padding)) return new RijndaelUnmanagedTransform(GetKeyType(rgbKey.Length * 8), CryptoMethod.Decrypt, rgbKey, rgbIV, this.Mode, this.FeedbackSize, this.Padding); } catch {} return m_Managed.CreateDecryptor(rgbKey, rgbIV); } /// /// Creates a symmetric encryptor object with the specified and initialization vector (). /// /// The secret key to be used for the symmetric algorithm. /// The IV to be used for the symmetric algorithm. /// A symmetric Rijndael encryptor object. /// Use the overload with the same signature to decrypt the result of this method. public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[] rgbIV) { if (m_Disposed) throw new ObjectDisposedException(this.GetType().FullName); if (rgbKey == null || rgbIV == null) throw new ArgumentNullException(); if (this.Mode == CipherMode.CTS || this.Mode == CipherMode.OFB || this.Mode == CipherMode.CFB) throw new CryptographicException(this.Mode.ToString() + " is not supported by this implementation."); try { if (CanUseUnmanaged(rgbKey.Length * 8, rgbIV.Length * 8, this.Padding)) return new RijndaelUnmanagedTransform(GetKeyType(rgbKey.Length * 8), CryptoMethod.Encrypt, rgbKey, rgbIV, this.Mode, this.FeedbackSize, this.Padding); } catch {} return m_Managed.CreateEncryptor(rgbKey, rgbIV); } /// /// Converts a key size into an instance of the enum. /// /// The size of the key, in bits. /// One of the CryptoAlgorithm values. /// is invalid. private CryptoAlgorithm GetKeyType(int size) { if (size == 128) return CryptoAlgorithm.Rijndael128; else if (size == 192) return CryptoAlgorithm.Rijndael192; else if (size == 256) return CryptoAlgorithm.Rijndael256; else throw new CryptographicException("Invalid keysize!"); } /// /// Gets the value that corresponds with the value of the property. /// /// One of the CryptoAlgorithm values. private CryptoAlgorithm KeyType { get { return GetKeyType(this.KeySize); } } /// /// Returns a value that indicates whether the unmanaged Rijndael implementation can be used with the specified parameters. /// /// A value that indicates the size of the key, in bits. /// A value that indicates the size of the blocks, in bits. /// One of the values. /// true if the unmanaged implementation can be used, false otherwise. private bool CanUseUnmanaged(int keySize, int blockSize, PaddingMode padding) { return (m_Provider != 0) && // make sure the unmanaged AES CSP is available (blockSize == 128) && // although Rijndael supports block sizes of 128, 192 and 256 bits, // the AES only specifies a blocksize of 128 bits [and only this block size is supported by the unmanaged implementation] (padding == PaddingMode.PKCS7 || padding == PaddingMode.None) && // only PKCS7 padding is supported (keySize == 128 || keySize == 192 || keySize == 256); // make sure we use one of the supported key lengths } /// /// Returns a value that indicates whether the unmanaged Rijndael implementation can be used with the current parameters. /// /// true if the unmanaged implementation can be used, false otherwise. private bool CanUseUnmanaged() { return CanUseUnmanaged(this.KeySize, this.BlockSize, this.Padding); } /// Holds a managed instance. private RijndaelManaged m_Managed; /// Handle of the unmanaged AES CSP. private int m_Provider; /// Holds a boolean that indicates whether this object has been disposed. private bool m_Disposed; } }