fix(crypto): align server-side encrypted format with client-side Web Crypto API

The server-side encrypt function in crypto-server.ts used format
`iv + authTag + encrypted`, but the client-side decrypt function in
crypto.ts expects `iv + encrypted + authTag` (authTag at the end,
matching Web Crypto API convention where ciphertext includes authTag).

This mismatch caused all admin API requests to fail with
"The operation failed for an operation-specific reason" when
NEXT_PUBLIC_ENCRYPTION_SECRET was configured, since the client
could not decrypt the server's response.

Fix: swap the order of authTag and encrypted in both encrypt and
decrypt functions in crypto-server.ts.
This commit is contained in:
2026-08-03 10:39:51 +08:00
parent 3e93317988
commit 848f4b51d2
+5 -4
View File
@@ -53,8 +53,9 @@ export function encrypt(plaintext: string): string {
]);
const authTag = cipher.getAuthTag();
// 格式: iv + authTag + encrypted
const combined = Buffer.concat([iv, authTag, encrypted]);
// 格式: iv + encrypted + authTag
// 与客户端 crypto.ts 的 decrypt 格式一致(Web Crypto API 将 authTag 附在 ciphertext 末尾)
const combined = Buffer.concat([iv, encrypted, authTag]);
return combined.toString('base64');
}
@@ -66,8 +67,8 @@ export function decrypt(encryptedBase64: string): string {
const combined = Buffer.from(encryptedBase64, 'base64');
const iv = combined.subarray(0, IV_LENGTH);
const authTag = combined.subarray(IV_LENGTH, IV_LENGTH + AUTH_TAG_LENGTH);
const encrypted = combined.subarray(IV_LENGTH + AUTH_TAG_LENGTH);
const authTag = combined.subarray(combined.length - AUTH_TAG_LENGTH);
const encrypted = combined.subarray(IV_LENGTH, combined.length - AUTH_TAG_LENGTH);
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(authTag);