CryptoJs - เข้ารหัส / ถอดรหัสโดย PHP และ Javascript - สตริงที่เข้ารหัสเอาต์พุตแบบง่าย

Sep 23 2020

ฉันต้องการเข้ารหัสและถอดรหัสสตริงใน Php และใน Javascript และดูบนเว็บวิธีที่ดีที่สุดและปลอดภัยที่สุดน่าจะเป็น CryptoJs

โพสต์นี้ไม่ซ้ำกับการเข้ารหัสด้วย PHP, ถอดรหัสด้วย Javascript (cryptojs)เนื่องจากสตริงเอาต์พุตไม่ง่าย

นี่คือรหัสของฉัน แต่รหัสถอดรหัส Js ไม่ทำงาน มันผิดอะไร?

<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js"></script>
    </head>
    <body>
        <p>--- PHP ------------------</p>
    
        <?php
            function myCrypt($value, $passphrase, $iv){ $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $passphrase, true, $iv); return base64_encode($encrypted_data);
            }
            
            function myDecrypt($value, $passphrase, $iv){ $value = base64_decode($value); $data = openssl_decrypt($value, 'aes-256-cbc', $passphrase, true, $iv); return $data;
            }
            

            $valTxt="MyText"; $pswd="MyPassword";
            $vector="1234567890123412"; $encrypted = myCrypt($valTxt, $pswd, $vector); $decrypted = myDecrypt($encrypted, $pswd, $vector); echo "<p>Text to crypt --------> ".$valTxt." </p>";
            echo "<p>Password: ".$pswd." </p>"; echo "<p>Vector: ".$vector." </p>";
            echo "<p>TextEncrypt: ".$encrypted." </p>"; echo "<p>TextDecrypt: ".$decrypted." </p>";
        ?>
        
        <br><br><br>
        <p>--- Javascript ------------------</p>
        <p>JS-DataEncrypt: --------- <span id="DataEncrypt"></span></p>
        <p>JS-DataPassword: -------- <span id="DataPassword"></span></p>
        <p>JS-DataVector: ---------- <span id="DataVector"></span></p>
        <p>JS-TextDecrypted: ------- <span id="result"></span></p>
        
        <script>
            var DataEncrypt='<?php echo $encrypted;?>'; var DataPassword='<?php echo $pswd;?>';
            var DataVector='<?php echo $vector;?>';
            
            //var key = CryptoJS.enc.Hex.parse(DataPassword);
            //var iv = CryptoJS.enc.Hex.parse(DataVector);
            //var decrypted = CryptoJS.AES.decrypt(DataEncrypt, key, { iv: iv });
            
            var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataPassword, { iv: DataVector });
            
            decrypted= CryptoJS.enc.Utf8.stringify(decrypted)
            
            
            
            document.getElementById("DataEncrypt").innerHTML = DataEncrypt;
            document.getElementById("DataPassword").innerHTML = DataPassword;
            document.getElementById("DataVector").innerHTML = DataVector;
            document.getElementById("result").innerHTML = decrypted;
        </script>
    
    
        
    </body>
</html>

ปล. จะดีกว่าถ้าสตริงเอาต์พุต (เข้ารหัส $) จะเป็น 16 หลัก A-Za-z0-9 ... เป็นไปได้ไหมที่จะเปลี่ยน 'aes-256-cbc'

คำตอบ

3 Topaco Sep 23 2020 at 03:21

