/// <summary> /// Returns the FSHP hash of password /// </summary> /// <param name="password">Encoded to bytes with UTF-8.</param> /// <param name="salt">Byte representation of salt to be used in hashing Encoded to bytes with UTF-8.</param> /// <param name="saltlen">Length of the salt to be generated.</param> /// <param name="rounds">Number of hashing rounds.</param> /// <param name="variant">FSHP variant indicating the behaviour and/or hashing algorithm /// to be used. /// 0: SHA-1 (not recommended) /// 1: SHA-256 /// 2: SHA-384 /// 3: SHA-512 /// </param> /// <returns>FSHP hash of password</returns> public static String crypt(byte[] password, byte[] salt, int saltlen, int rounds, int variant) { // Ensure we have sane values for salt length and rounds. if (saltlen < 0) saltlen = 0; if (rounds < 1) rounds = 1; // if salt is null, we generate a random one. RNGCryptoServiceProvider CryptoRng = new RNGCryptoServiceProvider(); if (salt == null) { salt = new byte[saltlen]; CryptoRng.GetBytes(salt); } else { saltlen = salt.Length; } HashAlgorithm hash; switch (variant) { case 0: hash = new SHA1Managed(); break; case 1: hash = new SHA256Managed(); break; case 2: hash = new SHA384Managed(); break; case 3: hash = new SHA512Managed(); break; default: throw new Exception(); } hash.Initialize(); byte[] saltedPassword = new byte[salt.Length + password.Length]; salt.CopyTo(saltedPassword, 0); password.CopyTo(saltedPassword, salt.Length); // Round 1 byte[] hashBytes = hash.ComputeHash(saltedPassword); // ...and other rounds. for (int i = 1; i < rounds; i++) { hashBytes = hash.ComputeHash(hashBytes); } string meta = "{FSHP" + variant + "|" + saltlen + "|" + rounds +"}"; byte[] saltdigest = new byte[salt.Length + hashBytes.Length]; salt.CopyTo(saltdigest, 0); hashBytes.CopyTo(saltdigest, salt.Length); string b64saltdigest = Convert.ToBase64String(saltdigest); return meta + b64saltdigest; }