發佈日期:
分類:
如何在PHP中‧利用Sodium函數對大容量檔案作出加密/解密動作
01. 早幾天終於解決了PHP函數Sodium的問題,今天就來到實戰時候,要如何對大容量檔案作出加密/解密動作。
02. PHP函數Sodium加密比較簡單,只要用『sodium_crypto_secretbox_keygen()』及『random_bytes()』產生的隨機數值『$keygen』及加密安全的隨機位元組(cryptographically secure random bytes)『$nonce』,便可以用『sodium_crypto_secretbox』函數對數據進行加密。
<?php $keygen = sodium_crypto_secretbox_keygen(); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $ciphertext = sodium_crypto_secretbox('Your message', $nonce, $keygen); $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $keygen); if ($plaintext === false) { throw new Exception("Bad ciphertext"); } echo $plaintext; ?>
03. 對於大檔案的做法,依然是將要原來的檔案以『FILE_ENCRYPTION_BLOCKS』設定來斬細,利用『sodium_crypto_secretbox』函數對數據進行加密。而加密時用到的『$keygen』及『$nonce』,會再用『OpenSSL』函數,以用戶設定的密碼再作一次加密,最後就是將所有資料(除用戶密碼),寫入加密檔案。以下例子,『$source』為要加密檔案,『$dest』為加密後檔案,『$key』為用戶用於OpenSSL加密時密碼。
<?php define('FILE_ENCRYPTION_BLOCKS', 10000); function encrypt($source, $dest, $key) { $cipher = 'aes-256-gcm'; $ivLenght = openssl_cipher_iv_length($cipher); $fpSource = fopen($source, 'rb'); $fpDest = fopen($dest, 'w'); while (! feof($fpSource)) { $plaintext = fread($fpSource, FILE_ENCRYPTION_BLOCKS); $keygen = sodium_crypto_secretbox_keygen(); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $iv = openssl_random_pseudo_bytes($ivLenght); $cipherkey = openssl_encrypt($keygen.$nonce, $cipher, $key, $options=0, $iv, $tag); $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $keygen); fwrite($fpDest, $cipherkey.$iv.$tag.$ciphertext); } fclose($fpSource); fclose($fpDest); } ?>
04. 解密方面,『$source』為已加密檔案,『$dest』為解密後檔案,『$key』為用戶加密時曾輸入的密碼。首先需要知道OpenSSL加密『$nonce』及『$keygen』時所在位置、長度及排列方式,再讀取加密內容,以『sodium_crypto_secretbox_open』函數作出解密。以上面『FILE_ENCRYPTION_BLOCKS』設定為10000為例,其加密後長度就是10016,所以『FILE_DECRYPTION_BLOCKS』設定便是10016。而其他數值長度,可參考下列例子。
<?php define('FILE_DECRYPTION_BLOCKS', 10016); define('CIPHERKEY_LENGTH', 76); define('IV_LENGTH', 12); define('TAG_LENGTH', 16); define('KEYEGN_LENGTH', 32); define('NONCE_LENGTH', 24); function decrypt($source, $dest, $key) { $cipher = 'aes-256-gcm'; $ivLenght = openssl_cipher_iv_length($cipher); $fpSource = fopen($source, 'rb'); $fpDest = fopen($dest, 'w'); while (! feof($fpSource)) { $cipherkey = fread($fpSource, CIPHERKEY_LENGTH); $iv = fread($fpSource, IV_LENGTH); $tag = fread($fpSource, TAG_LENGTH); $ciphertext = fread($fpSource, FILE_DECRYPTION_BLOCKS); $prefix = openssl_decrypt($cipherkey, $cipher, $key, $options=0, $iv, $tag); $keygen = substr($prefix, 0, KEYEGN_LENGTH); $nonce = substr($prefix, KEYEGN_LENGTH, NONCE_LENGTH); $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $keygen); fwrite($fpDest, $plaintext); } fclose($fpSource); fclose($fpDest); } ?>
05. 最終結果。
<?php define('FILE_ENCRYPTION_BLOCKS', 10000); define('FILE_DECRYPTION_BLOCKS', 10016); define('CIPHERKEY_LENGTH', 76); define('IV_LENGTH', 12); define('TAG_LENGTH', 16); define('KEYEGN_LENGTH', 32); define('NONCE_LENGTH', 24); function encrypt($source, $dest, $key) { $cipher = 'aes-256-gcm'; $ivLenght = openssl_cipher_iv_length($cipher); $fpSource = fopen($source, 'rb'); $fpDest = fopen($dest, 'w'); while (! feof($fpSource)) { $plaintext = fread($fpSource, FILE_ENCRYPTION_BLOCKS); $keygen = sodium_crypto_secretbox_keygen(); $nonce = random_bytes(SODIUM_CRYPTO_SECRETBOX_NONCEBYTES); $iv = openssl_random_pseudo_bytes($ivLenght); $cipherkey = openssl_encrypt($keygen.$nonce, $cipher, $key, $options=0, $iv, $tag); $ciphertext = sodium_crypto_secretbox($plaintext, $nonce, $keygen); fwrite($fpDest, $cipherkey.$iv.$tag.$ciphertext); } fclose($fpSource); fclose($fpDest); } function decrypt($source, $dest, $key) { $cipher = 'aes-256-gcm'; $ivLenght = openssl_cipher_iv_length($cipher); $fpSource = fopen($source, 'rb'); $fpDest = fopen($dest, 'w'); while (! feof($fpSource)) { $cipherkey = fread($fpSource, CIPHERKEY_LENGTH); $iv = fread($fpSource, IV_LENGTH); $tag = fread($fpSource, TAG_LENGTH); $ciphertext = fread($fpSource, FILE_DECRYPTION_BLOCKS); $prefix = openssl_decrypt($cipherkey, $cipher, $key, $options=0, $iv, $tag); $keygen = substr($prefix, 0, KEYEGN_LENGTH); $nonce = substr($prefix, KEYEGN_LENGTH, NONCE_LENGTH); $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $keygen); fwrite($fpDest, $plaintext); } fclose($fpSource); fclose($fpDest); } $video = 'file.mp4'; $password = 'password'; encrypt($video, 'encrypted.tmp', $password); decrypt('encrypted.tmp', 'decrypt.mp4', $password); ?>
發佈留言