From a4c44a3fbaac58793484d2d0fcc13d71c21e9958 Mon Sep 17 00:00:00 2001 From: imbytecat Date: Mon, 26 Jan 2026 13:49:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=94=AF=E6=8C=81Jav?= =?UTF-8?q?a=E5=85=BC=E5=AE=B9=E7=9A=84RSA=E5=8A=A0=E5=AF=86=E8=A7=A3?= =?UTF-8?q?=E5=AF=86=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加RSA加密解密工具函数,支持与Java兼容的OAEPWithSHA-256AndMGF1Padding加密标准,使用DER/SPKI和DER/PKCS8格式的公私钥进行加解密操作。 --- apps/server/src/lib/crypto.ts | 109 ++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 apps/server/src/lib/crypto.ts diff --git a/apps/server/src/lib/crypto.ts b/apps/server/src/lib/crypto.ts new file mode 100644 index 0000000..65779d0 --- /dev/null +++ b/apps/server/src/lib/crypto.ts @@ -0,0 +1,109 @@ +import { + constants, + createPrivateKey, + createPublicKey, + privateDecrypt, + publicEncrypt, +} from 'node:crypto' + +// 对应 Java: RSA/ECB/OAEPWithSHA-256AndMGF1Padding +const OAEP_HASH = 'sha256' + +/** + * 使用 Base64 编码的公钥加密明文 + * + * 加密标准: RSA/ECB/OAEPWithSHA-256AndMGF1Padding (兼容 Java) + * 公钥格式: SPKI/DER (对应 Java X509EncodedKeySpec) + * + * @param plainText - 待加密的明文字符串 + * @param publicKeyBase64 - Base64 编码的公钥 (DER/SPKI 格式) + * @returns Base64 编码的加密数据 + * @throws {Error} 加密失败时抛出错误 + * + * @example + * ```typescript + * const publicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...' + * const encrypted = encrypt('sensitive data', publicKey) + * console.log(encrypted) // "a3f5e8c2d1b4..." + * ``` + */ +export function encrypt(plainText: string, publicKeyBase64: string): string { + const buffer = Buffer.from(plainText, 'utf-8') + const keyBuffer = Buffer.from(publicKeyBase64, 'base64') + + try { + // 1. 先创建 KeyObject,在这里指定密钥的格式 (DER/SPKI) + const publicKey = createPublicKey({ + key: keyBuffer, + format: 'der', + type: 'spki', // 对应 Java X509EncodedKeySpec + }) + + // 2. 使用 KeyObject 进行加密,在这里指定 Padding 模式 + const encrypted = publicEncrypt( + { + key: publicKey, + padding: constants.RSA_PKCS1_OAEP_PADDING, + oaepHash: OAEP_HASH, + }, + buffer, + ) + + return encrypted.toString('base64') + } catch (error) { + throw new Error( + `Encryption failed: ${error instanceof Error ? error.message : String(error)}`, + ) + } +} + +/** + * 使用 Base64 编码的私钥解密数据 + * + * 解密标准: RSA/ECB/OAEPWithSHA-256AndMGF1Padding (兼容 Java) + * 私钥格式: PKCS8/DER (对应 Java PKCS8EncodedKeySpec) + * + * @param encryptedData - Base64 编码的加密数据 + * @param privateKeyBase64 - Base64 编码的私钥 (DER/PKCS8 格式) + * @returns 解密后的明文字符串 + * @throws {Error} 解密失败时抛出错误 + * + * @example + * ```typescript + * const privateKey = 'MIIEvQIBADANBgkqhkiG9w0BAQEFAASC...' + * const decrypted = decrypt('a3f5e8c2d1b4...', privateKey) + * console.log(decrypted) // "sensitive data" + * ``` + */ +export function decrypt( + encryptedData: string, + privateKeyBase64: string, +): string { + const buffer = Buffer.from(encryptedData, 'base64') + const keyBuffer = Buffer.from(privateKeyBase64, 'base64') + + try { + // 1. 先创建 KeyObject,在这里指定密钥的格式 (DER/PKCS8) + const privateKey = createPrivateKey({ + key: keyBuffer, + format: 'der', + type: 'pkcs8', // 对应 Java PKCS8EncodedKeySpec + }) + + // 2. 使用 KeyObject 进行解密 + const decrypted = privateDecrypt( + { + key: privateKey, + padding: constants.RSA_PKCS1_OAEP_PADDING, + oaepHash: OAEP_HASH, + }, + buffer, + ) + + return decrypted.toString('utf-8') + } catch (error) { + throw new Error( + `Decryption failed: ${error instanceof Error ? error.message : String(error)}`, + ) + } +}