Я вижу следующую ошибку при расшифровке части cipherText JWE. CipherText (alg: aes-256-gcm) создается с использованием узла Node Jose, и я пытаюсь расшифровать с помощью шифрования Node.
Error: Unsupported state or unable to authenticate data
at Decipheriv.final (node:internal/crypto/cipher:193:29)
at Object.<anonymous> (/index.js:100:24)
at Module._compile (node:internal/modules/cjs/loader:1103:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
at Module.load (node:internal/modules/cjs/loader:981:32)
at Function.Module._load (node:internal/modules/cjs/loader:822:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:77:12)
at node:internal/main/run_main_module:17:47
Узел jose encrypt decrypt: работает нормально
const jose = require('node-jose');
(async () => {
let keypair = {
"p": "wP8U4hF_Dyj-Z-YY_89gmT1ngnKER_0e_WTxOS5SWQp-giImmsCEVpP9mqKrwHBrkPJenFwa8kIxNgmJtGSuXkNMd8NoHdqzwtq50-mMCV1oDAIjXl0UUX8_CdVyy9TFe3-P5lJuQoOIRlCs_hvVJ_4x3fT9Xipo85OMoPTmwoM",
"kty": "RSA",
"q": "uk0U0Wx9GfpyLLJhJSJ1gyx5M31kAP9vMGQLrp0P40g-BOl7YYusv8H1EVcscZQuMrNBQvlIyJRTx229r_Wz961YqQ6fw-hJetQQ08CiLj8AuYMm6KHNbEyhzFV8lFyIZCwsDXbA-Ek6F5FLfOqfeFHjo0fOq77R7kcp8cepcP0",
"d": "bd1sMKhIu3QDbEvzrb8cj6f0egUQQU0S1M53okm9ByBWYOFCNPayoluvO9BWBBY2kMsIWQ64q7-sQgjc894yaffppF4Cz0jFCzIIIgoWmT4L4syQkdiiV7xoIrbKvZkloH7bSATf6QioxkcX-9Dz4Z5VHUg0GKzFkK74a655odc-VzlDp7cbrfeL29ETlbb9864IJIoC_hAhzfaVOHCMb8y5Rr5UTgGjf1QlYjSjMhE5L0BOty4QeyxDoy_dI7sosJPj9tVFQCa6yDJaOAFF_h3QTLyhXU1qL6GJbG_JklsTn5z93Vy8haVyf3FvHDW2PVkKy46HRTINpB72QWkV-Q",
"e": "AQAB",
"use": "enc",
"kid": "tester",
"qi": "tItxE9gwrpmifSWur8DdsaMZN7rylhhR18PIgtZaHhw58i9EBRpzW_CMZIHuzl8-ujh8ZsuUsmU2HbYX0VWZnPty3z_-hCWV1DjoElQ93WThcQa0HviIVSxbXEtYoLoNM_gzIFjihICGCtXhlF9Di3C4FKcCP0dYzYWpVFq4uac",
"dp": "M8j-IH7TWg0E3noWQSWy5MteJ9l0dyCLHTDlrRMp02yGb4KcWy_HErgY91IoxbUkl7sA-fGY5WIvdDFw-q99PhvOu9_54vDZBTLNY_gptCWVEovMU7ikCA4dqxTT_a904eNjiEib_0rt2PgywuhS9K03Ujg3d_nnOVxhAptUA-M",
"alg": "RSA-OAEP-256",
"dq": "NsOr5_gNOlK9t1fkaKcdhibPpgwpFoX_6Giwam7vGa_F02nTBBSr_l6ErMlEXkrh3bOF7qsa8yNvEUO4K_59HcSOOHv9CPjCiOHH5IdO5WtNyjq8eEv_9-L6-Pb0PSSKT3AQrxCGnzXfZsgmOZ06rYLc-MWGAkSAr5upv9Iig_0",
"n": "jHNxl1hhNxvYEn5PPObRia7LM6_koGcrcHLgWVVc-zU5loWn33xdd3R3EPs10ZwrhRBmXthN1WLFB0V4w-1QrGSM5wuBm2AqIFglDaYWW7d_aFCYMubCC6YiKYgrXezZtjngGtjBJPNwov4PC6KJgh7xwtqt5MTXX7TH8H6BhvQvNiD_IEH_vxF9hEhN-f5wKR6yNGlCT3X0NWwUiavG0vgtW0y1g6BHUskA1HogdrpURAfmcSSrDya9IoYjlAmUmql-0JGeEJIU53hoDB0ZVNREZbhJxZLN1hV8KrYeVPjfHCjXaJ6-Fw9MvnC9m-FGiX9dvE1A-Yp5sowUOqKLdw"
}
keypairob = await jose.JWK.asKey(JSON.stringify(keypair));
const contentAlg = "A256GCM";
const payload = JSON.stringify({test: 'test'});
const options = {
compact: true,
contentAlg: contentAlg,
protect: Object.keys({
alg: keypairob.alg,
kid: keypairob.kid,
enc: contentAlg
}),
fields: {
alg: keypairob.alg,
kid: keypairob.kid,
enc: contentAlg
}
};
const enc = await jose.JWE.createEncrypt(options, keypairob).update(payload, "utf8").final();
console.info("encrypted payload node jose : "+enc);
const dec = await jose.JWE.createDecrypt(keypairob,options).decrypt(enc)
let decryptJSON = JSON.stringify(dec);
let originalInput = Buffer.from(dec.plaintext).toString("utf-8");
console.info("decrypted payload node jose :"+originalInput);
})();
Вывод консоли:
узел зашифрованной полезной нагрузки jose: eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJraWQiOiJ0ZXN0ZXIiLCJlbmMiOiJBMjU2R0NNIn0.Dw_EDEJnahKdk8mwlY3juSWd4jTIM8Go8NdQLpLWeH2kCmDHNg0RZKvbINEc3jF 70bPECCzuI_fH3ovlUeQ_o7KqbF_5ZMiyOIYQvfsVlgSZpl1YSRHgoX1QaRg_FvOvUile 6xGJ_nRnLSk1VjdTWJFARu-IGEfed0aQ1mCsh5Hq9e2q55iQGlMUQVIHQ7X6ZzXNHu4QW7 jSMYLskP7Vg-EjQizrGvHC04S2SsglZ0znm5eyz0KR71zcdkR2O80_v6u2GM6APMCQrJ3 GQ7SSsguwEKMxxvCpQvZ65lCV4bujvP7tD4A8pn_SCKTjFGU8MulGzHi2z-K4EZU6yZXFQ .DOkezaeJUsRg6h8q.oXC0AtIkn53UgjF2KdxW.1NBMp9IdXW-U5emoiIrE7A
расшифрованный узел полезной нагрузки Хосе :{"test":"test"}
Крипто-расшифровка узла:
const crypto = require('crypto');
const testJwk = {
"p": "wP8U4hF_Dyj-Z-YY_89gmT1ngnKER_0e_WTxOS5SWQp-giImmsCEVpP9mqKrwHBrkPJenFwa8kIxNgmJtGSuXkNMd8NoHdqzwtq50-mMCV1oDAIjXl0UUX8_CdVyy9TFe3-P5lJuQoOIRlCs_hvVJ_4x3fT9Xipo85OMoPTmwoM",
"kty": "RSA",
"q": "uk0U0Wx9GfpyLLJhJSJ1gyx5M31kAP9vMGQLrp0P40g-BOl7YYusv8H1EVcscZQuMrNBQvlIyJRTx229r_Wz961YqQ6fw-hJetQQ08CiLj8AuYMm6KHNbEyhzFV8lFyIZCwsDXbA-Ek6F5FLfOqfeFHjo0fOq77R7kcp8cepcP0",
"d": "bd1sMKhIu3QDbEvzrb8cj6f0egUQQU0S1M53okm9ByBWYOFCNPayoluvO9BWBBY2kMsIWQ64q7-sQgjc894yaffppF4Cz0jFCzIIIgoWmT4L4syQkdiiV7xoIrbKvZkloH7bSATf6QioxkcX-9Dz4Z5VHUg0GKzFkK74a655odc-VzlDp7cbrfeL29ETlbb9864IJIoC_hAhzfaVOHCMb8y5Rr5UTgGjf1QlYjSjMhE5L0BOty4QeyxDoy_dI7sosJPj9tVFQCa6yDJaOAFF_h3QTLyhXU1qL6GJbG_JklsTn5z93Vy8haVyf3FvHDW2PVkKy46HRTINpB72QWkV-Q",
"e": "AQAB",
"use": "enc",
"kid": "tester",
"qi": "tItxE9gwrpmifSWur8DdsaMZN7rylhhR18PIgtZaHhw58i9EBRpzW_CMZIHuzl8-ujh8ZsuUsmU2HbYX0VWZnPty3z_-hCWV1DjoElQ93WThcQa0HviIVSxbXEtYoLoNM_gzIFjihICGCtXhlF9Di3C4FKcCP0dYzYWpVFq4uac",
"dp": "M8j-IH7TWg0E3noWQSWy5MteJ9l0dyCLHTDlrRMp02yGb4KcWy_HErgY91IoxbUkl7sA-fGY5WIvdDFw-q99PhvOu9_54vDZBTLNY_gptCWVEovMU7ikCA4dqxTT_a904eNjiEib_0rt2PgywuhS9K03Ujg3d_nnOVxhAptUA-M",
"alg": "RSA-OAEP-256",
"dq": "NsOr5_gNOlK9t1fkaKcdhibPpgwpFoX_6Giwam7vGa_F02nTBBSr_l6ErMlEXkrh3bOF7qsa8yNvEUO4K_59HcSOOHv9CPjCiOHH5IdO5WtNyjq8eEv_9-L6-Pb0PSSKT3AQrxCGnzXfZsgmOZ06rYLc-MWGAkSAr5upv9Iig_0",
"n": "jHNxl1hhNxvYEn5PPObRia7LM6_koGcrcHLgWVVc-zU5loWn33xdd3R3EPs10ZwrhRBmXthN1WLFB0V4w-1QrGSM5wuBm2AqIFglDaYWW7d_aFCYMubCC6YiKYgrXezZtjngGtjBJPNwov4PC6KJgh7xwtqt5MTXX7TH8H6BhvQvNiD_IEH_vxF9hEhN-f5wKR6yNGlCT3X0NWwUiavG0vgtW0y1g6BHUskA1HogdrpURAfmcSSrDya9IoYjlAmUmql-0JGeEJIU53hoDB0ZVNREZbhJxZLN1hV8KrYeVPjfHCjXaJ6-Fw9MvnC9m-FGiX9dvE1A-Yp5sowUOqKLdw"
}
// node crypto generated JWE ciphertext part - decrypt with node crypto code : success
const nodeCryptoEncPayload = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2R0NNIiwia2lkIjoidGVzdGVyIn0.FHEgUliMX84RcU_Vh50VsyvpradjaUclFyXpyTikqx6bhsBJ3KCUiE34plNNtI_e-CdU-NQEH-Aa7lhkpW4WsVoBi_2WO7IeIZ_Q2RkASlyB4Br0U98ExA6_vXzmZ1RtWaXsvcsRSpcwQTc5fmAah2eOG0A0glxYqf-dwM7PjOoIdP0ryc-6jrp5w1d2dE3ImoC-pMyOLTagsDj5iVUtIq8Y4xCFdXh4J1WYSZ6UAnyxYY0ctPVTv3NXJvlcbccy_4GsqgpSIk0Whcun0Jyjr0jPXw5Jaxl50VVW2Me8srgTSbFWugqEAfxkgVQHu2GDwd60oYyznMrfmYI8t4Q8lQ.8yeQaqpcresDf6Gy.7Bz_yj77Vfa1QTlfKZD1.ODRr0itIlYurZ3bbNH47jQ"
// node jose generated JWE ciphertext part - decrypt with node crypto code : error
const nodejoseEncPayload = "eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJraWQiOiJ0ZXN0ZXIiLCJlbmMiOiJBMjU2R0NNIn0.Dw_EDEJnahKdk8mwlY3juSWd4jTIM8Go8NdQLpLWeH2kCmDHNg0RZKvbINEc3jF70bPECCzuIi_fH3ovlUeQ_o7KqbF_5ZMiyOIYQvfsVlgSZpl1YSRHgoX1QaRg_FvOvUilE6xGJ_nRnLSk1VjdTWJFARu-IGEfed0aQ1mCsh5Hq9e2q55iQGlMUQVIHQ7X6ZzXNHu4QW7jSMYLskP7Vg-EjQizrGvHC04S2SsglZ0znm5eyz0KR71zcdkR2O80_v6u2GM6APMCQrJ3GQ7SSsguwEKMxxvCpQvZ65lCV4bujvP7tD4A8pn_SCKTjFGU8MulGzHi2z-K4EZU6yZXFQ.DOkezaeJUsRg6h8q.oXC0AtIkn53UgjF2KdxW.1NBMp9IdXW-U5emoiIrE7A"
// Step 1 : Split JWE string
// const jweParts = nodejoseEncPayload.split(".");
const jweParts = nodeCryptoEncPayload.split(".");
const jweProtectedHeaderPart = jweParts[0];
const jweEncryptedKeyPart = jweParts[1];
const jweIvPart = jweParts[2];
const jweCipherTextPart = jweParts[3];
const jweAuthTagPart = jweParts[4];
//step 2 : Decrypt CEK
const sk = crypto.createPrivateKey({ key: testJwk, format: 'jwk',encoding: "utf-8" });
const decryptedCek = crypto.privateDecrypt({key:sk,oaepHash: 'sha256',padding: crypto.constants.RSA_PKCS1_OAEP_PADDING},
Buffer.from(jweEncryptedKeyPart,"base64")
);
//step 3 : Decrypt Content
const dataToDecryptPart = jweCipherTextPart.slice(0, jweCipherTextPart.length - jweCipherTextPart.length);
const deCipher = crypto.createDecipheriv('aes-256-gcm', Buffer.from(decryptedCek, 'base64'), Buffer.from(jweIvPart, 'base64'));
deCipher.setAuthTag(Buffer.from(jweAuthTagPart, 'base64'))
deCipher.setAAD(Buffer.from(jweProtectedHeaderPart, 'base64'));
let decrypted = deCipher.update(Buffer.from(jweCipherTextPart, 'base64'), null, 'utf8');
deCipher.setAutoPadding(false);
decrypted += deCipher.final('utf8');
console.info("Decrypted text : "+decrypted)
**Console output**
Decrypted text : {"test":"test"}
блок кода шифрования узла работает с узлом, сгенерированным с помощью шифрования JWE nodeCryptoEncPayload, но возникают ошибки для узла, сгенерированного jose, используется JWE nodejoseEncPayload
Заголовок должен передаваться в формате AAD «как есть», т. е. в кодировке Base64. Ошибка в текущем коде — декодирование Base64. Исправить:
...
deCipher.setAAD(Buffer.from(jweProtectedHeaderPart, 'utf8'));
...
Благодаря этому исправлению расшифровка токена JOSE работает.
Несколько несоответствий, но без последствий: компоненты токена на самом деле закодированы Base64url. Поэтому было бы чище применить Base64url, то есть идентификатор base64url
вместо base64
. Хотя криптомодуль исправляет это изнутри, с правильным идентификатором было бы яснее.
Кроме того, decryptedCek
уже является буфером и не требует декодирования Base64 (что, впрочем, также неявно фиксируется криптомодулем).
Кроме того, насколько я знаю, заполнение автоматически отключается для GCM, поэтому явное отключение с помощью setAutoPadding()
не требуется.
@vion — Похоже, ваш код шифрования NodeJS не соответствует RFC 7516, поскольку он генерирует JWE, не соответствующий RFC 7516 (nodeCryptoEncPayload
), в отличие от кода шифрования node-jose. Без кода я могу только предполагать: возможно, здесь также необходимо использовать заголовок в кодировке Base64 в качестве AAD. Если это не решит проблему, вам следует задать новый вопрос, используя код шифрования NodeJS.
Спасибо. это устранило проблему. Есть ли у вас какие-либо предложения по обратной совместимости с шифрованием узла, поскольку теперь по той же причине он нарушает криптокод узла?