Подтвердить что ты не робот

Шифрование в Javascript, дешифрование на PHP, использование криптографии с открытым ключом

Я хотел бы зашифровать в JavaScript, расшифровать в PHP, используя криптографию с открытым ключом. Я пытался найти библиотеки, которые могут это сделать, но у меня проблемы.

Я сейчас смотрю openpgpjs, но мне нужна поддержка во всех браузерах, и даже на тестовой странице есть ошибки на только указанный браузер (Google Chrome).

Заметки о конечной цели:

TCP-соединение уже защищено SSL. Основная цель этого уровня защиты - защита от преднамеренного или непреднамеренного ведения журнала веб-сервера, аварийных дампов и т.д.

На стороне PHP будет создан временный закрытый ключ (он истечет через короткое время). Вызывающий (в Javascript) отвечает за запрос нового открытого ключа, когда он истекает. Причина истечения срока действия закрытого ключа заключается в предотвращении зашифрованного шифрования данных, если сервер, в котором хранится закрытый ключ, впоследствии скомпрометирован.

Сценарий с компрометацией серверов: кто-то получает резервные копии для всех машин, кроме сервера базы данных (и не может получить доступ к базе данных из-за брандмауэра, даже если он узнает пользователя и пароль). Поскольку закрытый ключ, который зашифровал зарегистрированные данные, больше не существует, злоумышленник ничего не может сделать.

4b9b3361

Ответ 1

Я использовал что-то похожее для моей страницы входа; он шифрует учетные данные для входа с использованием информации открытого ключа (N, e), которая может быть расшифрована в PHP.

Он использует следующие файлы, которые являются частью JSBN:

  • jsbn.js - для работы с большими целыми числами
  • rsa.js - только для шифрования RSA (использует jsbn.js)
  • rng.js - основной сборщик энтропии
  • prng4.js - сервер ARC4 RNG

Чтобы зашифровать данные:

$pk = '-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----';
$kh = openssl_pkey_get_private($pk);
$details = openssl_pkey_get_details($kh);

function to_hex($data)
{
    return strtoupper(bin2hex($data));
}

?>
<script>
var rsa = new RSAKey();
rsa.setPublic('<?php echo to_hex($details['rsa']['n']) ?>', '<?php echo to_hex($details['rsa']['e']) ?>');

// encrypt using RSA
var data = rsa.encrypt('hello world');
</script>

Вот как вы могли бы декодировать отправленные данные:

$kh = openssl_pkey_get_private($pk);
$details = openssl_pkey_get_details($kh);
// convert data from hexadecimal notation
$data = pack('H*', $data);
if (openssl_private_decrypt($data, $r, $kh)) {
   echo $r;
}

Ответ 2

Отметьте node-rsa.

Это модуль node.js

Этот модуль обеспечивает доступ к открытым ключам RSA из OpenSSL. Поддержка ограничена RSAES-OAEP и шифрование с помощью открытого ключа, дешифрование с помощью закрытого ключа.

Возможно, вы можете выполнить его портирование в браузере.

UPDATE

Клиентская библиотека RSA для javascript: (pidcrypt был официально прекращен и домен веб-сайта истек - см. ответ @jack, который содержит те же библиотеки, что и pidcrypt). https://www.pidder.com/pidcrypt/?page=rsa

Компонент на стороне сервера PHP: http://phpseclib.sourceforge.net/

Удачи!

Ответ 3

Будьте осторожны с реализацией RSA. На самом деле, вы, вероятно, не должны использовать RSA вообще. (Вместо этого используйте libsodium!)

Даже если вы используете библиотеку (например, расширение PHP OpenSSL напрямую или, до недавнего времени, Zend\Crypt), есть еще много чего, что может пойти не так. В частности:

  • Заполнение PKCS1v1.5, которое используется по умолчанию (и во многих случаях единственный поддерживаемый режим заполнения), уязвимо для класса атак с выбранным шифротекстом, называемых оракулом заполнения. Это было впервые обнаружено Даниэлем Блейхенбахером. В 1998 году.
  • RSA не подходит для шифрования больших сообщений, поэтому разработчики часто берут длинное сообщение, разбивают его на блоки фиксированного размера и шифруют каждый блок отдельно. Это не только медленно, но и аналогично ужасному режиму ECB для криптографии с симметричным ключом.

Лучшее, что можно сделать с Libsodium

Вы можете прочитать JavaScript-криптографию, считающуюся вредной, несколько раз, прежде чем идти по этому пути. Но это сказал...

  1. Используйте TLSv1.2 с HSTS и HPKP, предпочтительно с ChaCha20-Poly1305 и/или AES-GCM и сертификатом ECDSA-P256 (важно: когда IETF окрестит Curve25519 и Ed25519, переключитесь на это вместо этого).
  2. Добавьте libsodium.js в свой проект.
  3. Используйте crypto_box_seal() с открытым ключом для шифрования ваших сообщений на стороне клиента.
  4. В PHP используйте \Sodium\crypto_box_seal_open() с соответствующим секретным ключом для открытого ключа, чтобы расшифровать сообщение.

