我有人为我输入此代码,但我不知道如何将其存储在MySQL数据库中并能够解密它。我最初错误地做的是尝试存储密码,然后将其与不同的实例进行比较。当我意识到自己在做什么时,我对错误感到震惊。我考虑过存储实例,但这会带来安全风险,因为任何有权访问数据库的人都可以调用$ instance-> decodePassword($ password)。我想这个脚本并不是真的用来与MySQL一起使用,我付钱给脚本的人没有回复。我该怎么办?任何帮助将不胜感激。
<?php
/**
* Password protection class.
*/
class PasswordCrypt {
/* Number of rounds to use for PBKDF2 for new setups.
* May be tweaked up or down because the number of rounds is encoded with the data
*/
const DEFAULT_PBKDF2_ROUNDS = 10000;
/* Definition:
* Output data = 1 || IV || HMAC_SHA256(K, PROTECT_AES_256_CBC(K, IV, data)) || AES_256_CBC(K, IV, data)
*/
const PROTECT_AES_256_CBC_HMACSHA1 = 1;
/* Definition:
* Uses the PBKDF2 derivation algorithm. The key is encrypted with a protection algorithm..
* Output data = 1 || SALT || Rounds (4-byte-integer) || 'Protected' key)
*/
const TOKEN_PBKDF2_SHA256_AES_256_CBC = 1;
/* Token which is effectively the key encrypted with the password */
private $token;
/* Key used to perform encryption/decryption */
private $key;
/* Private constructor that takes the token and key.
*
* The token is kept around for storage purposes whereas the key is the main
* important piece.
*/
protected function __construct($token, $key) {
$this->token = $token;
$this->key = $key;
}
/**
* Create a new instance using a given password.
*
* @param $password text string from user
*/
public static function createWithNewPassword($password) {
/* Generate the new random key to use */
$key = PasswordCrypt::generateRandomKey();
/* Generate the token to store */
$token = PasswordCrypt::encodeProtectedToken($password, $key);
/* Setup the instance */
return new PasswordCrypt($token, $key);
}
/**
* Create a new instance given input token and password.
*
* @param $password password associated with the given $token
* @param $token token for the given user
*
* @return null if decoding fails, else value instance that can decrypt/encrypt passwords/etc.
*/
public static function createFromToken($password, $token) {
$key = PasswordCrypt::decodeProtectedToken($password, $token);
if (!$key) {
return null;
}
return new PasswordCrypt($token, $key);
}
/**
* Create a new instance using session data.
*/
public static function createWithSession() {
$token = $_SESSION['__pc_token'];
$sessionKey = $_SESSION['__pc_key'];
$cookieData = $_COOKIE['__pc_cookie'];
if (!$token || !$sessionKey || !$cookieData) {
return null;
}
$cookieData = PasswordCrypt::base64url_decode($cookieData);
$key = PasswordCrypt::decryptData($sessionKey, $cookieData);
return new PasswordCrypt($token, $key);
}
/**
* Utility function to check if the PHP session state has what is needed for
* session-based construction.
*/
public static function hasSessionData() {
return $_SESSION['__pc_token']
&& $_SESSION['__pc_key']
&& $_COOKIE['__pc_cookie'];
}
/**
* Clear the session state from PHP to prevent re-use (ex: useful for logout)
*/
public static function clearSessionData() {
unset($_SESSION['__pc_token']);
unset($_SESSION['__pc_key']);
PasswordCrypt::setSessionCookie('__pc_cookie', '');
}
/* Utility function to store data in the session and set a cookie to decrypt */
public function storeSessionKey() {
$sessionKey = PasswordCrypt::generateRandomKey();
$cookieData = PasswordCrypt::encryptData($sessionKey, $this->key);
$_SESSION['__pc_token'] = $this->token;
$_SESSION['__pc_key'] = $sessionKey;
PasswordCrypt::setSessionCookie('__pc_cookie', PasswordCrypt::base64url_encode($cookieData));
}
/**
* Utility method to set a session cookie - tweak as appropriate for proper cookie settings for the site.
*/
private static function setSessionCookie($name, $value) {
/* Store for session only and assume total path... */
$secure = false;
if ($_SERVER["HTTPS"]) {
$secure = TRUE;
}
setcookie($name, $value, 0, "", "", $secure, TRUE);
}
/**
* Method to decode passwords previously encoded.
*
* @param $ciphertext raw binary string representing the encrypted password.
*
* @return decoded password string as passed in to encodePassword.
* null may be returned if the data is corrupt, uses a format not supported by
* this version, or does not match the key.
*/
public function decodePassword($ciphertext) {
/* Changed first parameter in decryptData from $this->key to $InsideOutsideKey */
return PasswordCrypt::decryptData($this->key,$ciphertext);
}
/**
* Method to encode passwords to later be decoded using decodePassword.
*
* @param $password text representing the data to protect - may be raw binary.
*
* @return encrypted password data in raw binary form. null will be returned if
* something is critically wrong with the setup - not likely.
*/
public function encodePassword($password) {
return PasswordCrypt::encryptData($this->key, $password);
}
/**
* Method to create a new token protected with a new password.
* Useful for when the user wants to change their password.
*
* @param $password new password to encode against.
*
* @return token to later be decoded using the provided password.
*/
public function encodeToken($password) {
return PasswordCrypt::encodeProtectedToken($password, $this->key);
}
/**
* Method to get the current token as protected by the initial password input.
*
* @return token to later be decoded using the initial password at creation.
*/
public function getToken() {
return $this->token;
}
/**
*Method to get a random key used by AES-256
*/
public function getKey() {
return $this->key;
}
/**
* Utility method to generate a random key as required by AES-256
*/
private static function generateRandomKey() {
/* Since using AES 256 CBC - we need 256 bits */
return openssl_random_pseudo_bytes(256 / 8);
}
/* Generate the protected token data */
private static function encodeProtectedToken($password, $key) {
$implementation = PasswordCrypt::TOKEN_PBKDF2_SHA256_AES_256_CBC;
switch ($implementation) {
case PasswordCrypt::TOKEN_PBKDF2_SHA256_AES_256_CBC:
/* Generate fresh salt to prevent rainbow table attacks */
$salt = openssl_random_pseudo_bytes(16);
/* Use the defaults configured */
$iterations = PasswordCrypt::DEFAULT_PBKDF2_ROUNDS;
/* Derive the key using PBKDF2 with SHA256 per the token key generation scheme */
$passwordDerivedKey = PasswordCrypt::pbkdf2("SHA256", $password, $salt, $iterations, 256 / 8, true);
/* Encrypt the 'real' key data using the password-based key */
$data = PasswordCrypt::encryptData($passwordDerivedKey, $key);
return pack("Ca16La*", $implementation, $salt, $iterations, $data);
default:
return null;
}
}
/* Decode the protected token data */
private static function decodeProtectedToken($password, $tokenData) {
list(,$implementation) = unpack("C", $tokenData);
switch ($implementation) {
case PasswordCrypt::TOKEN_PBKDF2_SHA256_AES_256_CBC:
/* Extract the salt encoded in the protection data */
$salt = substr($tokenData, 1, 16);
/* Extract the number of iterations used for this data */
list(,$iterations) = unpack("L", substr($tokenData, 1 + 16, 4));
/* Extract the encrypted key data */
$encrypted = substr($tokenData, 1 + 16 + 4);
/* Derive the key using PBKDF2 with SHA256 per the token key generation scheme */
$passwordDerivedKey = PasswordCrypt::pbkdf2("SHA256", $password, $salt, $iterations, 256 / 8, true);
/* Decrypt the key data using the derived key */
return PasswordCrypt::decryptData($passwordDerivedKey, $encrypted);
default:
return null;
}
}
/* Protect data using a given input key */
private static function encryptData($key, $plaintext) {
$implementation = PasswordCrypt::PROTECT_AES_256_CBC_HMACSHA1;
switch ($implementation) {
case PasswordCrypt::PROTECT_AES_256_CBC_HMACSHA1:
/* Some PHP versions to not have OPENSSL_RAW_DATA option and instead use a boolean for raw, so handle it */
$options = defined("OPENSSL_RAW_DATA") ? OPENSSL_RAW_DATA : true;
/* 16-byte IV */
$iv = openssl_random_pseudo_bytes(16);
/* Encrypt the data first */
$encrypted = openssl_encrypt($plaintext, "aes-256-cbc", bin2hex($key), $options, $iv);
/* Generate an HMAC over the data to make sure final output matches */
$hmac = hash_hmac("sha1", $encrypted, $key, TRUE);
/* HMAC should be 160 bits long - 20 bytes */
return pack("Ca16a20A*", $implementation, $iv, $hmac, $encrypted);
default:
return null;
}
}
/* Decoded protected data with a given key */
private static function decryptData($key, $ciphertext) {
list(,$implementation) = unpack("C", $ciphertext);
switch ($implementation) {
case PasswordCrypt::PROTECT_AES_256_CBC_HMACSHA1:
$iv = substr($ciphertext, 1, 16);
$included_hmac = substr($ciphertext, 1 + 16, 20);
$encrypted = substr($ciphertext, 1 + 16 + 20);
/* Verify the HMAC */
$hmac = hash_hmac("sha1", $encrypted, $key, TRUE);
if ($hmac != $included_hmac) {
/* HMAC did not match, bad key or corrupt data */
return null;
}
/* Some PHP versions to not have OPENSSL_RAW_DATA option and instead use a boolean for raw, so handle it */
$options = defined("OPENSSL_RAW_DATA") ? OPENSSL_RAW_DATA : true;
$decrypted = openssl_decrypt($encrypted, "aes-256-cbc", bin2hex($key), $options, $iv);
return $decrypted;
default:
return null;
}
}
/*
* 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
*/
private static 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 we have a version of PHP with the native hash_pbkdf2 - use that! */
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));
}
}
private static function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private static function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
}
?>
答案 0 :(得分:0)
添加这个对我有用。现在我可以从数据库中检索的令牌创建一个新实例。我很惊讶你们没有人想到这一点。
/****
Beginning of added mysqli method
*****/
public static function createWithNewMysqli($fetched_token,$fetched_key)
{
return new PasswordCrypt($fetched_token, $fetched_key);
}
/****
End of added mysqli method
****/