Я создаю простое клиентское приложение для экспериментов с Mega и не могу понять, как используется RSA. . Возьмем, к примеру, расшифровку идентификатора сеанса — это одно из первых действий, которое необходимо сделать, чтобы войти в систему.
API предоставляет мне следующие данные RSA:
- р (1024 бита)
- д (1024 бита)
- д (2044 бита)
- u (1024 бита)
Начнем с того, что я не знаю, что означает "у". Я вижу из кода, что он вычисляется modinverse(p, q)
- это то, что обычно называют qInverse?
Это значительно меньше данных RSA для закрытого ключа, чем я использовал ранее, поэтому я не совсем уверен, что с этим делать. Однако мне дали понять, что некоторые данные RSA, используемые RSACryptoServiceProvider, являются просто предварительно рассчитанными данными для целей оптимизации, поэтому, возможно, остальные не нужны?
Используя эти данные, JavaScript сайта расшифровывает идентификатор сеанса с помощью следующей функции:
// Compute m**d mod p*q for RSA private key operations.
function RSAdecrypt(m, d, p, q, u)
{
var xp = bmodexp(bmod(m,p), bmod(d,bsub(p,[1])), p);
var xq = bmodexp(bmod(m,q), bmod(d,bsub(q,[1])), q);
var t=bsub(xq,xp);
if(t.length==0)
{
t=bsub(xp,xq);
t=bmod(bmul(t, u), q);
t=bsub(q,t);
}
else
{
t=bmod(bmul(t, u), q);
}
return badd(bmul(t,p), xp);
}
Я хотел бы сделать это в .NET, используя RSACryptoServiceProvider, но если я передам ему 4 части данных, которые у меня есть (при условии, что u == qInverse), ключ будет отклонен во время импорта с исключением «Неверные данные».
Должен ли я делать что-то еще с данными? Можно ли вообще использовать RSACryptoServiceProvider в этой ситуации?
Пример параметров и зашифрованных данных, которые я тестирую, приведен ниже.
var p = Convert.FromBase64String("1AkMwy3SPbJtL/k2RUPNztBQKow0NX9LVr5/73+zR3cuwgUToYkVefKdzlTgeri9CAVUq/+jU6o+P7sUpPUN+V97quZa00m3GSIdonRMdaMrDDH5aHnkQgOsCjLJDWXU6+TQBqLumR3XMSat3VO09Dps+6NcMc+uMi5atC3tb+0=");
var q = Convert.FromBase64String("qtnlmPbATJajNdihw1K6cwSormySATp7g75vYfilYx6RXN3xpNCZR/i8zFbx/lDh+n1a2rdHy1nWyuaD3UmE26d1xUkmsPDfBc72WXt88UqWE/gF7NJjtgTxS2Ui+2GGKUCloi5UA/pOI7R5TBvGI8zna00SH78bctyE0dcAcwM=");
var d = Convert.FromBase64String("CFL4QPQ8zLzrf2bUzCVX8S2/eALzo/P2cvQsW9lft7uelHYfC1CvHP+z4RvQgXABpgT8YTdU+sgdMHrhHT1vxeUaDRkcQv9lV0IP6YtAcD+gk5jDQkXk4ruYztTUF3v4u8rlMuZ8kAKKWKw+JH6grLWD/vXjMv2RybxPqq3fKI6VJaj/Y/ZnDjD5HrQmJopnCbOrZrPysNb/rGrN3ad9ysaZwBvQtIE0/tQvmL+lsI+PfF9oGKeHkciIo0D4N2abOKT2fiazNm1U9LnrQih687ge0aeAlP2OO8c0h/nbEkMbNg83n1GGEt3DNojIWbT5uHaj12M6G81leS77mfLvSQ==");
var u = Convert.FromBase64String("CNlUzgCf6Ymd/qeWiv3ScCIXYCwjP3SNLHxRgozIbNg2JEKpJn2M3vO72qLI+FT34xckaAGIcKWMkmpoaKy6PYF4jsAz2atLEClLimbMEPvpWxK7b/I5yvXMT7i2r5hr0OjjplL0wFQYL1IS2M8DTrL99rd9zXCoCWg5Tax6zQM=");
var encryptedData = Convert.FromBase64String("CABt/Qp7ZODvweEk5RY9JNMXoyFfUwMnc53zbP5jB4jnwWXibLLvjc+Dv5CwQAtUYRme+vRd80++178BiWl0YSOKKhQaDQKoeOUONn3KbZVWyCtyWyQZNtASPoQfizay/Dw3yP5BKsJmDpEv47awdEZzh8IqTcTKeQbpHFL+3uL5EjIENpxMh15rJUsY9w+jq6Yax+379tq67EPMUON0aYkRQ3k1Rsp9fOL6qrgoqOPmOc0cIQgx76t6SFB9LmDySkyBhtK+vcEkdn9GwzZqc6n/Jqt9K8a+mbBv3K7eO3Pa37SDncsaxEzlyLwQ2om1+bK2QwauSQl+7QwQS1a9Ejb9");
var rsa = new RSACryptoServiceProvider();
// Throws exception saying "Bad data"
rsa.ImportParameters(new RSAParameters
{
D = d,
P = p,
Q = q,
InverseQ = u
});
Дополнение от 2 февраля
Я копался в связанных ответах StackOverflow и достиг точки, когда я думаю, что определил, как сгенерировать отсутствующий компонент. Однако теперь я получаю исключение «Плохой ключ», которое ставит меня в тупик.
Я напишу код, который использую для генерации недостающих компонентов — возможно, вы где-нибудь заметите ошибку?
Я также рассчитал InverseQ и D вручную, и значения совпадают со значениями в моих входных данных. Ниже моя функция для генерации необходимых данных только на основе q, p и e.
private static RSAParameters CalculateRsaParameters(BigInteger p, BigInteger q, BigInteger e)
{
var modulus = BigInteger.Multiply(p, q);
var phi = BigInteger.Multiply(BigInteger.Subtract(p, BigInteger.One), BigInteger.Subtract(q, BigInteger.One));
BigInteger x, y;
// From http://www.codeproject.com/Articles/60108/BigInteger-Library
// Returns 1 with my test data.
ExtendedEuclidGcd(e, phi, out x, out y);
var d = BigInteger.Remainder(x, phi);
var dp = BigInteger.Remainder(d, BigInteger.Subtract(p, BigInteger.One));
var dq = BigInteger.Remainder(d, BigInteger.Subtract(q, BigInteger.One));
BigInteger x2, y2;
// Returns 1 with my test data.
ExtendedEuclidGcd(q, p, out x2, out y2);
// y2 since it matched the pre-generated inverseQ data I had and x2 was some negative value, so it did not seem to fit. I have no idea what the logic behind which to pick really is.
var qInverse = BigInteger.Remainder(y2, p);
return new RSAParameters
{
D = ToBigEndianByteArray(d, 256),
DP = ToBigEndianByteArray(dp, 128),
DQ = ToBigEndianByteArray(dq, 128),
InverseQ = ToBigEndianByteArray(qInverse, 128),
Exponent = ToBigEndianByteArray(e, 1),
Modulus = ToBigEndianByteArray(modulus, 256),
P = ToBigEndianByteArray(p, 128),
Q = ToBigEndianByteArray(q, 128)
};
}
Мои входные данные:
e = 17
p = 148896287039501678969147386479458178246000691707699594019852371996225136011987881033904404601666619814302065310828663028471342954821076961960815187788626496609581811628527023262215778397482476920164511192915070597893567835708908996890192512834283979142025668876250608381744928577381330716218105191496818716653
q = 119975764355551220778509708561576785383941026741388506773912560292606151764383332427604710071170171329268379604135341015979284377183953677973647259809025842247294479469402755370769383988530082830904396657573472653613365794770434467132057189606171325505138499276437937752474437953713231209677228298628994462467
И вот как я использую сгенерированную структуру:
var rsa = new RSACryptoServiceProvider(2048);
rsa.ImportParameters(CalculateRsaParameters(p, q, e));
Вызов ImportParameters
выдает исключение с сообщением «Плохой ключ». Что я делаю неправильно?
Что произойдет, если я поменяю местами Q и P?
Судя по всему, это заставляет RSACryptoServiceProvider принимать данные! Но что это означает?
Я получил эту идею от того, как мне пришлось использовать ExtendedEuclidGcd
в моем коде генерации. Необходимость использовать разные выходные данные для двух экземпляров меня очень беспокоила, поэтому я провел этот эксперимент.
Одно дело, что u != qInverse
- это правильно? Я не понимаю математику в исходной функции JavaScript, поэтому я не уверен, каковы последствия. Правильно ли я предполагаю, что значение u в оригинале на самом деле является каким-то внутренним ярлыком, а не QInverse?
Предстоит дальнейшее тестирование (т. е. фактическая расшифровка данных). Я отредактирую вопрос с любыми новыми разработками после того, как они будут сделаны.
С этим набором параметров расшифровка не удалась
У меня есть зашифрованные тестовые данные (в кодировке base64):
/TYSvVZLEAztfglJrgZDtrL5tYnaELzI5UzEGsudg7Tf2nM73q7cb7CZvsYrfasm/6lzajbDRn92JMG9vtKGgUxK8mAufVBIeqvvMQghHM055uOoKLiq+uJ8fcpGNXlDEYlpdONQzEPsutr2++3HGqarow/3GEsla16HTJw2BDIS+eLe/lIc6QZ5ysRNKsKHc0Z0sLbjL5EOZsIqQf7INzz8sjaLH4Q+EtA2GSRbcivIVpVtyn02DuV4qAINGhQqiiNhdGmJAb/Xvk/zXfT6nhlhVAtAsJC/g8+N77Js4mXB54gHY/5s851zJwNTXyGjF9MkPRblJOHB7+Bkewr9bQ==
or
bf0Ke2Tg78HhJOUWPSTTF6MhX1MDJ3Od82z+YweI58Fl4myy743Pg7+QsEALVGEZnvr0XfNPvte/AYlpdGEjiioUGg0CqHjlDjZ9ym2VVsgrclskGTbQEj6EH4s2svw8N8j+QSrCZg6RL+O2sHRGc4fCKk3EynkG6RxS/t7i+RIyBDacTIdeayVLGPcPo6umGsft+/bauuxDzFDjdGmJEUN5NUbKfXzi+qq4KKjj5jnNHCEIMe+rekhQfS5g8kpMgYbSvr3BJHZ/RsM2anOp/yarfSvGvpmwb9yu3jtz2t+0g53LGsRM5ci8ENqJtfmytkMGrkkJfu0MEEtWvRI2/Q==
Даны две альтернативы, поскольку я не уверен в порядке байтов. Это одни и те же данные в обеих строках.
Расшифровка обоих из них завершается сбоем, за исключением сообщения «Неверные данные» в первом случае и «Недостаточно памяти для обработки этой команды». во втором случае (что, по утверждению MSDN, может означать, что ключ не соответствует зашифрованным данным). Я сообщаю RSACryptoServiceProvider, что используется заполнение PKCS, хотя я также экспериментировал с OAEP (который только что выдал ошибку о невозможности декодировать заполнение).
Оригинальный JavaScript без проблем расшифровывает данные, хотя его "p" и "q" поменялись местами с моими.
Прямо сейчас мои вопросы:
- Является ли переключение P и Q допустимой операцией?
- Верны ли мои рассуждения или я где-то ошибся?
- Что мне делать дальше, чтобы успешно расшифровать мои тестовые данные?