Как проверить с помощью PHP-подписи жанр, оцененный cryptico.js

Я пытаюсь подписывать сообщения в javascript перед отправкой в ​​​​приложение PHP. Приложение PHP должно проверить подпись, чтобы убедиться, что она не является ложной.

В javascript я использую cryptico.js.

Это функция js для подписи сообщений

var sign = function(passphrase, text) {
    signingkey = cryptico.generateRSAKey(passphrase, 2048);
    signString = cryptico.b16to64(signingkey.signString(text, "sha256"));
    return signString;
}

Это функция для получения открытого ключа:

var getPublicKey = function(passphrase) {
 var rsaKey = cryptico.generateRSAKey(passphrase, 2048);
 return = cryptico.publicKeyString(rsaKey);
}

Например, для сообщения «message» и фразы-пароля «test2» открытый ключ и подпись

QH / J3 / ОДГ / h5U02uPyC9Qzn / hHEV5DzB9nFfqk5zbQqHdInVe4sfL + NPA + 4fjLGrBU30Iuvcr + o9paEjzpH5dY48cq6JHqz1RyJ0CQIc2Jr5 + sS4eL1ZIjxWlyN1pKMR + 4aE2rlDAad56Ad1cytiaHuVvyK / gdtbKiuGroSQhJ1EVfZ60m3NIqnqmpi5Zdsnmzny4VH / d66BcGXxGaGaUaqFn0WTypuwIMZMMtzZEK7peKoaW4H4rfkfdrKcD8AaT + z9v5lLGkTl0NcZZ4LN9sSUzsHNfyAFK6cSXo / 73z0tDAlGb5K + yWV6UHoYW1rcoIsxlNRZM6 / 6FYgMXbbfow ==

XbF4O6v6oadEQGtdpQ7d54Q2JB9 / ZEXEUH3S1FMn4E / PSqk7HLXjG4tNfuiUBa5eS8kYV49gwC8Yr + mn6YUAHt + K9lHPSsmltWoiHNOaas4aqai9nlyeft4TYYhP + GYbQfw + 3n2TcO39s6M0vw0m0a8AX9JfF02JwCUhP4bu4dzG6Bl5dj000TbUkric14Jyurp8OHmmMvKW62TvXPhNOW39 + wS1Qkfn9Bxmzi8UEVSVe3wP45JWZNgmgeGnpubDhD05FJEDErfVtZ / DRKD81q5YRd4X4cCkeDPDcJLgKW1jkCsA7yBqESXPDSkkrVUM06A9qMFUwk4mRI88fZ8ryQ ==

Я спрашиваю, как это проверить в php?

Я пробовал что-то вроде:

$rsa = new Crypt_RSA();
$rsa->loadKey('qH/J3/gvF/h5U02uPyC9Qzn/hHEV5DzB9nFfqk5zbQqHdInVe4sfL+npa+4fjLGrBU30Iuvcr+o9paEjzpH5dY48cq6JHqz1RyJ0CQIc2Jr5+sS4eL1ZIjxWlyN1pKMR+4aE2rlDAad56Ad1cytiaHuVvyK/gdtbKiuGroSQhJ1EVfZ60m3NIqnqmpi5Zdsnmzny4VH/d66BcGXxGaGaUaqFn0WTypuwIMZMMtzZEK7peKoaW4H4rfkfdrKcD8AaT+z9v5lLGkTl0NcZZ4LN9sSUzsHNfyAFK6cSXo/73z0tDAlGb5K+yWV6UHoYW1rcoIsxlNRZM6/6FYgMXbbfow=='); // public key
echo $rsa->verify('message', 'XbF4O6v6oadEQGtdpQ7d54Q2JB9/ZEXEUH3S1FMn4E/PSqk7HLXjG4tNfuiUBa5eS8kYV49gwC8Yr+mn6YUAHt+K9lHPSsmltWoiHNOaas4aqai9nlyeft4TYYhP+GYbQfw+3n2TcO39s6M0vw0m0a8AX9JfF02JwCUhP4bu4dzG6Bl5dj000TbUkric14Jyurp8OHmmMvKW62TvXPhNOW39+wS1Qkfn9Bxmzi8UEVSVe3wP45JWZNgmgeGnpubDhD05FJEDErfVtZ/DRKD81q5YRd4X4cCkeDPDcJLgKW1jkCsA7yBqESXPDSkkrVUM06A9qMFUwk4mRI88fZ8ryQ==') ? 'verified' : 'unverified';

Я думаю, что подпись и/или открытый ключ неправильно отформатированы для php. Есть идеи?

Заранее спасибо,

[EDIT] Я не уверен, что подпись верна. Если я использую функцию js cryptico.b64to16(signature), подпись будет выглядеть примерно так:

