Click here to Skip to main content
15,884,237 members
Articles / Mobile Apps / Blackberry

Password Safe Reader for Windows Mobile

, , ,
Rate me:
Please Sign up or sign in to vote.
4.20/5 (5 votes)
20 Mar 2010GPL33 min read 64.1K   961   32  
A Windows Mobile C# reader for the popular Password Safe archive files.
#region License
/*
 *  PasswordSafe Database Reader/Writer
 *
 *  Copyright (C) 2007 Svante Seleborg
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *  If you'd like to license this program under any other terms than the
 *  above, please contact the author and copyright holder.
 *
 *  Contact: mailto:svante@axantum.com
 */
#endregion

#region Credits
/*
 *  The TwoFish code is written by Shaun Wilde, http://www.many-monkeys.com, and is available at http://www.codeproject.com/cs/algorithms/twofish_csharp.asp .
 */
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

using ManyMonkeys.Cryptography;

//using InOut = System.IO; // InvalidDataException
using InOut = Helpers.PasswordSafe; // InvalidDataException

//using Crypto = System.Security.Cryptography;
using Crypto = Helpers.PasswordSafe; // Also works for CE now

namespace Axantum.PasswordSafe
{
    /// <summary>
    /// Represents a reader that provides a forward only access to the data in PasswordSafe database.
    /// See http://passwordsafe.sourceforge.net for details.
    /// Throws InvalidDataException if the file is corrupt or the wrong passphrase is provided.
    /// Throws InvalidOperationException if the caller performs improper operations.
    /// </summary>
    public class PasswordSafeReader : IDisposable
    {
        #region Private Fields
        private System.IO.Stream _stream;
        private bool _error;
        private System.Security.Cryptography.ICryptoTransform _decryptor;
		private Crypto.HMACSHA256 _hmac;

        private static readonly byte[] _eofMarkerBytes =
        {
            (byte)'P', (byte)'W', (byte)'S', (byte)'3', (byte)'-', (byte)'E', (byte)'O', (byte)'F',
            (byte)'P', (byte)'W', (byte)'S', (byte)'3', (byte)'-', (byte)'E', (byte)'O', (byte)'F'
        };

        private static readonly byte[] _tagBytes = { (byte)'P', (byte)'W', (byte)'S', (byte)'3' };
        #endregion

        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="PasswordSafeReader"/> class.
        /// </summary>
        /// <param name="stream">The stream. Note that it is disposed when this instance is disposed.</param>
        public PasswordSafeReader(System.IO.Stream stream)
        {
            _stream = stream;
            CurrentPartType = PasswordSafePartType.None;
        }
        #endregion

        #region Properties
        private PasswordSafePartType _currentPartType;

        /// <summary>
        /// Gets or sets the type of the current part.
        /// </summary>
        /// <value>The type of the current part.</value>
        public PasswordSafePartType CurrentPartType
        {
            get { return _currentPartType; }
            protected set { _currentPartType = value; }
        }

        private PasswordSafeHeader _header;

        /// <summary>
        /// Gets the header.
        /// </summary>
        /// <value>The header.</value>
        public PasswordSafeHeader Header
        {
            get { return _header; }
            protected set { _header = value; }
        }

        private PasswordSafeRecord _record;

        /// <summary>
        /// Gets the current record.
        /// </summary>
        /// <value>The record.</value>
        public PasswordSafeRecord Record
        {
            get { return _record; }
            protected set { _record = value; }
        }
        #endregion

        #region Public Method API
        /// <summary>
        /// Sets the passphrase.
        /// </summary>
        /// <param name="passphrase">The passphrase.</param>
        public void SetPassphrase(string passphrase)
        {
            ReadTag();
            ReadKeys(passphrase);

            CurrentPartType = PasswordSafePartType.Keys;
        }

