/// <summary> /// Gets a little endian byte array representation of the version code. /// </summary> /// <returns>either <c>0x0100</c> for SQL 2000-2008 or <c>0x0200</c> for SQL 2012-SQL 2017</returns> /// <exception cref="ArgumentOutOfRangeException"></exception> /// <remarks>Originally written because I didn't take endianness into account</remarks> public static byte[] GetBytes(this DbaPasswordHashVersion version) { switch (version) { case DbaPasswordHashVersion.Sql2000: return(new byte[] { 1, 0 }); case DbaPasswordHashVersion.Sql2012: return(new byte[] { 2, 0 }); default: throw new ArgumentOutOfRangeException(nameof(version), version, "Cannot call GetBytes on an invalid password has version."); } }
public static byte[] GenerateHash (string password, UInt32?salt = null, DbaPasswordHashVersion version = DbaPasswordHashVersion.Sql2016, bool caseInsensitive = false) { var saltBytes = new byte[4]; if (salt == null) { RngCryptoServiceProvider.GetNonZeroBytes(saltBytes); } else { saltBytes = BitConverter.GetBytes(salt.Value); } var passwordBytes = Encoding.Unicode.GetBytes(password).Concat(saltBytes).ToArray(); byte[] hash; switch (version) { case DbaPasswordHashVersion.Sql2005: hash = Sha1.ComputeHash(passwordBytes); if (caseInsensitive) { var upperCasePasswordBytes = Encoding.Unicode.GetBytes(password.ToUpper()).Concat(saltBytes).ToArray(); hash = hash.Concat(Sha1.ComputeHash(upperCasePasswordBytes)).ToArray(); } break; case DbaPasswordHashVersion.Sql2012: if (caseInsensitive) { throw new ArgumentException("Only Sql Server 2000 passwords can be case insensitive."); } hash = Sha512.ComputeHash(passwordBytes); break; default: throw new ArgumentOutOfRangeException(nameof(version), $"Unsupported password version of {(uint) version}"); } return(version.GetBytes().Concat(saltBytes).Concat(hash).ToArray()); }