5db1783babfaa1a744406b5da50edde78436241f7f6445c4507dd2d45327e04fcf4aa93b1cb5e31b8b4d7ee89405ae5e4bc918578f60c02f18afe9a7e985001edf8af651cf4ac9a5b56a221cd39a6ace1aa9a8bd9e5c9e7ede1361884ff8661b41fc3ede7d9370edfdb3a334bf0d26d1af005fd25f174d89c025213f86eee1dcc6e81979763d34d136d492b89cd78272baba7c3879a632f296eb64ef5cf84d396dfdfb04b54247e7f41c66ce2f141154957b7c0fe3925664d82681e1a7a6e6c3843d3914910312b7d5b59fc344a0fcd6ae5845de17e1c0a47833c37092e0296d63902b00ef206a1125cf0d2924ad550cd3a03da8c154c24e26448f3c7d9f2bc9

Я не уверен в формате ключа параметра $rsa->verify. Я попытался добавить префикс ssh-rsa. Но это не работает лучше.

Поэтому я попробовал формат подписи и ключ. Сообщение каждый раз "непроверено"


person Madef    schedule 29.05.2014    source источник
comment
Вы знаете, что при каждом вызове cryptico.generateRSAKey(passphrase, 2048) будет создаваться (надеюсь) другой ключ RSA, верно?   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Это швы, что каждый раз одно и то же. Я использую три JS-скрипта/страницы для тестирования библиотеки в JS. Один для генерации открытого ключа, другой для подписи и третий для проверки. Для первого я даю только парольную фразу, для второго — парольную фразу и сообщение, а для третьего — открытый ключ, сообщение и подпись.   -  person Madef    schedule 29.05.2014
comment
Если функция генерации ключей RSA (подпрограмма) всегда возвращает один и тот же ключ, не используйте ее! Если вы не тестируете PHP-скрипт и вас абсолютно не волнует часть JS, я настоятельно рекомендую сменить библиотеку. Ключи RSA должны быть как можно более случайными.   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Возможно, IK допустил ошибку, или crytico не является хорошей библиотекой. cryptico.generateRSAKey('test2', 2048).signString('message', "sha256"); Каждый раз одна и та же подпись. То же самое для открытого ключа: cryptico.publicKeyString(cryptico.generateRSAKey('test2', 2048));   -  person Madef    schedule 29.05.2014
comment
Одно и то же сообщение, генерирующее ту же подпись, допустимо в RSA. Более того, он должен быть всегда одинаковым. Что не нормально, так это то, что вы получаете один и тот же открытый ключ (а значит, и один и тот же закрытый ключ) при каждом вызове cryptico.publicKeyString(cryptico.generateRSAKey('test2', 2048));. Я не могу поручиться за cryptico (я никогда не использовал его), но это, безусловно, красный флаг.   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
У вас есть другая JS-библиотека, чтобы порекомендовать мне?   -  person Madef    schedule 29.05.2014
comment
Нет, я прошу прощения. Кроме того, поскольку вы сами не замечали проблемы с RSA, я могу сделать вывод, что у вас мало или совсем нет опыта написания криптографического/защитного кода. Я не имею в виду, что вы плохой программист, вы можете быть очень опытны в том, что я знаю, но вы должны оставить криптографический код людям, которые очень хорошо знают, что они делают. В то время как ошибка в обычном коде может привести к ошибке безопасности, ошибка в коде шифрования несомненно приведет к серьезной проблеме безопасности.   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Кроме того, прочитав несколько строк кода Cryptico, я также могу сделать вывод, что парень, который написал это, не очень хорошо знал, что она делала...   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Насколько я могу судить, cryptico не хранит ключи в каком-либо стандартном формате: stackoverflow.com/a/15643041/569976 phpseclib — используемая вами библиотека RSA — поддерживает стандартные схемы подписи — PKCS1 и PSS. Учитывая, что формат ключа cryptico является пользовательским, кажется вероятным, что формат их подписи также.   -  person neubert    schedule 29.05.2014
comment
Стефано Санфилиппо› Вы абсолютно правы. Я пытаюсь понять, как работают подписи. Этот код делается только для локальных тестов. Так что не очень важно, если в моем коде есть дыра в безопасности, потому что он сделан для обучения и никогда не будет использоваться в производстве (но, конечно, лучше, если в нем нет проблем с безопасностью). Одного я не понимаю. Если открытый ключ меняется каждый раз, когда Сэм отправляет сообщение. Как Мэтт может быть уверен, что сообщение от Сэма, а не от кого-то другого?   -  person Madef    schedule 29.05.2014
comment
На самом деле ключ должен быть сгенерирован один раз. Вы храните приватный ключ в надежном месте и распространяете публичный. Теперь можно зашифровать сообщения, используя закрытый ключ получателя, отправить их и попросить получателя расшифровать их, используя свой закрытый ключ. Таким образом, любой может зашифровать сообщение и отправить его вам, но вы единственный, кто может его расшифровать. Это только часть безопасной системы. Также нужно убедиться, что зашифрованное сообщение действительно было отправлено определенным человеком. Сертификат — это то, что связывает личность с открытым ключом.   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Это очень теоретическая область, вы должны остановиться здесь и потратить некоторое время, чтобы прочитать что-нибудь об PKI. по меньшей мере...   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Упс... замените второе вхождение private на public.   -  person Stefano Sanfilippo    schedule 29.05.2014
comment
Хорошо спасибо. Я понял, почему это не случайно. Парольная фраза используется для изменения начального случайного числа Math.random. Чтобы быть более точным, используйте sha256 парольной фразы. Я предполагаю, что это защищено только тогда, когда парольная фраза большая.   -  person Madef    schedule 29.05.2014
comment
Я пробовал с другой библиотекой js (jsrsasign и jsencrypt). jsencrypt для создания ключей и jsrsasign для подписи и проверки. Но подписи PHP и подписи jsrsasign не совпадают. Таким образом, подписи, сгенерированные с помощью jsrsasign, не могут быть проверены с помощью PHP.   -  person Madef    schedule 29.05.2014
comment
Не то чтобы я что-то знал о jsrsasign/jsencrypt, но... phpseclib по умолчанию использует схему подписи PSS. Хотя он более безопасен, он также не так широко используется. ПКС1 есть. См. phpseclib.sourceforge.net/rsa/examples.html#sign,sign2 для получения дополнительной информации.   -  person neubert    schedule 29.05.2014
comment
Да, система так же надежна, как и ее пароль. Что, вообще говоря, неплохое предположение, но, безусловно, верно, когда речь идет о RSA. На самом деле, для работы RSA даже не нужен пароль, это должна быть только форма резервного копирования на случай, если кто-то украдет ваш закрытый ключ.   -  person Stefano Sanfilippo    schedule 29.05.2014


