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)}`, + ) + } +}