Шифрування пароля за допомогою відкритого ключа RSA
Приклад для Postman (js)
Функції для шифрування
/**
* Encrypt data with RSA public key
* @param publicKey - get from server
* @param password - user key password
*/
utils = {
rsaEncrypt: async function(publicKey, password) {
const cryptoKey = await crypto.subtle.importKey("spki", publicKey, {
name: "RSA-OAEP",
hash: "SHA-256"
}, true, ["encrypt"]);
const encodedText = new TextEncoder().encode(password);
const encrypted = await crypto.subtle.encrypt({name: "RSA-OAEP"}, cryptoKey, encodedText);
//
return toBase64(encrypted);
}
}
function toBase64(buffer) {
const bytes = new Uint8Array(buffer);
let binary = '';
for (let i = 0; i < bytes.byteLength; i++) {
binary += String.fromCharCode(bytes[i]);
}
return btoa(binary);
}
Шифрування пароля
const encrypted = await utils.rsaEncrypt(Uint8Array.from(publicKey), password));
де,
- publicKey - публічний ключ, отриманий методом GET /api/external/key
- password - пароль від ключа
Приклад Java
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
String pem = """
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB...
-----END PUBLIC KEY-----
""";
String body = pem
.replace("-----BEGIN PUBLIC KEY-----", "")
.replace("-----END PUBLIC KEY-----", "")
.replaceAll("\\s", "");
byte[] der = Base64.getDecoder().decode(body);
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(der));
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-256",
"MGF1",
MGF1ParameterSpec.SHA256,
PSource.PSpecified.DEFAULT
);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPPadding");
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
byte[] encrypted = cipher.doFinal("my-secret-password".getBytes(StandardCharsets.UTF_8));
String encryptedBase64 = Base64.getEncoder().encodeToString(encrypted);
Приклад Node.js
import crypto from "node:crypto";
const publicKeyPem = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB...
-----END PUBLIC KEY-----`;
const encrypted = crypto.publicEncrypt(
{
key: publicKeyPem,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256"
},
Buffer.from("my-secret-password", "utf8")
);
const encryptedBase64 = encrypted.toString("base64");
Як шифрувати?
- Отримати публічний ключ через
/api/external/key?type=pem. - Імпортувати public key у crypto-бібліотеку як RSA public key.
- Зашифрувати plaintext пароль bytes.
- Результат RSA encryption закодувати в base64.
- Передати base64 string у відповідне поле запиту.
plaintext password
-> UTF-8 bytes
-> RSA encrypt with server public key
-> base64
-> JSON field
алгоритм шифрування має відповідати вимогам API: RSA/ECB/OAEPPadding з OAEP SHA-256 і MGF1 SHA-256;
- не використовуйте RSA_PKCS1_PADDING / RSA/ECB/PKCS1Padding — сервер не зможе розшифрувати такі дані;
- RSA OAEP має обмеження на довжину plaintext, яке залежить від розміру ключа; передавайте короткі секрети/паролі, а не великі JSON/файли;
- публічний ключ має TTL, тому його треба оновлювати після expiredAt / x-key-ttl;
- не кешуйте ключ назавжди;
- якщо сервер повертає decrypt_error для caPassPhrase або pkPassword, найчастіші причини:
- використаний застарілий public key;
- зашифровано не тим padding/OAEP hash;
- у JSON переданий не base64 від encrypted bytes;
- plaintext був зашифрований іншим ключем.