RSA опирается на свойства целых чисел в модульной арифметике и сложности разложения больших чисел на простые множители. В основе лежит факт: перемножить два больших простых числа легко, а вот восстановить их исходные множители из произведения — вычислительно крайне трудно.
Пусть выбраны два простых числа:
Их произведение образует модуль:
Это число ( n ) становится частью открытого и закрытого ключа.
Для дальнейших вычислений используется функция Эйлера:
Она определяет количество чисел, взаимно простых с ( n ).
Процесс построения ключевой пары основан на выборе двух экспонент: открытой и закрытой.
Выбирается число ( e ), удовлетворяющее условиям:
На практике часто используется значение 65537 — оно обеспечивает баланс между безопасностью и производительностью.
Закрытый ключ ( d ) вычисляется как обратное значение к ( e ) по модулю ( (n) ):
Это означает:
Таким образом формируется пара:
Все операции выполняются по модулю ( n ). Это ключевой механизм безопасности.
Основная операция:
означает, что ( a ) и ( b ) дают одинаковый остаток при делении на ( n ).
Сообщение ( m ), представленное числом, преобразуется в шифротекст:
Шифротекст возвращается в исходное сообщение:
Свойство корректности RSA основано на теореме Эйлера и структуре мультипликативной группы по модулю ( n ).
RSA не работает напрямую со строками. Любые данные сначала преобразуются в число.
В реальных системах применяются:
Без паддинга RSA уязвим к атакам на детерминированное шифрование.
Библиотека Jsrsasign реализует RSA через объект RSAKey,
предоставляя низкоуровневый доступ к ключам и операциям.
const rsa = new RSAKey();
rsa.generate(2048, "10001"); // 65537 в hex
Здесь:
2048 — длина ключа в битах"10001" — открытая экспонента в hexRSA-ключи обычно представлены в формате PEM.
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
const rsa = KEYUTIL.getKey(pemPrivateKey);
или для публичного:
const pub = KEYUTIL.getKey(pemPublicKey);
Jsrsasign поддерживает шифрование строк через RSAKey.
const rsa = KEYUTIL.getKey(publicKeyPem);
const encrypted = rsa.encrypt("Hello");
Результат обычно кодируется в hex или base64.
const rsa = KEYUTIL.getKey(privateKeyPem);
const decrypted = rsa.decrypt(encryptedHex);
RSA в Jsrsasign использует большие числа через собственную реализацию BigInteger.
Основные операции:
Ключевая операция:
Вместо прямого вычисления используется метод:
Он снижает сложность с линейной до логарифмической.
Для вычисления ( d ) применяется расширенный алгоритм Евклида:
Нужно найти такие ( x ) и ( y ), что:
Если ( (a, b) = 1 ), то:
В RSA это используется для вычисления:
const rsa = new RSAKey();
rsa.generate(1024, "10001");
const publicKey = KEYUTIL.getKey(rsa.getPublicPEM());
const privateKey = KEYUTIL.getKey(rsa.getPrivatePEM());
const message = "test message";
const encrypted = publicKey.encrypt(message);
const decrypted = privateKey.decrypt(encrypted);
Корректность опирается на следующее свойство:
Раскрытие степени даёт:
Так как:
то существует целое ( k ), что:
Следовательно:
По теореме Эйлера:
И всё выражение сводится к исходному ( m ).
RSA-ключ включает:
модуль ( n )
публичную экспоненту ( e )
приватную экспоненту ( d )
дополнительные параметры для оптимизации:
Эти значения ускоряют операции через китайскую теорему об остатках (CRT).
Вместо вычисления:
используются:
После этого результаты объединяются в исходное сообщение.
Это ускоряет RSA примерно в 3–4 раза.
Безопасность RSA основана на задаче факторизации:
Для больших ключей (2048+ бит) эта задача не имеет известного эффективного решения на классических компьютерах.
Алгоритм плохо подходит для больших данных:
Поэтому RSA обычно используется только для:
RSA используется не только для шифрования, но и для подписи:
где ( H(m) ) — хэш сообщения.
const rsa = KEYUTIL.getKey(privateKeyPem);
const signature = rsa.signString("message", "sha256");
Проверка:
const rsa = KEYUTIL.getKey(publicKeyPem);
const isValid = rsa.verifyString("message", signature);