我知道这是个老话题,但是我玩了一点我的小型OTP库,我想问你一个建议。一切都很完美,但是,我敢肯定,没有管理员希望看到类似“ php_value memory_limit 500000M”的内容:D。
我不会重新发明轮子,但我确实试图找到一些用于加密数据的库,并且我对AES,mcrypt等不满意,因为如果加密数据的大小小于大小,则没有100%的安全性的关键。如果有人能向我指示正确的方向,我将非常高兴。
我的库运行良好,但看起来对于1 GB的文件,我至少需要一个服务器机房;)并且由于我正在研究商业解决方案,因此安全级别“稍微”提高了一点,我将不满意库。
非常感谢您提供所有答案。
<?php
/** OtpFile - One time pad base64 file encryption
* @author Tomas Stofik, https://www.tomasstofik.com/
* @copyright 2018 Tomas Stofik
*/
final class OtpFile
{
private static $charSet = array(
'+','/','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G',
'H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
't','u','v','w','x','y','z'
);
public static function encryptFile(
$originalFilePath,
$encryptedFilePath,
$keyFilePath)
{
if(!self::existsFile($keyFilePath) || !self::existsFile($encryptedFilePath)) {
if($originalFileData = self::existsFile($originalFilePath)) {
$originalFileBase64Data = base64_encode($originalFileData);
$originalFileBase64DataLength = strlen($originalFileBase64Data) - 1;
$originalFileBase64DataArray = str_split($originalFileBase64Data);
$encryptedData = NULL;
$encryptedDataKey = NULL;
for ($i=0; $i <= $originalFileBase64DataLength; $i++) {
$randKey = rand(0, sizeOf(self::$charSet) - 1);
$arrayKey = array_search(
$originalFileBase64DataArray[$i],
self::$charSet
);
if($randKey > $arrayKey) {
$str='-'.($randKey - $arrayKey);
} elseif($randKey < $arrayKey) {
$str = ($randKey + $arrayKey);
} else {
$str = $randKey;
}
$encryptedData .= self::$charSet[$randKey];
$encryptedDataKey .= $str.';';
}
$encryptedDataString = $encryptedData;
$encryptedDataKeyString = $encryptedDataKey;
if(!self::existsFile($keyFilePath)) {
file_put_contents($keyFilePath, $encryptedDataKeyString);
}
if(!self::existsFile($encryptedFilePath)) {
file_put_contents($encryptedFilePath, $encryptedDataString);
}
return 'OK';
} else {
return 'Source file not exists';
}
} else {
return 'Encrypted data already exists';
}
}
public static function decryptFile(
$encryptedFilePath,
$keyFilePath,
$decryptedFilePath)
{
$keyFileData = self::existsFile($keyFilePath);
$encryptedFileData = self::existsFile($encryptedFilePath);
$encryptedFileDataLength = strlen($encryptedFileData) - 1;
if($encryptedFileData && $keyFileData) {
$encryptedFileDataArray = str_split($encryptedFileData);
$keyFileDataArray = explode(';',$keyFileData);
$decryptedData = NULL;
for ($i=0; $i <= $encryptedFileDataLength; $i++) {
$positionCurrent = array_search($encryptedFileDataArray[$i], self::$charSet);
$positionEncrypted = $keyFileDataArray[$i];
if ($positionEncrypted == $positionCurrent) {
$move = $positionEncrypted;
} elseif($positionEncrypted < 0) {
$move=$positionEncrypted + $positionCurrent;
} elseif($positionEncrypted > 0) {
$move=$positionEncrypted - $positionCurrent;
} else {
$move='0';
}
$decryptedData .= self::$charSet[$move];
}
if(!self::existsFile($decryptedFilePath)) {
file_put_contents(
$decryptedFilePath,
base64_decode(
$decryptedData
)
);
return 'OK';
} else {
return 'Decrypted data already exists';
}
}
}
private static function existsFile($filePath)
{
$fileData = @file_get_contents($filePath);
if($fileData) {
return $fileData;
}
return FALSE;
}
}
/* Using
$originalFilePath = 'original.jpg';
$keyFilePath = 'Otp_Key_' . $originalFilePath;
$encryptedFilePath = 'Otp_Data_' . $originalFilePath;
$decryptedFilePath = 'Otp_Decrypted_' . $originalFilePath;
echo OtpFile::encryptFile($originalFilePath, $encryptedFilePath, $keyFilePath);
echo OtpFile::decryptFile($encryptedFilePath, $keyFilePath, $decryptedFilePath);
*/
答案 0 :(得分:2)
这是一次性垫的非常差的实现。它受到许多严重的问题,包括密码和实际问题。
rand()
不是加密安全随机数的来源。根据运行PHP的操作系统的不同,状态可能会低于32位,这使得对状态进行反向工程和不使用密钥解密文件变得非常容易。
您提出的过程(在该过程中,加密数据在与随机数据合并之前先转换为Base64)在密码学上是不必要的,并且仅能极大地膨胀加密文件的大小。 (如果我的数学正确,则“加密数据密钥”可能比原始数据大2.6到8倍。)
您用于“加密数据”和“加密数据密钥”的术语实际上是向后。实际上,“加密数据”文件包含随机数据,而“加密数据密钥”则包含从原始文件派生的数据。
“加密”过程中存在加密偏差。由于原始数据和“随机密钥”都是正整数,但是它们的差存储为 signed 整数,因此可以从输出中获取有关原始数据高位的一些信息(在“加密数据密钥”文件中。)
密钥文件的极端大小使其无法安全地存储密钥。这是任何一次性填充加密方案的基本缺陷,并且是您应该使用标准分组密码的主要原因。
一次性密码垫不是大多数应用程序的合适加密方案。但是,如果您对实现一种方式一无所知,这是更合适的实现方法的基础:
$key_data = random_bytes(strlen($cleartext_data));
$encrypted_data = $cleartext_data ^ $key_data;
…
$cleartext_data = $encrypted_data ^ $key_data;
在此过程中的任何时候都无需将数据转换为Base64。