#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Collections.Generic; using System.Security.Cryptography; using System.IO; using CSharpTest.Net.IO; using System.Text; using System.Collections; using CSharpTest.Net.Bases; using System.Security; namespace CSharpTest.Net.Crypto { /// Creates a salted hash public sealed class PasswordHash : Comparable, IDisposable { readonly SaltedData _hash; /// Defines the derived hash iteration count used public const int StandardIterations = 1024; /// recreates a hash from a base-64 encoded string public static PasswordHash FromString(string hash) { return FromBytes(Convert.FromBase64String(hash)); } /// recreates a hash from the bytes returned by ToArray() public static PasswordHash FromBytes(byte[] hash) { return new PasswordHash(new SaltedData(hash)); } /// Recreates a hash private PasswordHash(SaltedData hash) { _hash = hash; } /// Creates a salted hash from the given bytes and salt public PasswordHash(Stream bytes, Salt salt) { using (bytes) using (HashDerivedBytes hashBytes = new HashDerivedBytes(bytes, salt, StandardIterations)) _hash = new SaltedData(salt, hashBytes.GetBytes(32)); } #region CTor overloads /// Creates a salted hash from the given password public PasswordHash(Password bytes) : this(bytes.ReadBytes(), new Salt()) { } /// Creates a salted hash from the given password and salt public PasswordHash(Password bytes, Salt salt) : this(bytes.ReadBytes(), salt) { } /// Creates the hash from the given bytes and salt public PasswordHash(bool clear, byte[] bytes, Salt salt) : this(new MemoryStream(bytes, 0, bytes.Length, false, false), salt) { if (clear) Array.Clear(bytes, 0, bytes.Length); } /// Creates the hash from the given bytes public PasswordHash(bool clear, byte[] bytes) : this(clear, bytes, new Salt()) { } /// Creates the hash from the given data and salt public PasswordHash(string data, Salt salt) : this(true, Password.Encoding.GetBytes(data), salt) { } /// Creates the hash from the given data and salt public PasswordHash(string data) : this(true, Password.Encoding.GetBytes(data), new Salt()) { } /// Creates the hash from the given data and salt public PasswordHash(SecureString data, Salt salt) : this(true, SecureStringUtils.ToByteArray(data, Password.Encoding), salt) { } /// Creates the hash from the given data and salt public PasswordHash(SecureString data) : this(true, SecureStringUtils.ToByteArray(data, Password.Encoding), new Salt()) { } #endregion /// Disposes of hash bytes public void Dispose() { _hash.Dispose(); } /// Gets the hash protected override int HashCode { get { return BinaryComparer.GetHashCode(_hash.ToArray()); } } /// Compares the hash public override int CompareTo(PasswordHash other) { if (((object)other) == null) return 1; return BinaryComparer.Compare(_hash.ToArray(), other._hash.ToArray()); } /// returns the salted-hash length in bytes public int Length { get { return _hash.Length; } } /// returns the salt used to create this hash public Salt Salt { get { return _hash.Salt; } } /// returns the salted-hash bytes public byte[] ToArray() { return _hash.ToArray(); } #region bool VerifyPassword(Password password) /// Returns true if the provided password matches this hash public bool VerifyPassword(Password password) { PasswordHash hash = password.CreateHash(Salt); return this.Equals(hash); } /// Returns true if the provided password matches this hash public bool VerifyPassword(Stream password) { PasswordHash hash = new PasswordHash(password, this.Salt); return this.Equals(hash); } /// Returns true if the provided password matches this hash public bool VerifyPassword(byte[] password) { using (Stream pb = new MemoryStream(password, 0, password.Length, false, false)) return VerifyPassword(pb); } /// Returns true if the provided password matches this hash public bool VerifyPassword(string password) { byte[] tmp = Password.Encoding.GetBytes(password); try { return VerifyPassword(tmp); } finally { Array.Clear(tmp, 0, tmp.Length); } } #endregion #region Object overrides /// Returns the hash as a base-64 encoded string public override string ToString() { return Convert.ToBase64String(_hash.ToArray()); } #endregion } }