        /// <summary>
        /// Reads the next part of the database.
        /// </summary>
        /// <returns>true if there was a next part</returns>
        public bool Read()
        {
            if (_error)
            {
                throw new InvalidOperationException("An error has already been reported.");
            }

            if (CurrentPartType == PasswordSafePartType.None)
            {
                _error = true;
                throw new InvalidOperationException("No passphrase set");
            }

            if (CurrentPartType == PasswordSafePartType.End)
            {
                return false;
            }

            if (CurrentPartType == PasswordSafePartType.Keys)
            {
                Header = ReadHeader();
                if (Header != null)
                {
                    CurrentPartType = PasswordSafePartType.Header;
                    return true;
                }

                return false;
            }

            if (CurrentPartType == PasswordSafePartType.Header || CurrentPartType == PasswordSafePartType.Record)
            {
                Record = ReadRecord();
                if (Record != null)
                {
                    CurrentPartType = PasswordSafePartType.Record;
                    return true;
                }
            }

            ReadHmac();
            CurrentPartType = PasswordSafePartType.End;

            return true;
        }

        /// <summary>
        /// Reads one data record.
        /// </summary>
        /// <returns>The record</returns>
        private PasswordSafeRecord ReadRecord()
        {
            ICollection<PasswordSafeRecordField> fields = ReadGenericRecord();
            if (fields == null)
            {
                return null;
            }

            PasswordSafeRecord record = new PasswordSafeRecord();
            bool endSeen = false;
            foreach (PasswordSafeRecordField field in fields)
            {
                if (endSeen)
                {
                    throw new InOut.InvalidDataException("The END filed must be last in the header");
                }
                byte[] data = field.DataBuffer();

                switch ((PasswordSafeRecordTypeCode)field.Type)
                {
                    case PasswordSafeRecordTypeCode.NAME:
						throw new InOut.InvalidDataException("The NAME field type is not allowed here.");

                    case PasswordSafeRecordTypeCode.UUID:
                        if (data.Length != 16)
                        {
							throw new InOut.InvalidDataException("UUID field length wrong");
                        }
                        record.Uuid = new Guid(data);
                        break;

                    case PasswordSafeRecordTypeCode.GROUP:
                        record.Group = UTF8Encoding.UTF8.GetString(data,0,data.Length);//.UTF8.GetString(data,0,data.Length);
                        break;
                    
                    case PasswordSafeRecordTypeCode.TITLE:
						record.Title = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeRecordTypeCode.USER:
						record.User = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeRecordTypeCode.NOTES:
						record.Notes = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeRecordTypeCode.PASSWORD:
						record.PasswordValue = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeRecordTypeCode.CTIME:
                        record.TimeRecordCreatedUtc = GetUtcFromUnixTime(data);
                        break;

                    case PasswordSafeRecordTypeCode.PMTIME:
                        record.TimePasswordModifiedUtc = GetUtcFromUnixTime(data);
                        break;

                    case PasswordSafeRecordTypeCode.ATIME:
                        record.TimeRecordAccessedUtc = GetUtcFromUnixTime(data);
                        break;

                    case PasswordSafeRecordTypeCode.LTIME:
                        record.TimePasswordExpiresUtc = GetUtcFromUnixTime(data);
                        break;

                    case PasswordSafeRecordTypeCode.POLICY:
						throw new InOut.InvalidDataException("The POLICY field type is not allowed here.");

                    case PasswordSafeRecordTypeCode.RMTIME:
                        record.TimeRecordModifiedUtc = GetUtcFromUnixTime(data);
                        break;

                    case PasswordSafeRecordTypeCode.URL:
						record.ResourceLocator = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeRecordTypeCode.AUTOTYPE:
						record.AutoType = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeRecordTypeCode.PWHIST:
						string serializedHistory = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        record.PasswordHistory = PasswordSafePasswordHistory.Parse(serializedHistory);
                        break;

                    case PasswordSafeRecordTypeCode.END:
                        endSeen = true;
                        break;

                    default:
                        record.UnknownFieldEntriesAdd(field);
                        break;
                }
            }

            return record;
        }