Мне нужно использовать RSA для решения этой проблемы.

Пожалуйста, не. Криптография с эллиптической кривой быстрее, проще и намного проще в реализации без побочных каналов. Большинство библиотек уже делают это для вас. (Libsodium!)

Но я действительно хочу использовать RSA!

Хорошо, следуйте этим рекомендациям в письме и не плачь в Кару, когда ты совершаешь ошибку (как SaltStack сделал), которая делает твою криптографию бесполезной.

paragonie/easyrsa.

  • . Одна из опций (которая не поставляется с дополнительной реализацией JavaScript, и не спрашивайте ее), которая направлена на обеспечение простого и легкого шифрования RSA.Он избегает оракулов заполнения, используя RSA-OAEP с MGF1 + SHA256 вместо PKCS1v1.5.
  • Он избегает режима ECB благодаря продуманному дизайну протокола:

Протокол шифрования EasyRSA

  1. EasyRSA генерирует случайный 128-битный ключ для криптографии с симметричным ключом (через AES).
  2. Ваше текстовое сообщение зашифровано с помощью defuse/php-encryption.
  3. Ваш ключ AES зашифрован с помощью RSA, предоставленного phpseclib, в правильном режиме (указан выше).
  4. Эта информация упакована в простую строку (с контрольной суммой).

Но на самом деле, если вы найдете подходящий вариант использования криптографии с открытым ключом, вам нужен libsodium.

Бонус: шифрование с помощью JavaScript, дешифрование с помощью PHP

Мы собираемся использовать натрий-плюс для достижения этой цели. (Принято из этого поста.)

const publicKey = X25519PublicKey.from('fb1a219011c1e0d17699900ef22723e8a2b6e3b52ddbc268d763df4b0c002e73', 'hex');

async function sendEncryptedMessage() {
    let key = await getExampleKey();
    let message = $("#user-input").val();
    let encrypted = await sodium.crypto_box_seal(message, publicKey);
    $.post("/send-message", {"message": encrypted}, function (response) {
        console.log(response);
        $("#output").append("<li><pre>" + response.message + "</pre></li>");
    });
}

А затем конгруэнтный код PHP:

<?php
declare(strict_types=1);
require 'vendor/autoload.php'; // Composer
header('Content-Type: application/json');
$keypair = sodium_hex2bin(
    '0202040a9fbf98e1e712b0be8f4e46e73e4f72e25edb72e0cdec026b370f4787' .
    'fb1a219011c1e0d17699900ef22723e8a2b6e3b52ddbc268d763df4b0c002e73'
);

$encrypted = $_POST['message'] ?? null;
if (!$encrypted) {
    echo json_encode(
        ['message' => null, 'error' => 'no message provided'],
        JSON_PRETTY_PRINT
    );
    exit(1);
}
$plaintext = sodium_crypto_box_seal_open($encrypted, $keypair);

echo json_encode(
    ['message' => $plaintext, 'original' => $encrypted],
    JSON_PRETTY_PRINT
);

Ответ 4

Использование примера RSA для pidCrypt (js) и phpseclib (php).

Не используйте повторно закрытый ключ в этом рабочем примере.

шифрование pidCrypt

//From the pidCrypt example sandbox
function certParser(cert) {
    var lines = cert.split('\n');
    var read = false;
    var b64 = false;
    var end = false;
    var flag = '';
    var retObj = {
    };
    retObj.info = '';
    retObj.salt = '';
    retObj.iv;
    retObj.b64 = '';
    retObj.aes = false;
    retObj.mode = '';
    retObj.bits = 0;
    for (var i = 0; i < lines.length; i++) {
        flag = lines[i].substr(0, 9);
        if (i == 1 && flag != 'Proc-Type' && flag.indexOf('M') == 0)//unencrypted cert?
        b64 = true;
        switch (flag) {
            case '-----BEGI':
                read = true;
                break;
            case 'Proc-Type':
                if (read)retObj.info = lines[i];
                break;
            case 'DEK-Info:':
                if (read) {
                    var tmp = lines[i].split(',');
                    var dek = tmp[0].split(': ');
                    var aes = dek[1].split('-');
                    retObj.aes = (aes[0] == 'AES') ? true : false;
                    retObj.mode = aes[2];
                    retObj.bits = parseInt(aes[1]);
                    retObj.salt = tmp[1].substr(0, 16);
                    retObj.iv = tmp[1];
                }
                break;
            case '':
                if (read)b64 = true;
                break;
            case '-----END ':
                if (read) {
                    b64 = false;
                    read = false;
                }
                break;
                default : if (read && b64)retObj.b64 += pidCryptUtil.stripLineFeeds(lines[i]);
        }
    }
    return retObj;
}

