上一遍博客:http://wyait.blog.51cto.com/12674066/1918470

PHP PBKDF2 密码hash代码

/*

 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).

 * Copyright (c) 2013, Taylor Hornby

 * All rights reserved.

 *

 * Redistribution and use in source and binary forms, with or without 

 * modification, are permitted provided that the following conditions are met:

 *

 * 1. Redistributions of source code must retain the above copyright notice, 

 * this list of conditions and the following disclaimer.

 *

 * 2. Redistributions in binary form must reproduce the above copyright notice,

 * this list of conditions and the following disclaimer in the documentation 

 * and/or other materials provided with the distribution.

 *

 * 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 HOLDER 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.

 */

// These constants may be changed without breaking existing hashes.

define("PBKDF2_HASH_ALGORITHM", "sha256");

define("PBKDF2_ITERATIONS", 1000);

define("PBKDF2_SALT_BYTE_SIZE", 24);

define("PBKDF2_HASH_BYTE_SIZE", 24);

define("HASH_SECTIONS", 4);

define("HASH_ALGORITHM_INDEX", 0);

define("HASH_ITERATION_INDEX", 1);

define("HASH_SALT_INDEX", 2);

define("HASH_PBKDF2_INDEX", 3);

function create_hash($password)

{

    // format: algorithm:iterations:salt:hash

    $salt = base64_encode(mcrypt_create_iv(PBKDF2_SALT_BYTE_SIZE, MCRYPT_DEV_URANDOM));

    return PBKDF2_HASH_ALGORITHM . ":" . PBKDF2_ITERATIONS . ":" .  $salt . ":" .

        base64_encode(pbkdf2(

            PBKDF2_HASH_ALGORITHM,

            $password,

            $salt,

            PBKDF2_ITERATIONS,

            PBKDF2_HASH_BYTE_SIZE,

            true

        ));

}

function validate_password($password, $correct_hash)

{

    $params = explode(":", $correct_hash);

    if(count($params) < HASH_SECTIONS)

       return false;

    $pbkdf2 = base64_decode($params[HASH_PBKDF2_INDEX]);

    return slow_equals(

        $pbkdf2,

        pbkdf2(

            $params[HASH_ALGORITHM_INDEX],

            $password,

            $params[HASH_SALT_INDEX],

            (int)$params[HASH_ITERATION_INDEX],

            strlen($pbkdf2),

            true

        )

    );

}

// Compares two strings $a and $b in length-constant time.

function slow_equals($a, $b)

{

    $diff = strlen($a) ^ strlen($b);

    for($i = 0; $i < strlen($a) && $i < strlen($b); $i++)

    {

        $diff |= ord($a[$i]) ^ ord($b[$i]);

    }

    return $diff === 0;

}

/*

 * PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt

 * $algorithm - The hash algorithm to use. Recommended: SHA256

 * $password - The password.

 * $salt - A salt that is unique to the password.

 * $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.

 * $key_length - The length of the derived key in bytes.

 * $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.

 * Returns: A $key_length-byte key derived from the password and salt.

 *

 * Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt

 *

 * This implementation of PBKDF2 was originally created by https://defuse.ca

 * With improvements by http://www.variations-of-shadow.com

 */

function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false)

{

    $algorithm = strtolower($algorithm);

    if(!in_array($algorithm, hash_algos(), true))

        trigger_error('PBKDF2 ERROR: Invalid hash algorithm.', E_USER_ERROR);

    if($count <= 0 || $key_length <= 0)

        trigger_error('PBKDF2 ERROR: Invalid parameters.', E_USER_ERROR);

    if (function_exists("hash_pbkdf2")) {

        // The output length is in NIBBLES (4-bits) if $raw_output is false!

        if (!$raw_output) {

            $key_length = $key_length * 2;

        }

        return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);

    }

    $hash_length = strlen(hash($algorithm, "", true));

    $block_count = ceil($key_length / $hash_length);

    $output = "";

    for($i = 1; $i <= $block_count; $i++) {

        // $i encoded as 4 bytes, big endian.

        $last = $salt . pack("N", $i);

        // first iteration

        $last = $xorsum = hash_hmac($algorithm, $last, $password, true);

        // perform the other $count - 1 iterations

        for ($j = 1; $j < $count; $j++) {

            $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));

        }

        $output .= $xorsum;

    }

    if($raw_output)

        return substr($output, 0, $key_length);

    else

        return bin2hex(substr($output, 0, $key_length));

}

