PHP AES 암호화/암호 해독
저는 PHP에서 문자열을 en/decoding하는 예제를 찾았습니다.처음에는 아주 좋아 보이지만 작동하지 않습니다 :-(
뭐가 문제인지 아는 사람?
$Pass = "Passwort";
$Clear = "Klartext";
$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypted: ".$crypted."</br>";
$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypted: ".$newClear."</br>";
function fnEncrypt($sValue, $sSecretKey) {
return trim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sDecrypted, MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND))));
}
function fnDecrypt($sValue, $sSecretKey) {
return trim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sEncrypted), MCRYPT_MODE_ECB, mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB), MCRYPT_RAND)));
}
결과는 다음과 같습니다.
암화됨:boKRNTYYNp7AiOvY1CidqsAn9wX4ufz/D9XrpjAOPk8=
해독됨: 암호해됨:—‚(ÑÁ ^ yË~F'¸®Ó–í œð2Á_B‰Â—
기존 보안 PHP 암호화 라이브러리를 사용하십시오.
다른 사람들의 암호화 구현을 파괴한 경험이 없는 한 자신의 암호화를 작성하는 것은 일반적으로 좋지 않습니다.
여기에 있는 예제 중 암호 텍스트를 인증하는 것이 없으므로 비트 재작성 공격에 취약합니다.
PECL 확장을 설치할 수 있다면 립소듐이 훨씬 더 좋습니다.
<?php
// PECL libsodium 0.2.1 and newer
/**
* Encrypt a message
*
* @param string $message - message to encrypt
* @param string $key - encryption key
* @return string
*/
function safeEncrypt($message, $key)
{
$nonce = \Sodium\randombytes_buf(
\Sodium\CRYPTO_SECRETBOX_NONCEBYTES
);
return base64_encode(
$nonce.
\Sodium\crypto_secretbox(
$message,
$nonce,
$key
)
);
}
/**
* Decrypt a message
*
* @param string $encrypted - message encrypted with safeEncrypt()
* @param string $key - encryption key
* @return string
*/
function safeDecrypt($encrypted, $key)
{
$decoded = base64_decode($encrypted);
$nonce = mb_substr($decoded, 0, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, '8bit');
$ciphertext = mb_substr($decoded, \Sodium\CRYPTO_SECRETBOX_NONCEBYTES, null, '8bit');
return \Sodium\crypto_secretbox_open(
$ciphertext,
$nonce,
$key
);
}
그런 다음 테스트하기:
<?php
// This refers to the previous code block.
require "safeCrypto.php";
// Do this once then store it somehow:
$key = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_KEYBYTES);
$message = 'We are all living in a yellow submarine';
$ciphertext = safeEncrypt($message, $key);
$plaintext = safeDecrypt($ciphertext, $key);
var_dump($ciphertext);
var_dump($plaintext);
이 기능은 최종 사용자가 데이터를 해독하거나 안정적으로 조작할 수 없을 정도로 상당히 확실하게 데이터를 클라이언트에 전달하는 모든 상황(예: 서버 측 스토리지가 없는 세션의 암호화된 쿠키, 암호화된 URL 매개 변수 등)에서 사용할 수 있습니다.
libsodium은 교차 플랫폼이기 때문에 Java 애플릿 또는 네이티브 모바일 앱과 같은 PHP와 더 쉽게 통신할 수 있습니다.
참고: 립소듐으로 작동하는 암호화된 쿠키를 앱에 추가해야 한다면, 제 고용주인 파라곤 이니셔티브 엔터프라이즈는 이 모든 것을 해주는 할라이트라는 라이브러리를 개발하고 있습니다.
15줄의 코드로 해결할 수 있는 항목에 대해 과도한 종속성을 사용하지 않으려면 기본 제공 OpenSSL 기능을 사용합니다.대부분의 PHP 설치는 PHP에서 빠르고 호환되며 안전한 AES 암호화를 제공하는 OpenSSL과 함께 제공됩니다.모범 사례를 따르는 한 안전합니다.
다음 코드:
- CBC 모드에서 AES256 사용
- mcrypt는 PKCS#7 대신 PKCS#5를 사용하므로 다른 AES 구현과 호환되지만 mcrypt는 호환되지 않습니다.
- SHA256을 사용하여 제공된 암호에서 키를 생성합니다.
- 무결성 검사를 위해 암호화된 데이터의 hmac 해시를 생성합니다.
- 각 메시지에 대해 랜덤 IV를 생성
- 암호문 앞에 IV(16바이트)와 해시(32바이트)를 추가합니다.
- 꽤 안전해야 합니다.
IV는 공개 정보이므로 각 메시지에 대해 임의여야 합니다.해시를 사용하면 데이터가 손상되지 않습니다.
function encrypt($plaintext, $password) {
$method = "AES-256-CBC";
$key = hash('sha256', $password, true);
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, $method, $key, OPENSSL_RAW_DATA, $iv);
$hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);
return $iv . $hash . $ciphertext;
}
function decrypt($ivHashCiphertext, $password) {
$method = "AES-256-CBC";
$iv = substr($ivHashCiphertext, 0, 16);
$hash = substr($ivHashCiphertext, 16, 32);
$ciphertext = substr($ivHashCiphertext, 48);
$key = hash('sha256', $password, true);
if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true), $hash)) return null;
return openssl_decrypt($ciphertext, $method, $key, OPENSSL_RAW_DATA, $iv);
}
용도:
$encrypted = encrypt('Plaintext string.', 'password'); // this yields a binary string
echo decrypt($encrypted, 'password');
// decrypt($encrypted, 'wrong password') === null
편집: 사용하도록 업데이트됨hash_equals
그리고 해시에 IV를 추가했습니다.
$sDecrypted
그리고.$sEncrypted
코드에 정의되지 않았습니다.작동하지만 안전하지 않은 솔루션을 확인합니다.
멈춰요!
이 예제는 안전하지 않습니다!사용하지 마십시오!
$Pass = "Passwort";
$Clear = "Klartext";
$crypted = fnEncrypt($Clear, $Pass);
echo "Encrypred: ".$crypted."</br>";
$newClear = fnDecrypt($crypted, $Pass);
echo "Decrypred: ".$newClear."</br>";
function fnEncrypt($sValue, $sSecretKey)
{
return rtrim(
base64_encode(
mcrypt_encrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey, $sValue,
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND)
)
), "\0"
);
}
function fnDecrypt($sValue, $sSecretKey)
{
return rtrim(
mcrypt_decrypt(
MCRYPT_RIJNDAEL_256,
$sSecretKey,
base64_decode($sValue),
MCRYPT_MODE_ECB,
mcrypt_create_iv(
mcrypt_get_iv_size(
MCRYPT_RIJNDAEL_256,
MCRYPT_MODE_ECB
),
MCRYPT_RAND
)
), "\0"
);
}
그러나 이 코드에는 특히 ECB(암호화 모드가 아니라 암호화 모드만 정의할 수 있는 구성 요소)의 사용과 같은 불안정한 다른 문제가 있습니다.최악의 문제를 신속하게 해결하려면 Fab Sa의 답변을 참조하고 이를 올바르게 수행하는 방법에 대한 Scott의 답변을 참조하십시오.
내용은 참로MCRYPT_MODE_ECB
IV(초기화 벡터)를 사용하지 않습니다.ECB 모드는 메시지를 블록으로 나누고 각 블록은 별도로 암호화됩니다.정말 추천하지 않습니다.
CBC 모드는 IV를 사용하여 각 메시지를 고유하게 만듭니다.CBC가 권장되며 ECB 대신 사용되어야 합니다.
예:
<?php
$password = "myPassword_!";
$messageClear = "Secret message";
// 32 byte binary blob
$aes256Key = hash("SHA256", $password, true);
// for good entropy (for MCRYPT_RAND)
srand((double) microtime() * 1000000);
// generate random iv
$iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC), MCRYPT_RAND);
$crypted = fnEncrypt($messageClear, $aes256Key);
$newClear = fnDecrypt($crypted, $aes256Key);
echo
"IV: <code>".$iv."</code><br/>".
"Encrypred: <code>".$crypted."</code><br/>".
"Decrypred: <code>".$newClear."</code><br/>";
function fnEncrypt($sValue, $sSecretKey) {
global $iv;
return rtrim(base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, $sValue, MCRYPT_MODE_CBC, $iv)), "\0\3");
}
function fnDecrypt($sValue, $sSecretKey) {
global $iv;
return rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $sSecretKey, base64_decode($sValue), MCRYPT_MODE_CBC, $iv), "\0\3");
}
각 메시지를 디코딩하려면 IV를 저장해야 합니다(IV는 비밀이 아닙니다).각 메시지에는 고유한 IV가 있기 때문에 각 메시지는 고유합니다.
이것은 다음과 같은 효과적인 솔루션입니다.AES encryption
를사하구현을 사용하여 openssl
암호 블록 체인 모드(CBC-Mode)를 사용합니다. 서따라와 나란히, 에옆▁thusdata
그리고.key
를 지정할 수 .iv
그리고.block size
<?php
class AESEncryption {
protected $key;
protected $data;
protected $method;
protected $iv;
/**
* Available OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING
*
* @var type $options
*/
protected $options = 0;
/**
*
* @param type $data
* @param type $key
* @param type $iv
* @param type $blockSize
* @param type $mode
*/
public function __construct($data = null, $key = null, $iv = null, $blockSize = null, $mode = 'CBC') {
$this->setData($data);
$this->setKey($key);
$this->setInitializationVector($iv);
$this->setMethod($blockSize, $mode);
}
/**
*
* @param type $data
*/
public function setData($data) {
$this->data = $data;
}
/**
*
* @param type $key
*/
public function setKey($key) {
$this->key = $key;
}
/**
* CBC 128 192 256
CBC-HMAC-SHA1 128 256
CBC-HMAC-SHA256 128 256
CFB 128 192 256
CFB1 128 192 256
CFB8 128 192 256
CTR 128 192 256
ECB 128 192 256
OFB 128 192 256
XTS 128 256
* @param type $blockSize
* @param type $mode
*/
public function setMethod($blockSize, $mode = 'CBC') {
if($blockSize==192 && in_array('', array('CBC-HMAC-SHA1','CBC-HMAC-SHA256','XTS'))){
$this->method=null;
throw new Exception('Invalid block size and mode combination!');
}
$this->method = 'AES-' . $blockSize . '-' . $mode;
}
/**
*
* @param type $data
*/
public function setInitializationVector($iv) {
$this->iv = $iv;
}
/**
*
* @return boolean
*/
public function validateParams() {
if ($this->data != null &&
$this->method != null ) {
return true;
} else {
return FALSE;
}
}
//it must be the same when you encrypt and decrypt
protected function getIV() {
return $this->iv;
}
/**
* @return type
* @throws Exception
*/
public function encrypt() {
if ($this->validateParams()) {
return trim(openssl_encrypt($this->data, $this->method, $this->key, $this->options,$this->getIV()));
} else {
throw new Exception('Invalid params!');
}
}
/**
*
* @return type
* @throws Exception
*/
public function decrypt() {
if ($this->validateParams()) {
$ret=openssl_decrypt($this->data, $this->method, $this->key, $this->options,$this->getIV());
return trim($ret);
} else {
throw new Exception('Invalid params!');
}
}
}
샘플 사용량:
<?php
$data = json_encode(['first_name'=>'Dunsin','last_name'=>'Olubobokun','country'=>'Nigeria']);
$inputKey = "W92ZB837943A711B98D35E799DFE3Z18";
$iv = "tuqZQhKP48e8Piuc";
$blockSize = 256;
$aes = new AESEncryption($data, $inputKey, $iv, $blockSize);
$enc = $aes->encrypt();
$aes->setData($enc);
$dec=$aes->decrypt();
echo "After encryption: ".$enc."<br/>";
echo "After decryption: ".$dec."<br/>";
다음은 AES256 CBC를 사용하여 PHP로 문자열을 암호화/암호 해독하는 간단한 방법입니다.
function encryptString($plaintext, $password, $encoding = null) {
$iv = openssl_random_pseudo_bytes(16);
$ciphertext = openssl_encrypt($plaintext, "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, $iv);
$hmac = hash_hmac('sha256', $ciphertext.$iv, hash('sha256', $password, true), true);
return $encoding == "hex" ? bin2hex($iv.$hmac.$ciphertext) : ($encoding == "base64" ? base64_encode($iv.$hmac.$ciphertext) : $iv.$hmac.$ciphertext);
}
function decryptString($ciphertext, $password, $encoding = null) {
$ciphertext = $encoding == "hex" ? hex2bin($ciphertext) : ($encoding == "base64" ? base64_decode($ciphertext) : $ciphertext);
if (!hash_equals(hash_hmac('sha256', substr($ciphertext, 48).substr($ciphertext, 0, 16), hash('sha256', $password, true), true), substr($ciphertext, 16, 32))) return null;
return openssl_decrypt(substr($ciphertext, 48), "AES-256-CBC", hash('sha256', $password, true), OPENSSL_RAW_DATA, substr($ciphertext, 0, 16));
}
용도:
$enc = encryptString("mysecretText", "myPassword");
$dec = decryptString($enc, "myPassword");
편집: AES256 GCM 및 PBKDF2를 키 파생으로 사용하는 새로운 버전의 기능으로, 보다 안전합니다.
function str_encryptaesgcm($plaintext, $password, $encoding = null) {
if ($plaintext != null && $password != null) {
$keysalt = openssl_random_pseudo_bytes(16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length("aes-256-gcm"));
$tag = "";
$encryptedstring = openssl_encrypt($plaintext, "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag, "", 16);
return $encoding == "hex" ? bin2hex($keysalt.$iv.$encryptedstring.$tag) : ($encoding == "base64" ? base64_encode($keysalt.$iv.$encryptedstring.$tag) : $keysalt.$iv.$encryptedstring.$tag);
}
}
function str_decryptaesgcm($encryptedstring, $password, $encoding = null) {
if ($encryptedstring != null && $password != null) {
$encryptedstring = $encoding == "hex" ? hex2bin($encryptedstring) : ($encoding == "base64" ? base64_decode($encryptedstring) : $encryptedstring);
$keysalt = substr($encryptedstring, 0, 16);
$key = hash_pbkdf2("sha512", $password, $keysalt, 20000, 32, true);
$ivlength = openssl_cipher_iv_length("aes-256-gcm");
$iv = substr($encryptedstring, 16, $ivlength);
$tag = substr($encryptedstring, -16);
return openssl_decrypt(substr($encryptedstring, 16 + $ivlength, -16), "aes-256-gcm", $key, OPENSSL_RAW_DATA, $iv, $tag);
}
}
용도:
$enc = str_encryptaesgcm("mysecretText", "myPassword", "base64"); // return a base64 encrypted string, you can also choose hex or null as encoding.
$dec = str_decryptaesgcm($enc, "myPassword", "base64");
AES 암호화와 관련하여 주의해야 할 몇 가지 중요한 사항:
- 일반 텍스트를 암호화 키로 사용하지 마십시오.항상 일반 텍스트 키를 해시한 다음 암호화에 사용합니다.
- 암호화 및 암호 해독에는 항상 랜덤 IV(초기화 벡터)를 사용합니다.진정한 무작위화가 중요합니다.
- 위에서 언급한 것처럼 ecb 모드를 사용하지 말고 사용합니다.
CBC
대신.
사용하는 MCRYPT_RIJNDAEL_128을 사용해 .rtrim($output, "\0\3")
문자열의 길이가 16보다 작으면 암호 해독 기능은 16자 길이의 문자열을 반환하고 끝에 03을 추가합니다.
예를 들어 다음을 시도하여 쉽게 확인할 수 있습니다.
$string = "TheString";
$decrypted_string = decrypt_function($stirng, $key);
echo bin2hex($decrypted_string)."=".bin2hex("TheString");
다음은 블레이드가 작성한 코드를 기반으로 한 개선된 버전입니다.
- 댓글 달기
- 예외를 사용하여 암호가 유출되지 않도록 던지기 전에 인수 덮어쓰기
- openssl 및 hmac 함수에서 반환 값 확인
코드:
class Crypto
{
/**
* Encrypt data using OpenSSL (AES-256-CBC)
* @param string $plaindata Data to be encrypted
* @param string $cryptokey key for encryption (with 256 bit of entropy)
* @param string $hashkey key for hashing (with 256 bit of entropy)
* @return string IV+Hash+Encrypted as raw binary string. The first 16
* bytes is IV, next 32 bytes is HMAC-SHA256 and the rest is
* $plaindata as encrypted.
* @throws Exception on internal error
*
* Based on code from: https://stackoverflow.com/a/46872528
*/
public static function encrypt($plaindata, $cryptokey, $hashkey)
{
$method = "AES-256-CBC";
$key = hash('sha256', $cryptokey, true);
$iv = openssl_random_pseudo_bytes(16);
$cipherdata = openssl_encrypt($plaindata, $method, $key, OPENSSL_RAW_DATA, $iv);
if ($cipherdata === false)
{
$cryptokey = "**REMOVED**";
$hashkey = "**REMOVED**";
throw new \Exception("Internal error: openssl_encrypt() failed:".openssl_error_string());
}
$hash = hash_hmac('sha256', $cipherdata.$iv, $hashkey, true);
if ($hash === false)
{
$cryptokey = "**REMOVED**";
$hashkey = "**REMOVED**";
throw new \Exception("Internal error: hash_hmac() failed");
}
return $iv.$hash.$cipherdata;
}
/**
* Decrypt data using OpenSSL (AES-256-CBC)
* @param string $encrypteddata IV+Hash+Encrypted as raw binary string
* where the first 16 bytes is IV, next 32 bytes is HMAC-SHA256 and
* the rest is encrypted payload.
* @param string $cryptokey key for decryption (with 256 bit of entropy)
* @param string $hashkey key for hashing (with 256 bit of entropy)
* @return string Decrypted data
* @throws Exception on internal error
*
* Based on code from: https://stackoverflow.com/a/46872528
*/
public static function decrypt($encrypteddata, $cryptokey, $hashkey)
{
$method = "AES-256-CBC";
$key = hash('sha256', $cryptokey, true);
$iv = substr($encrypteddata, 0, 16);
$hash = substr($encrypteddata, 16, 32);
$cipherdata = substr($encrypteddata, 48);
if (!hash_equals(hash_hmac('sha256', $cipherdata.$iv, $hashkey, true), $hash))
{
$cryptokey = "**REMOVED**";
$hashkey = "**REMOVED**";
throw new \Exception("Internal error: Hash verification failed");
}
$plaindata = openssl_decrypt($cipherdata, $method, $key, OPENSSL_RAW_DATA, $iv);
if ($plaindata === false)
{
$cryptokey = "**REMOVED**";
$hashkey = "**REMOVED**";
throw new \Exception("Internal error: openssl_decrypt() failed:".openssl_error_string());
}
return $plaindata;
}
}
올바른 암호화 및 해시 키를 사용할 수 없지만 사용자가 입력한 암호를 유일한 암호로 사용해야 하는 경우 다음과 같은 작업을 수행할 수 있습니다.
/**
* @param string $password user entered password as the only source of
* entropy to generate encryption key and hash key.
* @return array($encryption_key, $hash_key) - note that PBKDF2 algorithm
* has been configured to take around 1-2 seconds per conversion
* from password to keys on a normal CPU to prevent brute force attacks.
*/
public static function generate_encryptionkey_hashkey_from_password($password)
{
$hash = hash_pbkdf2("sha512", "$password", "salt$password", 1500000);
return str_split($hash, 64);
}
만약 당신이 PHP >= 7.2를 사용하고 있다면, 암호화를 위해 내장된 나트륨 코어 확장을 사용하는 것을 고려해보세요.
자세한 내용은 여기를 참조하십시오.http://php.net/manual/en/intro.sodium.php
.
언급URL : https://stackoverflow.com/questions/3422759/php-aes-encrypt-decrypt
'programing' 카테고리의 다른 글
Github에 푸시할 때 Git 푸시가 중단됩니까? (0) | 2023.08.26 |
---|---|
도커 파일 CMD 내의 변수를 사용하려면 어떻게 해야 합니까? (0) | 2023.08.26 |
Firefox는 url에서 인코딩된 매개 변수를 자동으로 디코딩하며 IE에서 발생하지 않습니다. (0) | 2023.08.26 |
용어가 나타날 경우 용어가 나타나는 횟수를 카운트하는 SQL 쿼리 (0) | 2023.08.26 |
C#을 사용하여 사용자 지정 XML을 열린 Excel 2007 워크북에 추가하려면 어떻게 해야 합니까? (0) | 2023.08.26 |