Ответы (1)


Спасибо @neubert, это благодаря подписи PSS.

Итак, вот решение.

Я использую:

  • phpseclib : PHP-библиотека, используемая для проверки сообщения.
  • jsrsasign : библиотека JS, используемая для подписи сообщений.
  • jsencrypt: библиотека JS для создания закрытого и открытого ключа

Сначала сгенерируйте ключи в JS:

crypt = new JSEncrypt({default_key_size: 512});
var key = crypt.getKey();

var publicKey = key.getPublicKey();
var privateKey = key.getPrivateKey();

Во-вторых, создайте подпись:

var rsa = new RSAKey();
rsa.readPrivateKeyFromPEMString(privateKey);
var hSig = rsa.signStringPSS('message', 'sha1');
var signature = linebrk(hSig, 64);
console.log(signature);

По умолчанию подпись имеет неправильный формат. Мы должны закодировать hSig в базе 64 с помощью функции base64Chars.

var base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
b16to64 = function(h) {
    var i;
    var c;
    var ret = "";
    if(h.length % 2 == 1)
    {
        h = "0" + h;
    }
    for (i = 0; i + 3 <= h.length; i += 3)
    {
        c = parseInt(h.substring(i, i + 3), 16);
        ret += base64Chars.charAt(c >> 6) + base64Chars.charAt(c & 63);
    }
    if (i + 1 == h.length)
    {
        c = parseInt(h.substring(i, i + 1), 16);
        ret += base64Chars.charAt(c << 2);
    }
    else if (i + 2 == h.length)
    {
        c = parseInt(h.substring(i, i + 2), 16);
        ret += base64Chars.charAt(c >> 2) + base64Chars.charAt((c & 3) << 4);
    }
    while ((ret.length & 3) > 0) ret += "=";
    return ret;
}

Чтобы закончить, мы проверяем в PHP. Мы предполагаем, что подпись и открытый ключ хранятся в переменных с одинаковым именем:

$rsa = new Crypt_RSA();
$rsa->loadKey($publickey); // public key;
echo $rsa->verify('message', base64_decode($signature)) ? 'verified' : 'unverified';
person Madef    schedule 29.05.2014
comment
Отличная работа. Вы можете переместить кодирующую часть на сервер, если хотите, удалив b16to64 и заменив base64_decode на hex2bin на сервере. В любом случае, вы должны знать, что криптография в браузерах с JS — это очень плохая идея (см. это), по крайней мере, до Web Crypto API широко используется. - person Stefano Sanfilippo; 01.06.2014