ในโค้ด PHP ควรพิจารณาสิ่งต่อไปนี้:

  • $passphraseไม่ได้แสดงถึงข้อความรหัสผ่าน แต่เป็นกุญแจสำคัญ คีย์นี้จะต้องเป็น 32 aes-256-cbcไบต์ในขนาดสำหรับทางเลือก หากสั้นเกินไปจะเต็มไปด้วยค่า 0 หากยาวเกินไปค่านั้นจะถูกตัดทอน นี่เป็นแหล่งที่มาของข้อผิดพลาดทั่วไปดังนั้นควรใช้คีย์ขนาด 32 ไบต์ หากคุณต้องการใช้รหัสผ่านคุณต้องใช้ KDF (เช่น PBKDF2)
  • ในแฟล็กพารามิเตอร์ที่สี่ถูกตั้งค่าและไม่มีนิพจน์บูลีน (เหมือนtrue) หากควรส่งคืนข้อมูลในรูปแบบไบนารีOPENSSL_RAW_DATAต้องตั้งค่าสถานะ
  • Static IVs ไม่ปลอดภัยโดยปกติ IV ใหม่จะถูกสร้างขึ้นสำหรับการเข้ารหัสแต่ละครั้งซึ่งจะส่งไปยังผู้รับพร้อมกับ ciphertext เนื่องจาก IV ไม่ใช่ความลับจึงมักจะวางไว้ด้านหน้าของการเข้ารหัสในระดับไบต์โดยไม่มีการเข้ารหัส

ตัวอย่างโค้ด PHP ต่อไปนี้ (ขึ้นอยู่กับรหัสที่โพสต์):

function myCrypt($value, $key, $iv){
    $encrypted_data = openssl_encrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return base64_encode($encrypted_data); } function myDecrypt($value, $key, $iv){
    $value = base64_decode($value);
    $data = openssl_decrypt($value, 'aes-256-cbc', $key, OPENSSL_RAW_DATA, $iv);
    return $data; } $valTxt="MyText";
$key="01234567890123456789012345678901"; // 32 bytes $vector="1234567890123412"; // 16 bytes
$encrypted = myCrypt($valTxt, $key, $vector);
$decrypted = myDecrypt($encrypted, $key, $vector);
print($encrypted . "\n"); print($decrypted . "\n");

ส่งคืนผลลัพธ์ต่อไปนี้:

1SF+kez1CE5Rci3H6ff8og==
MyText

รหัส CryptoJS ที่เกี่ยวข้องสำหรับการถอดรหัสคือ:

var DataEncrypt = "1SF+kez1CE5Rci3H6ff8og==";
var DataKey = CryptoJS.enc.Utf8.parse("01234567890123456789012345678901");
var DataVector = CryptoJS.enc.Utf8.parse("1234567890123412");
var decrypted = CryptoJS.AES.decrypt(DataEncrypt, DataKey, { iv: DataVector });        
var decrypted = CryptoJS.enc.Utf8.stringify(decrypted);
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>

ด้วยเอาต์พุตMyTextที่สอดคล้องกับข้อความธรรมดาดั้งเดิม

เป็นสิ่งสำคัญที่คีย์จะถูกส่งผ่านWordArrayเพื่อให้ถูกตีความว่าเป็นคีย์ไม่ใช่เป็นข้อความรหัสผ่าน สำหรับการแปลง CryptoJS มีตัวเข้ารหัส (เช่นCryptoJS.enc.Utf8)


เกี่ยวกับคำถามของคุณในตอนท้าย: Ciphertexts เป็นลำดับไบนารีโดยพลการที่สามารถแปลงเป็นสตริงที่มีการเข้ารหัสแบบไบนารีเป็นข้อความพิเศษ (เช่น Base64 ในกรณีนี้หรือเลขฐานสิบหก) ซึ่งโดยทั่วไปจะยาวกว่าข้อมูลดิบ (Base64: ประสิทธิภาพ 75% เลขฐานสิบหก: ประสิทธิภาพ 50% ดูที่นี่ )
การแสดงบล็อกไซเฟอร์เท็กซ์ที่มีอักขระตัวเลขและตัวอักษร (เช่น 16 ตัวอักษร) เท่ากับขนาดบล็อก (เช่น 16 ไบต์สำหรับ AES) โดยทั่วไปจึงไม่สามารถทำได้
โปรดทราบว่าการแปลงเป็นสตริงด้วยการเข้ารหัสชุดอักขระเช่น UTF8 ไม่ใช่วิธีแก้ปัญหา แต่จะทำให้ข้อมูลเสียหาย