?>

java PBKDF2 密码hash代码

/* 

 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).

 * Copyright (c) 2013, Taylor Hornby

 * All rights reserved.

 *

 * Redistribution and use in source and binary forms, with or without 

 * modification, are permitted provided that the following conditions are met:

 *

 * 1. Redistributions of source code must retain the above copyright notice, 

 * this list of conditions and the following disclaimer.

 *

 * 2. Redistributions in binary form must reproduce the above copyright notice,

 * this list of conditions and the following disclaimer in the documentation 

 * and/or other materials provided with the distribution.

 *

 * 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 HOLDER 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.

 */

 

import java.security.SecureRandom;

import javax.crypto.spec.PBEKeySpec;

import javax.crypto.SecretKeyFactory;

import java.math.BigInteger;

import java.security.NoSuchAlgorithmException;

import java.security.spec.InvalidKeySpecException;

 

/*

 * PBKDF2 salted password hashing.

 * Author: havoc AT defuse.ca

 * www: http://crackstation.net/hashing-security.htm

 */

public class PasswordHash

{

    public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";

 

    // The following constants may be changed without breaking existing hashes.

    public static final int SALT_BYTE_SIZE = 24;

    public static final int HASH_BYTE_SIZE = 24;

    public static final int PBKDF2_ITERATIONS = 1000;

 

    public static final int ITERATION_INDEX = 0;

    public static final int SALT_INDEX = 1;

    public static final int PBKDF2_INDEX = 2;

 

    /**

     * Returns a salted PBKDF2 hash of the password.

     *

     * @param   password    the password to hash

     * @return              a salted PBKDF2 hash of the password

     */

    public static String createHash(String password)

        throws NoSuchAlgorithmException, InvalidKeySpecException

    {

        return createHash(password.toCharArray());

    }

 

    /**

     * Returns a salted PBKDF2 hash of the password.

     *

     * @param   password    the password to hash

     * @return              a salted PBKDF2 hash of the password

     */

    public static String createHash(char[] password)

        throws NoSuchAlgorithmException, InvalidKeySpecException

    {

        // Generate a random salt

        SecureRandom random = new SecureRandom();

        byte[] salt = new byte[SALT_BYTE_SIZE];

        random.nextBytes(salt);

 

        // Hash the password

        byte[] hash = pbkdf2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);

        // format iterations:salt:hash