var strCreditCardPublicKey="-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC\/tI7cw+gnUPK2LqWp50XboJ1i\njrLDn+4\/gPOe+pB5kz4VJX2KWwg9iYMG9UJ1M+AeN33qT7xt9ob2dxgtTh7Mug2S\nn1TLz4donuIzxCmW+SZdU1Y+WNDINds194hWsAVhMC1ClMQTfldUGzQnI5sXvZTF\nJWp\/9jheCNLDRIkAnQIDAQAB\n-----END PUBLIC KEY-----\n";

var objParams=certParser(strCreditCardPublicKey);
var binaryPrivateKey=pidCryptUtil.decodeBase64(objParams.b64);

var rsa=new pidCrypt.RSA();

var asn=pidCrypt.ASN1.decode(pidCryptUtil.toByteArray(key));
var tree=asn.toHexTree();
rsa.setPublicKeyFromASN(tree);

var strHexSensitiveDataEncrypted=rsa.encrypt("4111111111111111");

var strBase64SensitiveDataEncrypted=pidCryptUtil.fragment(pidCryptUtil.encodeBase64(pidCryptUtil.convertFromHex(strHexSensitiveDataEncrypted)), 64))

console.log(strBase64SensitiveDataEncrypted);

.

расшифровка phpseclib

require_once("Crypt/RSA.php");

function decrypt($strBase64CipherText)
{
    //CRYPT_RSA_MODE_INTERNAL is slow
    //CRYPT_RSA_MODE_OPENSSL is fast, but requires openssl to be installed, configured and accessible.
    define("CRYPT_RSA_MODE", CRYPT_RSA_MODE_INTERNAL);

    $rsa=new Crypt_RSA();


    //$strPrivateKey=file_get_contents("private.pem");
    //This private key is for example purposes
    //DO NOT REUSE
    $strPrivateKey="-----BEGIN RSA PRIVATE KEY-----
        MIICXQIBAAKBgQDBNHK7R2CCYGqljipbPoj3Pwyz4cF4bL5rsm1t8S30gbEbMnKn
        1gpzteoPlKp7qp0TnsgKab13Fo1d+Yy8u3m7JUd/sBrUa9knY6dpreZ9VTNul8Bs
        p2LNnAXOIA5xwT10PU4uoWOo1v/wn8eMeBS7QsDFOzIm+dptHYorB3DOUQIDAQAB
        AoGBAKgwGyxy702v10b1omO55YuupEU3Yq+NopqoQeCyUnoGKIHvgaYfiwu9sdsM
        ZPiwxnqc/7Eo6Zlw1XGYWu61GTrOC8MqJKswJvzZ0LrO3oEb8IYRaPxvuRn3rrUz
        K7WnPJyQ2FPL+/D81NK6SH1eHZjemb1jV9d8uGb7ifvha5j9AkEA+4/dZV+dZebL
        dRKtyHLfbXaUhJcNmM+04hqN1DUhdLAfnFthoiSDw3i1EFixvPSiBfwuWC6h9mtL
        CeKgySaOkwJBAMSdBhn3C8NHhsJA8ihQbsPa6DyeZN+oitiU33HfuggO3SVIBN/7
        HmnuLibqdxpnDOtJT+9A+1D29TkNENlTWgsCQGjVIC8xtFcV4e2s1gz1ihSE2QmU
        JU9sJ3YeGMK5TXLiPpobHsnCK8LW16WzQIZ879RMrkeDT21wcvnwno6U6c8CQQCl
        dsiVvXUmyOE+Rc4F43r0VRwxN9QI7hy7nL5XZUN4WJoAMBX6Maos2Af7NEM78xHK
        SY59+aAHSW6irr5JR351AkBA+o7OZzHIhvJfaZLUSwTPsRhkdE9mx44rEjXoJsaT
        e8DYZKr84Cbm+OSmlApt/4d6M4YA581Os1eC8kopewpy
        -----END RSA PRIVATE KEY-----
    ";
    $strPrivateKey=preg_replace("/[ \t]/", "", $strPrivateKey);//this won't be necessary when loading from PEM


    $rsa->loadKey($strPrivateKey);

    $binaryCiphertext=base64_decode($strBase64CipherText);

    $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1);
    $strBase64DecryptedData=$rsa->decrypt($binaryCiphertext);

    return base64_decode($strBase64DecryptedData);
}

//The pidCrypt example implementation will output a base64 string of an encrypted base64 string which contains the original data, like this one:
$strBase64CipherText="JDlK7L/nGodDJodhCj4uMw0/LW329HhO2EvxNXNUuhe+C/PFcJBE7Gp5GWZ835fNekJDbotsUFpLvP187AFAcNEfP7VAH1xLhhlB2a9Uj/z4Hulr4E2EPs6XgvmLBS3MwiHALX2fES5hSKY/sfSUssRH10nBHHO9wBLHw5mRaeg=";

$binaryDecrypted=decrypt($strBase64CipherText);

//should output '4111111111111111'
var_export($binaryDecrypted);