Я буду краток и чист.
Мне нужно отправить OTP в Microsoft API. Однако этот OTP шифруется на стороне клиента с помощью некоторой функции JavaScript, а затем отправляется в API. Это скрипт Microsoft для шифрования (исходный скрипт имеет более 2000 строк, я оставил только функции, используемые функцией Encrypt(). Я думаю, что это просто шифрование RSA в JavaScript, но я могу ошибаться):
function Encrypt(e, t, i, r) {
var n = [];
switch (i.toLowerCase()) {
case "saproof":
if (null == t) {
return null
}
n = PackageSADataForProof(t);
break;
case "newpwd":
if (null == r) {
return null
}
}
if (null == n || "undefined" == typeof n) {
return n
}
if ("undefined" != typeof Key && void 0 !== parseRSAKeyFromString) {
var o = parseRSAKeyFromString(Key)
}
var s = RSAEncrypt(n, o, randomNum);
return s
}
function PackageSADataForProof(e) {
var t, i = [], r = 0;
for (t = 0; t < e.length; t++) {
i[r++] = 127 & e.charCodeAt(t),
i[r++] = (65280 & e.charCodeAt(t)) >> 8
}
return i
}
function parseRSAKeyFromString(e) {
var t = e.indexOf(";");
if (0 > t) {
return null
}
var i = e.substr(0, t)
, r = e.substr(t + 1)
, n = i.indexOf(" = ");
if (0 > n) {
return null
}
var o = i.substr(n + 1);
if (n = r.indexOf(" = "),
0 > n) {
return null
}
var s = r.substr(n + 1)
, a = new Object;
return a.n = hexStringToMP(s),
a.e = parseInt(o, 16),
a
}
function hexStringToMP(e) {
var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
for (n.size = r,
t = 0; r > t; t++) {
i = e.substr(4 * t, 4),
n.data[r - 1 - t] = parseInt(i, 16)
}
return n
}
function RSAEncrypt(e, t) {
for (var i = [], r = 42, n = 2 * t.n.size - r, o = 0; o < e.length; o += n) {
if (o + n >= e.length) {
var s = RSAEncryptBlock(e.slice(o), t, randomNum);
s && (i = s.concat(i))
} else {
var s = RSAEncryptBlock(e.slice(o, o + n), t, randomNum);
s && (i = s.concat(i))
}
}
var a = byteArrayToBase64(i);
return a
}
function RSAEncryptBlock(e, t, i) {
var r = t.n
, n = t.e
, o = e.length
, s = 2 * r.size
, a = 42;
if (o + a > s) {
return null
}
applyPKCSv2Padding(e, s, i),
e = e.reverse();
var l = byteArrayToMP(e)
, d = modularExp(l, n, r);
d.size = r.size;
var h = mpToByteArray(d);
return h = h.reverse()
}
function JSMPnumber() {
this.size = 1,
this.data = [],
this.data[0] = 0
}
function byteArrayToMP(e) {
var t = new JSMPnumber
, i = 0
, r = e.length
, n = r >> 1;
for (i = 0; n > i; i++) {
t.data[i] = e[2 * i] + (e[1 + 2 * i] << 8)
}
return r % 2 && (t.data[i++] = e[r - 1]),
t.size = i,
t
}
function modularExp(e, t, i) {
for (var r = [], n = 0; t > 0; ) {
r[n] = 1 & t,
t >>>= 1,
n++
}
for (var o = duplicateMP(e), s = n - 2; s >= 0; s--) {
o = modularMultiply(o, o, i),
1 == r[s] && (o = modularMultiply(o, e, i))
}
return o
}
function modularMultiply(e, t, i) {
var r = multiplyMP(e, t)
, n = divideMP(r, i);
return n.r
}
function multiplyMP(e, t) {
var i = new JSMPnumber;
i.size = e.size + t.size;
var r, n;
for (r = 0; r < i.size; r++) {
i.data[r] = 0
}
var o = e.data
, s = t.data
, a = i.data;
if (e == t) {
for (r = 0; r < e.size; r++) {
a[2 * r] += o[r] * o[r]
}
for (r = 1; r < e.size; r++) {
for (n = 0; r > n; n++) {
a[r + n] += 2 * o[r] * o[n]
}
}
} else {
for (r = 0; r < e.size; r++) {
for (n = 0; n < t.size; n++) {
a[r + n] += o[r] * s[n]
}
}
}
return normalizeJSMP(i),
i
}
function normalizeJSMP(e) {
var t, i, r, n, o;
for (r = e.size,
i = 0,
t = 0; r > t; t++) {
n = e.data[t],
n += i,
o = n,
i = Math.floor(n / 65536),
n -= 65536 * i,
e.data[t] = n
}
}
function removeLeadingZeroes(e) {
for (var t = e.size - 1; t > 0 && 0 == e.data[t--]; ) {
e.size--
}
}
function divideMP(e, t) {
var i = e.size
, r = t.size
, n = t.data[r - 1]
, o = t.data[r - 1] + t.data[r - 2] / 65536
, s = new JSMPnumber;
s.size = i - r + 1,
e.data[i] = 0;
for (var a = i - 1; a >= r - 1; a--) {
var l = a - r + 1
, d = Math.floor((65536 * e.data[a + 1] + e.data[a]) / o);
if (d > 0) {
var h = multiplyAndSubtract(e, d, t, l);
for (0 > h && (d--,
multiplyAndSubtract(e, d, t, l)); h > 0 && e.data[a] >= n; ) {
h = multiplyAndSubtract(e, 1, t, l),
h > 0 && d++
}
}
s.data[l] = d
}
removeLeadingZeroes(e);
var u = {
"q": s,
"r": e
};
return u
}
function multiplyAndSubtract(e, t, i, r) {
var n, o = e.data.slice(0), s = 0, a = e.data;
for (n = 0; n < i.size; n++) {
var l = s + i.data[n] * t;
s = l >>> 16,
l -= 65536 * s,
l > a[n + r] ? (a[n + r] += 65536 - l,
s++) : a[n + r] -= l
}
return s > 0 && (a[n + r] -= s),
a[n + r] < 0 ? (e.data = o.slice(0),
-1) : 1
}
function applyPKCSv2Padding(e, t, i) {
var r, n = e.length, o = [218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9], s = t - n - 40 - 2, a = [];
for (r = 0; s > r; r++) {
a[r] = 0
}
a[s] = 1;
var l = o.concat(a, e)
, d = [];
for (r = 0; 20 > r; r++) {
d[r] = Math.floor(256 * Math.random())
}
d = SHA1(d.concat(i));
var h = MGF(d, t - 21)
, u = XORarrays(l, h)
, c = MGF(u, 20)
, p = XORarrays(d, c)
, f = [];
for (f[0] = 0,
f = f.concat(p, u),
r = 0; r < f.length; r++) {
e[r] = f[r]
}
}
function MGF(e, t) {
if (t > 4096) {
return null
}
var i = e.slice(0)
, r = i.length;
i[r++] = 0,
i[r++] = 0,
i[r++] = 0,
i[r] = 0;
for (var n = 0, o = []; o.length < t; ) {
i[r] = n++,
o = o.concat(SHA1(i))
}
return o.slice(0, t)
}
function XORarrays(e, t) {
if (e.length != t.length) {
return null
}
for (var i = [], r = e.length, n = 0; r > n; n++) {
i[n] = e[n] ^ t[n]
}
return i
}
function SHA1(e) {
var t, i = e.slice(0);
PadSHA1Input(i);
var r = {
"A": 1732584193,
"B": 4023233417,
"C": 2562383102,
"D": 271733878,
"E": 3285377520
};
for (t = 0; t < i.length; t += 64) {
SHA1RoundFunction(r, i, t)
}
var n = [];
return wordToBytes(r.A, n, 0),
wordToBytes(r.B, n, 4),
wordToBytes(r.C, n, 8),
wordToBytes(r.D, n, 12),
wordToBytes(r.E, n, 16),
n
}
function wordToBytes(e, t, i) {
var r;
for (r = 3; r >= 0; r--) {
t[i + r] = 255 & e,
e >>>= 8
}
}
function PadSHA1Input(e) {
var t, i = e.length, r = i, n = i % 64, o = 55 > n ? 56 : 120;
for (e[r++] = 128,
t = n + 1; o > t; t++) {
e[r++] = 0
}
var s = 8 * i;
for (t = 1; 8 > t; t++) {
e[r + 8 - t] = 255 & s,
s >>>= 8
}
}
function SHA1RoundFunction(e, t, i) {
var r, n, o, s = 1518500249, a = 1859775393, l = 2400959708, d = 3395469782, h = [], u = e.A, c = e.B, p = e.C, f = e.D, m = e.E;
for (n = 0,
o = i; 16 > n; n++,
o += 4) {
h[n] = t[o] << 24 | t[o + 1] << 16 | t[o + 2] << 8 | t[o + 3] << 0
}
for (n = 16; 80 > n; n++) {
h[n] = rotateLeft(h[n - 3] ^ h[n - 8] ^ h[n - 14] ^ h[n - 16], 1)
}
var g;
for (r = 0; 20 > r; r++) {
g = rotateLeft(u, 5) + (c & p | ~c & f) + m + h[r] + s & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
for (r = 20; 40 > r; r++) {
g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + a & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
for (r = 40; 60 > r; r++) {
g = rotateLeft(u, 5) + (c & p | c & f | p & f) + m + h[r] + l & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
for (r = 60; 80 > r; r++) {
g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + d & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
e.A = e.A + u & 4294967295,
e.B = e.B + c & 4294967295,
e.C = e.C + p & 4294967295,
e.D = e.D + f & 4294967295,
e.E = e.E + m & 4294967295
}
function rotateLeft(e, t) {
var i = e >>> 32 - t
, r = (1 << 32 - t) - 1
, n = e & r;
return n << t | i
}
function hexStringToMP(e) {
var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
for (n.size = r,
t = 0; r > t; t++) {
i = e.substr(4 * t, 4),
n.data[r - 1 - t] = parseInt(i, 16)
}
return n
}
function duplicateMP(e) {
var t = new JSMPnumber;
return t.size = e.size,
t.data = e.data.slice(0),
t
}
function mpToByteArray(e) {
var t = []
, i = 0
, r = e.size;
for (i = 0; r > i; i++) {
t[2 * i] = 255 & e.data[i];
var n = e.data[i] >>> 8;
t[2 * i + 1] = n
}
return t
}
function byteArrayToBase64(e) {
var t, i, r = e.length, n = "";
for (t = r - 3; t >= 0; t -= 3) {
i = e[t] | e[t + 1] << 8 | e[t + 2] << 16,
n += base64Encode(i, 4)
}
var o = r % 3;
for (i = 0,
t += 2; t >= 0; t--) {
i = i << 8 | e[t]
}
return 1 == o ? n = n + base64Encode(i << 16, 2) + "= = " : 2 == o && (n = n + base64Encode(i << 8, 3) + " = "),
n
}
function base64Encode(e, t) {
var i, r = "";
for (i = t; 4 > i; i++) {
e >>= 6
}
for (i = 0; t > i; i++) {
r = mapByteToBase64(63 & e) + r,
e >>= 6
}
return r
}
function mapByteToBase64(e) {
return e >= 0 && 26 > e ? String.fromCharCode(65 + e) : e >= 26 && 52 > e ? String.fromCharCode(97 + e - 26) : e >= 52 && 62 > e ? String.fromCharCode(48 + e - 52) : 62 == e ? "+" : "/"
}
var Key = "e=10001;m=d0fa1d37fa0bb621a8cbb6669249ba1d14bbd5058592f050240d8c3b68674f0e28283018a7753f4377aaa3b3645e5f119a0032129a0a64322f74888aed3519de49e98c5b3c221460218140616f01ac5e9f2f8042e2749b8a89112f15310690dad7531f6758c0c65e525dff7859283b566a5b154352c57161cd24e59133a61432f461583e40cac749d722909dfcf0edd6af3cbc9a25e639b0caaf55e8c7b08b53c7d52038b48e1b26ad40f8bb84b3bb9c92bc9b947d2ab5ae4664a5093a4895af09659a78c9393797ea76b5b9416a45025e2ab3ea1627f08d85abd22e156d3e842efbaa1d0e1e4885028b2bc0aa7be8e444799e96fce0444f2b56bd14c0244b4d"
var randomNum = "AA278C7C00D44877AA95055BB0497A6169195B7B79C664E0AC9DFDCE1112CE28282BAA83D5CD95041CB512CD35624CCD6FD873C98579E4D3C4D25E5E6134F65628BDA9DE9C00A6E53A0194EA7483BCB1AABD8AA983282259E2953CC8705D36BB9936E57E"
console.info(Encrypt(null, "1234", "saproof"))
Example output from above code
V2loAc3ik+qXTJMVF/V/0yKdbQeKGqO7UIA82cqafWUPJ6ALYd/6Bwb9QFsJOEC+yKfwqu5YE36+J3Dpsuk14EQG4YktFVao5D2i5C/2Akm5i1WhPDMbHUFfUJtxbQ3Ldc0jgpMpOrhuEkQ/u9MUgL3l1tL08GTvl4KdnvW2delt1HpuyGFI11WNb5/xXbzmZvRsF2fc3tNpZCvHSTCEQuMOzsQSTeghttXEBFFvmqC9fwH+KjiBGwl11zyH+shv8kYi+LRaqN2LoVz4eFMmWDJ0PNNeA7Aq+mQ/9BIY+tz7Tzz51OSlCgajsPnJbxyHiURdUrc103oKdja8Vd9dnw==
Основная функция здесь Encrypt(). Моя программа написана на Python. Я попытался зашифровать свой OTP с помощью модуля Python rsa, но безрезультатно. API отклонил запрос и пометил зашифрованный OTP как недействительный.
import rsa
import base64
Key = "d0fa1d37fa0bb621a8cbb6669249ba1d14bbd5058592f050240d8c3b68674f0e28283018a7753f4377aaa3b3645e5f119a0032129a0a64322f74888aed3519de49e98c5b3c221460218140616f01ac5e9f2f8042e2749b8a89112f15310690dad7531f6758c0c65e525dff7859283b566a5b154352c57161cd24e59133a61432f461583e40cac749d722909dfcf0edd6af3cbc9a25e639b0caaf55e8c7b08b53c7d52038b48e1b26ad40f8bb84b3bb9c92bc9b947d2ab5ae4664a5093a4895af09659a78c9393797ea76b5b9416a45025e2ab3ea1627f08d85abd22e156d3e842efbaa1d0e1e4885028b2bc0aa7be8e444799e96fce0444f2b56bd14c0244b4d"
encrypted = rsa.encrypt(b'1234', rsa.PublicKey(int(Key, 16), 65537))
b64encrypted = base64.b64encode(encrypted)
print(b64encrypted.decode('utf-8'))
Итак, мой вопрос: как я могу использовать функцию шифрования Microsoft в Python? Нужно ли самому вручную переводить код, или может я что-то не так делаю с модулем rsa?
Спасибо за помощь.
Это официальный код от Microsoft, изначально это было 2K+ строк. Я оставил только те функции, которые использует функция Encrypt. Я не совсем понимаю их код, он, мягко говоря, сложен, но я обновлю свой пост более подробной информацией, которую я собрал в процессе. Спасибо за ваш вклад.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Код JavaScript использует RSA с OAEP в качестве заполнения и SHA-1 для обоих дайджестов (см. applyPKCSv2Padding()). Кроме того, открытый текст кодируется с помощью UTF-16LE (см. PackageSADataForProof()), а зашифрованный текст кодируется с прямым порядком байтов (см. byteArrayToBase64()).
Используемая библиотека RSA не поддерживает OAEP, см. выпуск №68. Возможная библиотека, поддерживающая OAEP, — PyCryptodome, а возможная реализация, функционально идентичная коду JavaScript (строго говоря, это относится только к открытым текстам до 214 байт, более длинные см. в следующем разделе):
from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64
modulusHex = 'd0fa1d37fa0bb621a8cbb6669249ba1d14bbd5058592f050240d8c3b68674f0e28283018a7753f4377aaa3b3645e5f119a0032129a0a64322f74888aed3519de49e98c5b3c221460218140616f01ac5e9f2f8042e2749b8a89112f15310690dad7531f6758c0c65e525dff7859283b566a5b154352c57161cd24e59133a61432f461583e40cac749d722909dfcf0edd6af3cbc9a25e639b0caaf55e8c7b08b53c7d52038b48e1b26ad40f8bb84b3bb9c92bc9b947d2ab5ae4664a5093a4895af09659a78c9393797ea76b5b9416a45025e2ab3ea1627f08d85abd22e156d3e842efbaa1d0e1e4885028b2bc0aa7be8e444799e96fce0444f2b56bd14c0244b4d';
pubExpHex = '010001'
plaintextStr = '1234'
modulus = int(modulusHex, 16)
pubExp = int(pubExpHex, 16)
plaintext = plaintextStr.encode('utf-16le') # encode plaintext with UTF-16LE
publicKey = RSA.construct((modulus, pubExp))
cipher = PKCS1_OAEP.new(publicKey) # apply OAEP as padding
ciphertext = cipher.encrypt(plaintext)
ciphertextReversed = ciphertext[::-1] # encode ciphertext in little endian order
ciphertextReversedB64 = base64.b64encode(ciphertextReversed)
print(ciphertextReversedB64)
Обратите внимание, что OAEP не является детерминированным, т. е. повторное шифрование с одним и тем же ключом и открытым текстом дает разные зашифрованные тексты. Следовательно, реализация не может быть проверена путем сравнения зашифрованных текстов, а только успешной дешифрацией (см. последний раздел).
Открытые тексты размером более 214 байт. Опубликованный код JavaScript реализует довольно необычную функцию: RSA может шифровать только открытые тексты, максимальная длина которых равна длине ключа (здесь 2048 бит = 256 байт) за вычетом пространства, необходимого для заполнения (здесь для OAEP с SHA-1: 42 байта), т.е. максимальная длина здесь 256 - 42 = 214 байт, см. здесь.
Но размещенный здесь код Java Script разбивает открытый текст на блоки по 214 байт (или меньше в случае последнего блока), шифрует каждый блок отдельно и объединяет зашифрованные тексты. Это позволяет шифровать произвольно длинные открытые тексты.
Приведенный выше фрагмент кода Python не учитывает это, так как он не нужен для короткого открытого текста 1234, используемого в примере. Но для открытых текстов размером более 214 байт это необходимо добавить.
Обратите внимание, что обычно длинные открытые тексты шифруются не таким образом, а гибридным шифрованием, в котором, например. RSA сочетается с AES: открытый текст шифруется с помощью AES, ключ AES с помощью RSA.
Тест: приведенный ниже код JavaScript расшифровывает зашифрованный текст, сгенерированный с помощью кода JavaScript, опубликованного в вопросе, и зашифрованный текст, сгенерированный с помощью кода Python, опубликованного в этом ответе.
Результат: оба зашифрованных текста могут быть расшифрованы с помощью одной и той же логики, что показывает, что код шифрования JavaScript и Python функционально идентичен.
Обратите внимание, что для этого теста использовалась новая пара ключей, поскольку в вопросе был опубликован только открытый ключ, и что открытый ключ этой новой пары ключей должен применяться при создании зашифрованных текстов (см. код JavaScript для шифрования), чтобы закрытый ключ, используемый при расшифровке совпадений.
(async () => {
function Encrypt(e, t, i, r) {
var n = [];
switch (i.toLowerCase()) {
case "saproof":
if (null == t) {
return null
}
n = PackageSADataForProof(t);
break;
case "newpwd":
if (null == r) {
return null
}
}
if (null == n || "undefined" == typeof n) {
return n
}
if ("undefined" != typeof Key && void 0 !== parseRSAKeyFromString) {
var o = parseRSAKeyFromString(Key)
}
var s = RSAEncrypt(n, o, randomNum);
return s
}
function PackageSADataForProof(e) {
var t, i = [], r = 0;
for (t = 0; t < e.length; t++) {
i[r++] = 127 & e.charCodeAt(t),
i[r++] = (65280 & e.charCodeAt(t)) >> 8
}
return i
}
function parseRSAKeyFromString(e) {
var t = e.indexOf(";");
if (0 > t) {
return null
}
var i = e.substr(0, t)
, r = e.substr(t + 1)
, n = i.indexOf(" = ");
if (0 > n) {
return null
}
var o = i.substr(n + 1);
if (n = r.indexOf(" = "),
0 > n) {
return null
}
var s = r.substr(n + 1)
, a = new Object;
return a.n = hexStringToMP(s),
a.e = parseInt(o, 16),
a
}
function hexStringToMP(e) {
var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
for (n.size = r,
t = 0; r > t; t++) {
i = e.substr(4 * t, 4),
n.data[r - 1 - t] = parseInt(i, 16)
}
return n
}
function RSAEncrypt(e, t) {
for (var i = [], r = 42, n = 2 * t.n.size - r, o = 0; o < e.length; o += n) {
if (o + n >= e.length) {
var s = RSAEncryptBlock(e.slice(o), t, randomNum);
s && (i = s.concat(i))
} else {
var s = RSAEncryptBlock(e.slice(o, o + n), t, randomNum);
s && (i = s.concat(i))
}
}
var a = byteArrayToBase64(i);
return a
}
function RSAEncryptBlock(e, t, i) {
var r = t.n
, n = t.e
, o = e.length
, s = 2 * r.size
, a = 42;
if (o + a > s) {
return null
}
applyPKCSv2Padding(e, s, i),
e = e.reverse();
var l = byteArrayToMP(e)
, d = modularExp(l, n, r);
d.size = r.size;
var h = mpToByteArray(d);
return h = h.reverse()
}
function JSMPnumber() {
this.size = 1,
this.data = [],
this.data[0] = 0
}
function byteArrayToMP(e) {
var t = new JSMPnumber
, i = 0
, r = e.length
, n = r >> 1;
for (i = 0; n > i; i++) {
t.data[i] = e[2 * i] + (e[1 + 2 * i] << 8)
}
return r % 2 && (t.data[i++] = e[r - 1]),
t.size = i,
t
}
function modularExp(e, t, i) {
for (var r = [], n = 0; t > 0; ) {
r[n] = 1 & t,
t >>>= 1,
n++
}
for (var o = duplicateMP(e), s = n - 2; s >= 0; s--) {
o = modularMultiply(o, o, i),
1 == r[s] && (o = modularMultiply(o, e, i))
}
return o
}
function modularMultiply(e, t, i) {
var r = multiplyMP(e, t)
, n = divideMP(r, i);
return n.r
}
function multiplyMP(e, t) {
var i = new JSMPnumber;
i.size = e.size + t.size;
var r, n;
for (r = 0; r < i.size; r++) {
i.data[r] = 0
}
var o = e.data
, s = t.data
, a = i.data;
if (e == t) {
for (r = 0; r < e.size; r++) {
a[2 * r] += o[r] * o[r]
}
for (r = 1; r < e.size; r++) {
for (n = 0; r > n; n++) {
a[r + n] += 2 * o[r] * o[n]
}
}
} else {
for (r = 0; r < e.size; r++) {
for (n = 0; n < t.size; n++) {
a[r + n] += o[r] * s[n]
}
}
}
return normalizeJSMP(i),
i
}
function normalizeJSMP(e) {
var t, i, r, n, o;
for (r = e.size,
i = 0,
t = 0; r > t; t++) {
n = e.data[t],
n += i,
o = n,
i = Math.floor(n / 65536),
n -= 65536 * i,
e.data[t] = n
}
}
function removeLeadingZeroes(e) {
for (var t = e.size - 1; t > 0 && 0 == e.data[t--]; ) {
e.size--
}
}
function divideMP(e, t) {
var i = e.size
, r = t.size
, n = t.data[r - 1]
, o = t.data[r - 1] + t.data[r - 2] / 65536
, s = new JSMPnumber;
s.size = i - r + 1,
e.data[i] = 0;
for (var a = i - 1; a >= r - 1; a--) {
var l = a - r + 1
, d = Math.floor((65536 * e.data[a + 1] + e.data[a]) / o);
if (d > 0) {
var h = multiplyAndSubtract(e, d, t, l);
for (0 > h && (d--,
multiplyAndSubtract(e, d, t, l)); h > 0 && e.data[a] >= n; ) {
h = multiplyAndSubtract(e, 1, t, l),
h > 0 && d++
}
}
s.data[l] = d
}
removeLeadingZeroes(e);
var u = {
"q": s,
"r": e
};
return u
}
function multiplyAndSubtract(e, t, i, r) {
var n, o = e.data.slice(0), s = 0, a = e.data;
for (n = 0; n < i.size; n++) {
var l = s + i.data[n] * t;
s = l >>> 16,
l -= 65536 * s,
l > a[n + r] ? (a[n + r] += 65536 - l,
s++) : a[n + r] -= l
}
return s > 0 && (a[n + r] -= s),
a[n + r] < 0 ? (e.data = o.slice(0),
-1) : 1
}
function applyPKCSv2Padding(e, t, i) {
var r, n = e.length, o = [218, 57, 163, 238, 94, 107, 75, 13, 50, 85, 191, 239, 149, 96, 24, 144, 175, 216, 7, 9], s = t - n - 40 - 2, a = [];
for (r = 0; s > r; r++) {
a[r] = 0
}
a[s] = 1;
var l = o.concat(a, e)
, d = [];
for (r = 0; 20 > r; r++) {
d[r] = Math.floor(256 * Math.random())
}
d = SHA1(d.concat(i));
var h = MGF(d, t - 21)
, u = XORarrays(l, h)
, c = MGF(u, 20)
, p = XORarrays(d, c)
, f = [];
for (f[0] = 0,
f = f.concat(p, u),
r = 0; r < f.length; r++) {
e[r] = f[r]
}
}
function MGF(e, t) {
if (t > 4096) {
return null
}
var i = e.slice(0)
, r = i.length;
i[r++] = 0,
i[r++] = 0,
i[r++] = 0,
i[r] = 0;
for (var n = 0, o = []; o.length < t; ) {
i[r] = n++,
o = o.concat(SHA1(i))
}
return o.slice(0, t)
}
function XORarrays(e, t) {
if (e.length != t.length) {
return null
}
for (var i = [], r = e.length, n = 0; r > n; n++) {
i[n] = e[n] ^ t[n]
}
return i
}
function SHA1(e) {
var t, i = e.slice(0);
PadSHA1Input(i);
var r = {
"A": 1732584193,
"B": 4023233417,
"C": 2562383102,
"D": 271733878,
"E": 3285377520
};
for (t = 0; t < i.length; t += 64) {
SHA1RoundFunction(r, i, t)
}
var n = [];
return wordToBytes(r.A, n, 0),
wordToBytes(r.B, n, 4),
wordToBytes(r.C, n, 8),
wordToBytes(r.D, n, 12),
wordToBytes(r.E, n, 16),
n
}
function wordToBytes(e, t, i) {
var r;
for (r = 3; r >= 0; r--) {
t[i + r] = 255 & e,
e >>>= 8
}
}
function PadSHA1Input(e) {
var t, i = e.length, r = i, n = i % 64, o = 55 > n ? 56 : 120;
for (e[r++] = 128,
t = n + 1; o > t; t++) {
e[r++] = 0
}
var s = 8 * i;
for (t = 1; 8 > t; t++) {
e[r + 8 - t] = 255 & s,
s >>>= 8
}
}
function SHA1RoundFunction(e, t, i) {
var r, n, o, s = 1518500249, a = 1859775393, l = 2400959708, d = 3395469782, h = [], u = e.A, c = e.B, p = e.C, f = e.D, m = e.E;
for (n = 0,
o = i; 16 > n; n++,
o += 4) {
h[n] = t[o] << 24 | t[o + 1] << 16 | t[o + 2] << 8 | t[o + 3] << 0
}
for (n = 16; 80 > n; n++) {
h[n] = rotateLeft(h[n - 3] ^ h[n - 8] ^ h[n - 14] ^ h[n - 16], 1)
}
var g;
for (r = 0; 20 > r; r++) {
g = rotateLeft(u, 5) + (c & p | ~c & f) + m + h[r] + s & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
for (r = 20; 40 > r; r++) {
g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + a & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
for (r = 40; 60 > r; r++) {
g = rotateLeft(u, 5) + (c & p | c & f | p & f) + m + h[r] + l & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
for (r = 60; 80 > r; r++) {
g = rotateLeft(u, 5) + (c ^ p ^ f) + m + h[r] + d & 4294967295,
m = f,
f = p,
p = rotateLeft(c, 30),
c = u,
u = g
}
e.A = e.A + u & 4294967295,
e.B = e.B + c & 4294967295,
e.C = e.C + p & 4294967295,
e.D = e.D + f & 4294967295,
e.E = e.E + m & 4294967295
}
function rotateLeft(e, t) {
var i = e >>> 32 - t
, r = (1 << 32 - t) - 1
, n = e & r;
return n << t | i
}
function hexStringToMP(e) {
var t, i, r = Math.ceil(e.length / 4), n = new JSMPnumber;
for (n.size = r,
t = 0; r > t; t++) {
i = e.substr(4 * t, 4),
n.data[r - 1 - t] = parseInt(i, 16)
}
return n
}
function duplicateMP(e) {
var t = new JSMPnumber;
return t.size = e.size,
t.data = e.data.slice(0),
t
}
function mpToByteArray(e) {
var t = []
, i = 0
, r = e.size;
for (i = 0; r > i; i++) {
t[2 * i] = 255 & e.data[i];
var n = e.data[i] >>> 8;
t[2 * i + 1] = n
}
return t
}
function byteArrayToBase64(e) {
var t, i, r = e.length, n = "";
for (t = r - 3; t >= 0; t -= 3) {
i = e[t] | e[t + 1] << 8 | e[t + 2] << 16,
n += base64Encode(i, 4)
}
var o = r % 3;
for (i = 0,
t += 2; t >= 0; t--) {
i = i << 8 | e[t]
}
return 1 == o ? n = n + base64Encode(i << 16, 2) + "= = " : 2 == o && (n = n + base64Encode(i << 8, 3) + " = "),
n
}
function base64Encode(e, t) {
var i, r = "";
for (i = t; 4 > i; i++) {
e >>= 6
}
for (i = 0; t > i; i++) {
r = mapByteToBase64(63 & e) + r,
e >>= 6
}
return r
}
function mapByteToBase64(e) {
return e >= 0 && 26 > e ? String.fromCharCode(65 + e) : e >= 26 && 52 > e ? String.fromCharCode(97 + e - 26) : e >= 52 && 62 > e ? String.fromCharCode(48 + e - 52) : 62 == e ? "+" : "/"
}
var Key = "e=10001;m=ba71796836ba1c27cb30c23f3192d3e610e4df4e0253eca914c162063a3041d50c40a60767e7e4941ec6a4bc426adc5827f859eeb8df1c7dca96adb4fd8d446d80d1a29c9349f92b4f33f3c2c5f852bfe5db90ad75095d847093320c1ffc4bff6ee04da1b7f375913e0e176fe654302ad8c8e379432af022e6411eb764c5c1f25c26b06244978470b7d58164d7191427ee09e058aca452ee6b4bcdde014d711fd14ba2341fca26bcf7f13292238eeffdd45b077e3004d9f472e34b1dbf2dfde0da3afcd5e89f12660396b8bf399a783a166efb4068d4b5e5a024defaf9884366025fb11ab543aec9032dd95c31e2e57e77f0370cfe8fb4c1812a917c4ade221b"
var randomNum = "AA278C7C00D44877AA95055BB0497A6169195B7B79C664E0AC9DFDCE1112CE28282BAA83D5CD95041CB512CD35624CCD6FD873C98579E4D3C4D25E5E6134F65628BDA9DE9C00A6E53A0194EA7483BCB1AABD8AA983282259E2953CC8705D36BB9936E57E"
var ciphertextB64FromJS = Encrypt(null, "1234", "saproof");
document.getElementById("js_enc").innerHTML = "JavaScript code (encryption): " + ciphertextB64FromJS;
// Decryption ======================================================================================================================================================
// Decryption of a ciphertext from the JavaScript Code
var decryptedCiphertextFromJS = await decrypt(ciphertextB64FromJS);
document.getElementById("js_dec").innerHTML = "JavaScript code (decryption): " + decryptedCiphertextFromJS;
// Decryption of a ciphertext from the Python code for encryption
var ciphertextB64FromPy = 'iQefWJtEXRjMpJj59NmODmDVuJJkmsSV2pNvPrAX74lDWiXHjY4H34XAEGHY5bJ/3xJ3f7pM1R0Nb8cBJSgQMpCO/0PCdDSbalw7M/cbEEOzrIVMxg8NXOsUM6tqetaroSAutyesg8+EsP4liow3ssV6I7cX/QDpunFV0vRHxTM9Am35QNrNfpvZMu4kV642dq9ocSJLdbaBfaKXUyBA6nYzUIhq3nHx8XzPxo9DnAoE6qkGeRpQzV7Mo+jeY26YOP/DyVmb0JuOXPI8Uz/4yxhQp7ygAVOF5CNojZN0XtZVqORV4+2bFEMD+Fi9YIIFFMrym169ACoy2faTfLYjlw=='; // MAKE SURE TO USE THE CORRECT PUBLIC KEY DURING ENCRYPTION!!!: modulus = 0xba717968..., public exponent = 0x10001 (s. above)
var decryptedCiphertextFromPy = await decrypt(ciphertextB64FromPy);
document.getElementById("py_dec").innerHTML = "Python code (decryption): " + decryptedCiphertextFromPy;
async function decrypt(ciphertextBase64){
var pkcs8Der = b642ab('MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC6cXloNrocJ8swwj8xktPmEOTfTgJT7KkUwWIGOjBB1QxApgdn5+SUHsakvEJq3Fgn+FnuuN8cfcqWrbT9jURtgNGinJNJ+StPM/PCxfhSv+XbkK11CV2EcJMyDB/8S/9u4E2ht/N1kT4OF2/mVDAq2MjjeUMq8CLmQR63ZMXB8lwmsGJEl4Rwt9WBZNcZFCfuCeBYrKRS7mtLzd4BTXEf0UuiNB/KJrz38TKSI47v/dRbB34wBNn0cuNLHb8t/eDaOvzV6J8SZgOWuL85mng6Fm77QGjUteWgJN76+YhDZgJfsRq1Q67JAy3ZXDHi5X538DcM/o+0wYEqkXxK3iIbAgMBAAECggEASlJj0ExIomKmmBhG8q8SM1s2sWG6gdQMjs6MEeluRT/1c2v79cq2Dum5y/+UBl8x8TUKPKSLpCLs+GXkiVKgHXrFlqoN+OYQArG2EUWzuODwczdYPhhupBXwR3oX4g41k/BsYfQfZBVzBFEJdWrIDLyAUFWNlfdGIj2BTiAoySfyqmamvmW8bsvc8coiGlZ28UC85/Xqx9wOzjeGoRkCH7PcTMlc9F7SxSthwX/k1VBXmNOHa+HzGOgO/W3k1LDqJbq2wKjZTW3iVEg2VodjxgBLMm0MueSGoI6IuaZSPMyFEM3gGvC2+cDBI2SL/amhiTUa/VDlTVw/IKbSuar9uQKBgQDd76M0Po5Lqh8ZhQ3obhFqkfO5EBXy7HUL15cw51kVtwF6Gf/J2HNHjwsg9Nb0eJETTS6bbuVd9bn884JoRS986nVTFNZ4dnjEgKjjQ8GjfzdkpbUxsRLWiIxuOQSpIUZGdMi2ctTTtspvMsDsjRRYdYIQCe/SDsdHGT3vcUCybwKBgQDXDz6iVnY84Fh5iDDVrQOR4lYoxCL/ikCDJjC6y1mjR0eVFdBPQ4j1dDSPU9lahBLby0VyagQCDp/kxQOl0z2zBLRI4I8jUtz9/9KW6ze7U7dQJ7OTfumd5I97OyQOG9XZwKUkRgfyb/PAMBSUSLgosi38f+OC3IN3qlvHFzvxFQKBgQCITpUDEmSczih5qQGIvolN1cRF5j5Ey7t7gXbnXz+Umah7kJpMIvdyfMVOAXJABgi8PQwiBLM0ySXo2LpARjXLV8ilNUggBktYDNktc8DrJMgltayaj3HNd2IglD5rjfc2cKWRgOd7/GlKcHaTEnbreYhfR2sWrWLxJOyoMfuVWwKBgFalCbMV6qU0LfEo8aPlBN8ttVDPVNpntP4h0NgxPXgPK8Pg+gA1UWSy4MouGg/hzkdHaj9ifyLlCX598a5JoT4S0x/ZeVHd/LNI8mtjcRzD6cMde7gdFbpLb5NSjIAyrsIAX4hxvpnqiOYRePkVIz0iLGziiaMbfMwlkrxvm/LRAoGBALPRbtSbE2pPgvOHKHTGPr7gKbmsWVbOcQA8rG801T38W/UPe1XtynMEjzzQ29OaVeQwvUN9+DxFXJ6Yvwj6ih4Wdq109i7Oo1fDnMczOQN9DKch2eNAHrNSOMyLDCBm++wbyHAsS2T0VO8+gzLABviZm5AFCQWfke4LZo5mOS10');
var privateKey = await window.crypto.subtle.importKey("pkcs8", pkcs8Der, {name: "RSA-OAEP", hash: "SHA-1"}, true, ["decrypt"]);
var ciphertext = b642ab(ciphertextBase64);
ciphertext = ciphertext.reverse(); // reverse little endian order
var decrypted = await window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, ciphertext); // apply OAEP as padding
return decodeUTF16LE(decrypted); // decode with UTF-16LE
}
// Helper
function b642ab(base64_string){
return Uint8Array.from(window.atob(base64_string), c => c.charCodeAt(0));
}
// https://stackoverflow.com/a/14601808
function decodeUTF16LE(buf) {
var cp = [];
var binaryStr = String.fromCharCode.apply(null, new Uint8Array(buf));
for( var i = 0; i < binaryStr.length; i+=2) {
cp.push(
binaryStr.charCodeAt(i) |
( binaryStr.charCodeAt(i+1) << 8 )
);
}
return String.fromCharCode.apply( String, cp );
}
})();<p style = "font-family:'Courier New', monospace;" id = "js_enc"></p>
<p style = "font-family:'Courier New', monospace;" id = "js_dec"></p>
<p style = "font-family:'Courier New', monospace;" id = "py_dec"></p>Я глубоко извиняюсь за задержку, я как-то совсем пропустил ваш ответ. Я только что протестировал его, и он отлично работает, и я хотел бы оставить вам отзыв о вашей феноменальной помощи. Вы случайно не пользуетесь криптовалютой? Если да, пожалуйста, оставьте свой адрес кошелька BTC или ETH в комментарии. Еще раз спасибо за помощь, это очень важно для меня. @Топако
Я буду краток и понятен... за которым следуют 425 строк плотного, некомментарного кода javascript с переменной из одной буквы. Для меня это слишком много кода для обратного проектирования. Я бы порекомендовал вам найти способ сделать этот пример более минимальным и воспроизводимым.