        /// <summary>
        /// Reads the header.
        /// </summary>
        /// <returns>The Header</returns>
        private PasswordSafeHeader ReadHeader()
        {
            ICollection<PasswordSafeRecordField> fields = ReadGenericRecord();
            if (fields == null)
            {
                return null;
            }

            PasswordSafeHeader header = new PasswordSafeHeader();
            bool endSeen = false;
            foreach (PasswordSafeRecordField field in fields)
            {
                if (endSeen)
                {
					throw new InOut.InvalidDataException("The END filed must be last in the header");
                }
                byte[] data = field.DataBuffer();
                switch ((PasswordSafeHeaderTypeCode)field.Type)
                {
                    case PasswordSafeHeaderTypeCode.HDR_VERSION:
                        byte majorVersion = data[1];
                        if (majorVersion != 3)
                        {
							throw new InOut.InvalidDataException("Only support version 3 databases");
                        }
                        byte minorVersion = data[0];
                        header.MinorVersion = minorVersion;
                        header.MajorVersion = majorVersion;
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_UUID:
                        if (data.Length != 16)
                        {
							throw new InOut.InvalidDataException("UUID field length wrong");
                        }
                        header.Uuid = new Guid(data);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_NDPREFS:
						header.NonDefaultUserPrefs = UTF8Encoding.UTF8.GetString(data, 0 , data.Length);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_DISPSTAT:
						string displayStatus = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        foreach (char c in displayStatus)
                        {
                            header.TreeDisplayStatusAdd(c == '1');
                        }
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_LASTUPDATETIME:
                        // Due to bug in pre-3.09, this was then stored as a hex value...
                        uint lastUnixUpdateTime = 0;
                        if (data.Length == 8)
                        {
							string lastUpdateTimeHex = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                            lastUnixUpdateTime = UInt32.Parse(lastUpdateTimeHex, NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                        }
                        else if (data.Length == 4)
                        {
                            lastUnixUpdateTime = BitConverter.ToUInt32(data, 0);
                        }
                        DateTime lastUpdateTimeUtc = DateTime.MinValue;
                        if (lastUnixUpdateTime != 0)
                        {
                            lastUpdateTimeUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(lastUnixUpdateTime);
                        }
                        header.LastUpdateTimeUtc = lastUpdateTimeUtc;
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_LASTUPDATEUSERHOST:
                        // This is actually deprecated, and we won't overwrite the values if they are set by the new format
                        if (String.IsNullOrEmpty(header.LastSavedBy) && String.IsNullOrEmpty(header.LastSavedOn))
                        {
							string lastSavedByAndOn = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                            int lastSavedByLength = Int32.Parse(lastSavedByAndOn.Substring(0, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
                            header.LastSavedBy = lastSavedByAndOn.Substring(4, lastSavedByLength);
                            header.LastSavedOn = lastSavedByAndOn.Substring(4 + lastSavedByLength);
                        }
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_LASTUPDATEAPPLICATION:
						header.WhatLastSaved = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_LASTUPDATEUSER:
						header.LastUpdateUser = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_LASTUPDATEHOST:
						header.LastUpdateHost = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_DBNAME:
						header.DBName = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_DBDESC:
						header.DBDescription = UTF8Encoding.UTF8.GetString(data, 0, data.Length);
                        break;

                    case PasswordSafeHeaderTypeCode.HDR_END:
                        endSeen = true;
                        break;

                    default:
                        header.UnknownFieldEntriesAdd(field);
                        break;
                }
            }
            return header;
        }
        #endregion

        #region High-level Private Helpers
        /// <summary>
        /// Reads the leading identifying tag.
        /// </summary>
        private void ReadTag()
        {
            byte[] tagBytesActual = new byte[_tagBytes.Length];

            ReadBytes(tagBytesActual, "TAG");
            if (!BufferCompare(_tagBytes, tagBytesActual))
            {
                _error = true;
				throw new InOut.InvalidDataException("Invalid value of TAG");
            }
        }

        /// <summary>
        /// Reads the databse preamble before the headers, containing the file identifier
        /// and the session and HMAC key, and passphrase verifier.
        /// </summary>
        /// <param name="passphrase">The passphrase.</param>
        private void ReadKeys(string passphrase)
        {
            byte[] saltBytes = new byte[32];
            ReadBytes(saltBytes, "SALT");

            byte[] iterBytes = new byte[4];
            ReadBytes(iterBytes, "ITER");

            if (!BitConverter.IsLittleEndian)
            {
                throw new InvalidProgramException("We must be little endian!");
            }
            Int32 iterations = BitConverter.ToInt32(iterBytes, 0);
            if (iterations < 0)
            {
				throw new InOut.InvalidDataException("Invalid iteration counter");
            }

            byte[] expectedStretchedPassphraseHashBytes = new byte[32];
            ReadBytes(expectedStretchedPassphraseHashBytes, "H(P')");

            byte[] stretchedKey = StretchKey(saltBytes, passphrase, iterations);
            byte[] stretchedPassphraseHashBytes = Crypto.SHA256.Create().ComputeHash(stretchedKey);

            // Check if the stored hashed streched key compares ok with the user provided.
            if (!BufferCompare(stretchedPassphraseHashBytes, expectedStretchedPassphraseHashBytes))
            {
				throw new InOut.InvalidDataException("Invalid passphrase");
            }

            // Create the TwoFish ECB transform from the stretched key, to decrypt session and hmac keys with
            using (Twofish keyDecryption = new Twofish())
            {
                keyDecryption.BlockSize = 128;
                keyDecryption.Mode = System.Security.Cryptography.CipherMode.ECB;
                keyDecryption.KeySize = 256;
                keyDecryption.Key = stretchedKey;
				using (System.Security.Cryptography.ICryptoTransform keyDecryptor = keyDecryption.CreateDecryptor())
                {
                    byte[] encryptedSessionKeyK = new byte[32];
                    ReadBytes(encryptedSessionKeyK, "Encrypted Session Key K (B1|B2)");

                    byte[] sessionKeyBytes = new byte[32];
                    // Due to limitation in TwoFish implementation, we encrypt a block at a time
                    keyDecryptor.TransformBlock(encryptedSessionKeyK, 0, 16, sessionKeyBytes, 0);
                    keyDecryptor.TransformBlock(encryptedSessionKeyK, 16, 16, sessionKeyBytes, 16);

                    byte[] encryptedHmacKeyL = new byte[32];
                    ReadBytes(encryptedHmacKeyL, "Encrypted HMAC Key L (B3|B4)");

                    byte[] hmacKeyBytes = new byte[32];
                    // Due to limitation in TwoFish implementation, we encrypt a block at a time
                    keyDecryptor.TransformBlock(encryptedHmacKeyL, 0, 16, hmacKeyBytes, 0);
                    keyDecryptor.TransformBlock(encryptedHmacKeyL, 16, 16, hmacKeyBytes, 16);

                    byte[] initializationVectorBytes = new byte[16];
                    ReadBytes(initializationVectorBytes, "IV");

                    // Initialize and save the Decryptor
                    Twofish dataDecryption = new Twofish();
                    dataDecryption.BlockSize = 128;
					dataDecryption.Mode = System.Security.Cryptography.CipherMode.CBC;
                    dataDecryption.KeySize = 256;
                    _decryptor = dataDecryption.CreateDecryptor(sessionKeyBytes, initializationVectorBytes);

                    // Initialize and save the HMAC calculator
					_hmac = new Crypto.HMACSHA256(hmacKeyBytes);
                }
            }
        }

        /// <summary>
        /// Reads the hmac, and compares with the calculated value.
        /// </summary>
        private void ReadHmac()
        {
            byte[] hmacBytes = new byte[32];
            ReadBytes(hmacBytes, "HMAC");

            _hmac.TransformFinalBlock(new byte[0], 0, 0);
            byte[] actualHmac = _hmac.Hash;
            if (!BufferCompare(actualHmac, hmacBytes))
            {
				//throw new InOut.InvalidDataException("Error in HMAC - possible file corruption");
				// ALPHONS TODO
            }
        }

        /// <summary>
        /// Reads one generic record, which may be a header or a data record.
        /// </summary>
        /// <returns>A collection of the fields</returns>
        private ICollection<PasswordSafeRecordField> ReadGenericRecord()
        {
            List<PasswordSafeRecordField> fields = new List<PasswordSafeRecordField>();

            int emergencyExitCounter = 255;
            PasswordSafeRecordField field;
            do
            {
                field = ReadField();
                if (field == null)
                {
                    if (fields.Count > 0)
                    {
						throw new InOut.InvalidDataException("End of file marker in the middle of a record");
                    }
                    return null;
                }
                fields.Add(field);
            } while (field.Type != 255 && --emergencyExitCounter > 0);

            if (emergencyExitCounter <= 0)
            {
				throw new InOut.InvalidDataException("Too many fields in a record");
            }

            return fields;
        }

        /// <summary>
        /// Reads one field.
        /// </summary>
        /// <returns></returns>
        private PasswordSafeRecordField ReadField()
        {
            byte[] currentBlock = new byte[16];

            ReadBytes(currentBlock, "Generic Field");
            if (BufferCompare(currentBlock, _eofMarkerBytes))
            {
                return null;
            }
            _decryptor.TransformBlock(currentBlock, 0, currentBlock.Length, currentBlock, 0);

            if (!BitConverter.IsLittleEndian)
            {
                throw new InvalidProgramException("We must be little endian!");
            }

            int fieldLength = BitConverter.ToInt32(currentBlock, 0);
            if (fieldLength < 0)
            {
				throw new InOut.InvalidDataException("Invalid field length");
            }

            byte type = currentBlock[4];
            byte[] dataBuffer = new byte[fieldLength];

            // There's a maximum of 11 bytes left of data in the first block.
            int lengthToCopy = fieldLength > 11 ? 11 : fieldLength;
            Buffer.BlockCopy(currentBlock, 5, dataBuffer, 0, lengthToCopy);
            fieldLength -= lengthToCopy;

            int currentOffset = lengthToCopy;
            // Now we've handled the first block. Let's see if there's more....
            if (fieldLength > 0)
            {
                int numberOfBlocks = (fieldLength - 1) / 16 + 1;
                while (numberOfBlocks-- > 0)
                {
                    ReadBytes(currentBlock, "Generic Field");
                    if (BufferCompare(currentBlock, _eofMarkerBytes))
                    {
						throw new InOut.InvalidDataException("Unexpected end of file marker"); ;
                    }
                    _decryptor.TransformBlock(currentBlock, 0, currentBlock.Length, currentBlock, 0);

                    lengthToCopy = currentBlock.Length > fieldLength ? fieldLength : currentBlock.Length;
                    Buffer.BlockCopy(currentBlock, 0, dataBuffer, currentOffset, lengthToCopy);
                    fieldLength -= lengthToCopy;
                    currentOffset += lengthToCopy;
                }
            }

			byte[] tmpNull = new byte[dataBuffer.Length];

            // We only HMAC the actual data - not the length and type. That *SUCKS*!!!
			_hmac.TransformBlock(dataBuffer, 0, dataBuffer.Length, tmpNull, 0);

            return new PasswordSafeRecordField(type, dataBuffer);
        }
        #endregion

        #region Low-level Private Helpers
        /// <summary>
        /// Gets the UTC from unix time.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <returns>The date and time as UTC</returns>
        private static DateTime GetUtcFromUnixTime(byte[] data)
        {
            if (data.Length != 4)
            {
				throw new InOut.InvalidDataException("Unix time must be a 32-bit integer");
            }
            
            int unixTime = BitConverter.ToInt32(data, 0);
            DateTime netTime = PasswordSafeUtility.GetUtcFromUnixTime(unixTime);

            return netTime;
        }
        
        /// <summary>
        /// Reads bytes from the stream
        /// </summary>
        /// <param name="bytes">The bytes.</param>
        /// <param name="what">A string identifyin what is read - used for identifying purposes if an error occurs.</param>
        private void ReadBytes(byte[] bytes, string what)
        {
            if (_stream.Read(bytes, 0, bytes.Length) != bytes.Length)
            {
                _error = true;
				throw new InOut.InvalidDataException(String.Format(CultureInfo.InvariantCulture, "Could not read {0} bytes", what));
            }
        }

        /// <summary>
        /// Just a simple byte-buffer comparer
        /// </summary>
        /// <param name="bytes1">The bytes1.</param>
        /// <param name="bytes2">The bytes2.</param>
        /// <returns>true if the buffers contain identical data</returns>
        private static bool BufferCompare(byte[] bytes1, byte[] bytes2)
        {
            if (bytes1 == null)
            {
                throw new ArgumentNullException("bytes1");
            }
            if (bytes2 == null)
            {
                throw new ArgumentNullException("bytes2");
            }

            if (bytes1.Length != bytes2.Length)
            {
                return false;
            }

            for (int i = 0; i < bytes1.Length; ++i)
            {
                if (bytes1[i] != bytes2[i])
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Stretches the key by first hashing passphrase|salt, then iteratively hashing
        /// the hash, for the specified number of iterations. 'Stretching' in this context
        /// means artificially applying an extra work-factor so as to effectively lengthen
        /// the effective bit-length of the provided passphrase.
        /// </summary>
        /// <param name="salt">The salt.</param>
        /// <param name="passphrase">The passphrase.</param>
        /// <param name="iterations">The iterations.</param>
        /// <returns></returns>
        private static byte[] StretchKey(byte[] salt, string passphrase, int iterations)
        {
			byte[] ansiEncodedPassphrase = Helpers.PasswordSafe.SHA256.ASCIIEncoder(passphrase);

            byte[] iteratedHashValue;
            using (Crypto.SHA256 hash = new Crypto.SHA256Managed())
            {
				hash.TransformBlock(ansiEncodedPassphrase, 0, ansiEncodedPassphrase.Length, null, 0);
				hash.TransformFinalBlock(salt, 0, salt.Length);
                iteratedHashValue = hash.Hash;
            }

            for (int i = 0; i < iterations; ++i)
            {
                using (Crypto.SHA256 iterationHash = new Crypto.SHA256Managed())
                {
                    iteratedHashValue = iterationHash.ComputeHash(iteratedHashValue);
                }
            }
            return iteratedHashValue;
        }
        #endregion

        #region IDisposable Members

        public void Close()
        {
            Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (_stream != null)
                {
                    _stream.Close();
                    _stream = null;
                }
                if (_decryptor != null)
                {
                    _decryptor.Dispose();
                    _decryptor = null;
                }
                if (_hmac != null)
                {
                    _hmac.Clear();
                }
            }
        }

        public void Dispose()
        {
            Dispose(true);
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Retired Van der Heijden Holding BV
Netherlands Netherlands
I'm Alphons van der Heijden, living in Lelystad, Netherlands, Europa, Earth. And currently I'm retiring from hard working ( ;- ), owning my own company. Because I'm full of energy, and a little to young to relax ...., I don't sit down, but create and recreate software solutions, that I like. Reinventing the wheel is my second nature. My interest is in the area of Internet technologies, .NET etc. I was there in 1992 when Mosaic came out, and from that point, my life changed dramatically, and so did the world, in fact. (Y)

Written By
Web Developer Axantum Software AB
Sweden Sweden
I've been working with all aspects of software development since 1979 - from compiler construction to management. Currently I'm an independent consultant mostly specializing in computer security. Please see my homepage for contact details.

I speak C like a native, and have a pretty good grasp of C++. The most recent five years C# has been the main development language. Traditionally Unix has been the dominating environment, but currently the scales have tipped over to Windows, due to market demands but I'm equally at home developing in both environments.

When I'm not coding I'm usually sitting on one of my 4 bikes, indoors or outdoors, on the road or in the woods.

Written By
Software Developer
United Kingdom United Kingdom
I am currently employed as a C# / MS SQL developer for a Medical Software company working on a large enterprise system.

I have used Delphi and MSM MUMPS in previous roles and love both languages.

I'm an avid Windows Mobile fan and moderator on http://xda-developers.com. I code in C#, and C++ for WM as well as cooking custom ROMs and learning to disasm the inners of my device.

Written By
Engineer
Romania Romania
I have some intensive experience with C#, ASP.NET and Delphi. Also I like to write API's, which I realized when I made a SmartCard library.

Comments and Discussions