        return PBKDF2_ITERATIONS + ":" + toHex(salt) + ":" +  toHex(hash);

    }

 

    /**

     * Validates a password using a hash.

     *

     * @param   password        the password to check

     * @param   correctHash     the hash of the valid password

     * @return                  true if the password is correct, false if not

     */

    public static boolean validatePassword(String password, String correctHash)

        throws NoSuchAlgorithmException, InvalidKeySpecException

    {

        return validatePassword(password.toCharArray(), correctHash);

    }

 

    /**

     * Validates a password using a hash.

     *

     * @param   password        the password to check

     * @param   correctHash     the hash of the valid password

     * @return                  true if the password is correct, false if not

     */

    public static boolean validatePassword(char[] password, String correctHash)

        throws NoSuchAlgorithmException, InvalidKeySpecException

    {

        // Decode the hash into its parameters

        String[] params = correctHash.split(":");

        int iterations = Integer.parseInt(params[ITERATION_INDEX]);

        byte[] salt = fromHex(params[SALT_INDEX]);

        byte[] hash = fromHex(params[PBKDF2_INDEX]);

        // Compute the hash of the provided password, using the same salt, 

        // iteration count, and hash length

        byte[] testHash = pbkdf2(password, salt, iterations, hash.length);

        // Compare the hashes in constant time. The password is correct if

        // both hashes match.

        return slowEquals(hash, testHash);

    }

 

    /**

     * Compares two byte arrays in length-constant time. This comparison method

     * is used so that password hashes cannot be extracted from an on-line 

     * system using a timing attack and then attacked off-line.

     * 

     * @param   a       the first byte array

     * @param   b       the second byte array 

     * @return          true if both byte arrays are the same, false if not

     */

    private static boolean slowEquals(byte[] a, byte[] b)

    {

        int diff = a.length ^ b.length;

        for(int i = 0; i < a.length && i < b.length; i++)

            diff |= a[i] ^ b[i];

        return diff == 0;

    }

 

    /**

     *  Computes the PBKDF2 hash of a password.

     *

     * @param   password    the password to hash.

     * @param   salt        the salt

     * @param   iterations  the iteration count (slowness factor)

     * @param   bytes       the length of the hash to compute in bytes

     * @return              the PBDKF2 hash of the password

     */

    private static byte[] pbkdf2(char[] password, byte[] salt, int iterations, int bytes)

        throws NoSuchAlgorithmException, InvalidKeySpecException

    {

        PBEKeySpec spec = new PBEKeySpec(password, salt, iterations, bytes * 8);

        SecretKeyFactory skf = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);

        return skf.generateSecret(spec).getEncoded();

    }

 

    /**

     * Converts a string of hexadecimal characters into a byte array.

     *

     * @param   hex         the hex string

     * @return              the hex string decoded into a byte array

     */

    private static byte[] fromHex(String hex)

    {

        byte[] binary = new byte[hex.length() / 2];

        for(int i = 0; i < binary.length; i++)

        {

            binary[i] = (byte)Integer.parseInt(hex.substring(2*i, 2*i+2), 16);

        }

        return binary;

    }

 

    /**

     * Converts a byte array into a hexadecimal string.

     *

     * @param   array       the byte array to convert

     * @return              a length*2 character string encoding the byte array

     */

    private static String toHex(byte[] array)

    {

        BigInteger bi = new BigInteger(1, array);

        String hex = bi.toString(16);

        int paddingLength = (array.length * 2) - hex.length();

        if(paddingLength > 0)

            return String.format("%0" + paddingLength + "d", 0) + hex;

        else

            return hex;

    }

 

    /**

     * Tests the basic functionality of the PasswordHash class

     *

     * @param   args        ignored

     */

    public static void main(String[] args)

    {

        try

        {

            // Print out 10 hashes

            for(int i = 0; i < 10; i++)

                System.out.println(PasswordHash.createHash("p\r\nassw0Rd!"));

 

            // Test password validation

            boolean failure = false;

            System.out.println("Running tests...");

            for(int i = 0; i < 100; i++)

            {

                String password = ""+i;

                String hash = createHash(password);

                String secondHash = createHash(password);

                if(hash.equals(secondHash)) {

                    System.out.println("FAILURE: TWO HASHES ARE EQUAL!");

                    failure = true;

                }

                String wrongPassword = ""+(i+1);

                if(validatePassword(wrongPassword, hash)) {

                    System.out.println("FAILURE: WRONG PASSWORD ACCEPTED!");

                    failure = true;

                }

                if(!validatePassword(password, hash)) {

                    System.out.println("FAILURE: GOOD PASSWORD NOT ACCEPTED!");

                    failure = true;

                }

            }

            if(failure)

                System.out.println("TESTS FAILED!");

            else

                System.out.println("TESTS PASSED!");

        }

        catch(Exception ex)

        {

            System.out.println("ERROR: " + ex);

        }

    }

 

}

