一键加密

时间:2019-03-08 22:29:19

标签: php encryption base64 one-time-password

我知道这是个老话题,但是我玩了一点我的小型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);

*/

1 个答案:

答案 0 :(得分:2)

这是一次性垫的非常差的实现。它受到许多严重的问题,包括密码和实际问题。

  1. rand()不是加密安全随机数的来源。根据运行PHP的操作系统的不同,状态可能会低于32位,这使得对状态进行反向工程和不使用密钥解密文件变得非常容易。

  2. 您提出的过程(在该过程中,加密数据在与随机数据合并之前先转换为Base64)在密码学上是不必要的,并且仅能极大地膨胀加密文件的大小。 (如果我的数学正确,则“加密数据密钥”可能比原始数据大2.6到8倍。)

  3. 您用于“加密数据”和“加密数据密钥”的术语实际上是向后。实际上,“加密数据”文件包含随机数据,而“加密数据密钥”则包含从原始文件派生的数据。

  4. “加密”过程中存在加密偏差。由于原始数据和“随机密钥”都是正整数,但是它们的差存储为 signed 整数,因此可以从输出中获取有关原始数据高位的一些信息(在“加密数据密钥”文件中。)

  5. 密钥文件的极端大小使其无法安全地存储密钥。这是任何一次性填充加密方案的基本缺陷,并且是您应该使用标准分组密码的主要原因。

一次性密码垫不是大多数应用程序的合适加密方案。但是,如果您对实现一种方式一无所知,这是更合适的实现方法的基础:

$key_data = random_bytes(strlen($cleartext_data));
$encrypted_data = $cleartext_data ^ $key_data;
…
$cleartext_data = $encrypted_data ^ $key_data;

在此过程中的任何时候都无需将数据转换为Base64。