ASP.NET (C#)密码hash代码

/* 

 * Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).

 * Copyright (c) 2013, Taylor Hornby

 * All rights reserved.

 *

 * Redistribution and use in source and binary forms, with or without 

 * modification, are permitted provided that the following conditions are met:

 *

 * 1. Redistributions of source code must retain the above copyright notice, 

 * this list of conditions and the following disclaimer.

 *

 * 2. Redistributions in binary form must reproduce the above copyright notice,

 * this list of conditions and the following disclaimer in the documentation 

 * and/or other materials provided with the distribution.

 *

 * 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 HOLDER 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.Text;

using System.Security.Cryptography;

 

namespace PasswordHash

{

    /// 

    /// Salted password hashing with PBKDF2-SHA1.

    /// Author: havoc AT defuse.ca

    /// www: http://crackstation.net/hashing-security.htm

    /// Compatibility: .NET 3.0 and later.

    /// 

    public class PasswordHash

    {

        // The following constants may be changed without breaking existing hashes.

        public const int SALT_BYTE_SIZE = 24;

        public const int HASH_BYTE_SIZE = 24;

        public const int PBKDF2_ITERATIONS = 1000;

 

        public const int ITERATION_INDEX = 0;

        public const int SALT_INDEX = 1;

        public const int PBKDF2_INDEX = 2;

 

        /// 

        /// Creates a salted PBKDF2 hash of the password.

        /// 

        /// The password to hash.

        /// The hash of the password.

        public static string CreateHash(string password)

        {

            // Generate a random salt

            RNGCryptoServiceProvider csprng = new RNGCryptoServiceProvider();

            byte[] salt = new byte[SALT_BYTE_SIZE];

            csprng.GetBytes(salt);

 

            // Hash the password and encode the parameters

            byte[] hash = PBKDF2(password, salt, PBKDF2_ITERATIONS, HASH_BYTE_SIZE);

            return PBKDF2_ITERATIONS + ":" +

                Convert.ToBase64String(salt) + ":" +

                Convert.ToBase64String(hash);

        }

 

        /// 

        /// Validates a password given a hash of the correct one.

        /// 

        /// The password to check.

        /// A hash of the correct password.

        /// True if the password is correct. False otherwise.

        public static bool ValidatePassword(string password, string correctHash)

        {

            // Extract the parameters from the hash

            char[] delimiter = { ':' };

            string[] split = correctHash.Split(delimiter);

            int iterations = Int32.Parse(split[ITERATION_INDEX]);

            byte[] salt = Convert.FromBase64String(split[SALT_INDEX]);

            byte[] hash = Convert.FromBase64String(split[PBKDF2_INDEX]);

 

            byte[] testHash = PBKDF2(password, salt, iterations, hash.Length);

            return SlowEquals(hash, testHash);

        }

 

        /// 

        /// Compares two byte arrays in length-constant time. This comparison

        /// method is used so that password hashes cannot be extracted from

        /// on-line systems using a timing attack and then attacked off-line.

        /// 

        /// The first byte array.

        /// The second byte array.

        /// True if both byte arrays are equal. False otherwise.

        private static bool SlowEquals(byte[] a, byte[] b)

        {

            uint diff = (uint)a.Length ^ (uint)b.Length;

            for (int i = 0; i < a.Length && i < b.Length; i++)

                diff |= (uint)(a[i] ^ b[i]);

            return diff == 0;

        }

 

        /// 

        /// Computes the PBKDF2-SHA1 hash of a password.

        /// 

        /// The password to hash.

        /// The salt.

        /// The PBKDF2 iteration count.

        /// The length of the hash to generate, in bytes.

        /// A hash of the password.

        private static byte[] PBKDF2(string password, byte[] salt, int iterations, int outputBytes)

        {

            Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, salt);

            pbkdf2.IterationCount = iterations;

            return pbkdf2.GetBytes(outputBytes);

        }

    }

}

Ruby (on Rails) 密码hash代码

# Password Hashing With PBKDF2 (http://crackstation.net/hashing-security.htm).

# Copyright (c) 2013, Taylor Hornby

# All rights reserved.

# Redistribution and use in source and binary forms, with or without 

# modification, are permitted provided that the following conditions are met:

# 1. Redistributions of source code must retain the above copyright notice, 

# this list of conditions and the following disclaimer.

# 2. Redistributions in binary form must reproduce the above copyright notice,

# this list of conditions and the following disclaimer in the documentation 

# and/or other materials provided with the distribution.

# 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 HOLDER 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.

 

require 'securerandom'

require 'openssl'

require 'base64'

 

# Salted password hashing with PBKDF2-SHA1.

# Authors: @RedragonX (dicesoft.net), havoc AT defuse.ca 

# www: http://crackstation.net/hashing-security.htm

module PasswordHash

 

  # The following constants can be changed without breaking existing hashes.

  PBKDF2_ITERATIONS = 1000

  SALT_BYTE_SIZE = 24

  HASH_BYTE_SIZE = 24

 

  HASH_SECTIONS = 4

  SECTION_DELIMITER = ':'

  ITERATIONS_INDEX = 1

  SALT_INDEX = 2

  HASH_INDEX = 3

 

  # Returns a salted PBKDF2 hash of the password.

  def self.createHash( password )

    salt = SecureRandom.base64( SALT_BYTE_SIZE )

    pbkdf2 = OpenSSL::PKCS5::pbkdf2_hmac_sha1(

      password,

      salt,

      PBKDF2_ITERATIONS,

      HASH_BYTE_SIZE

    )

    return ["sha1", PBKDF2_ITERATIONS, salt, Base64.encode64( pbkdf2 )].join( SECTION_DELIMITER )

  end

 

  # Checks if a password is correct given a hash of the correct one.

  # correctHash must be a hash string generated with createHash.

  def self.validatePassword( password, correctHash )

    params = correctHash.split( SECTION_DELIMITER )

    return false if params.length != HASH_SECTIONS

 

    pbkdf2 = Base64.decode64( params[HASH_INDEX] )

    testHash = OpenSSL::PKCS5::pbkdf2_hmac_sha1(

      password,

      params[SALT_INDEX],

      params[ITERATIONS_INDEX].to_i,

      pbkdf2.length

    )

 

    return pbkdf2 == testHash

  end

 

  # Run tests to ensure the module is functioning properly.

  # Returns true if all tests succeed, false if not.

  def self.runSelfTests

    puts "Sample hashes:"

    3.times { puts createHash("password") }

 

    puts "\nRunning self tests..."

    @@allPass = true

 

    correctPassword = 'aaaaaaaaaa'

    wrongPassword = 'aaaaaaaaab'

    hash = createHash(correctPassword)

 

    assert( validatePassword( correctPassword, hash ) == true, "correct password" )

    assert( validatePassword( wrongPassword, hash ) == false, "wrong password" )

 

    h1 = hash.split( SECTION_DELIMITER )

    h2 = createHash( correctPassword ).split( SECTION_DELIMITER )

    assert( h1[HASH_INDEX] != h2[HASH_INDEX], "different hashes" )

    assert( h1[SALT_INDEX] != h2[SALT_INDEX], "different salt" )

 

    if @@allPass

      puts "*** ALL TESTS PASS ***"

    else

      puts "*** FAILURES ***"

    end

 

    return @@allPass

  end

 

  def self.assert( truth, msg )

    if truth

      puts "PASS [#{msg}]"

    else

      puts "FAIL [#{msg}]"

      @@allPass = false

    end

  end

 

end

 

PasswordHash.runSelfTests

原文出自:http://www.freebuf.com/articles/web/28527.htmlhttp://netsecurity.51cto.com/art/201403/432090.htm