fix(crypto): 修复 RSA-OAEP 加密与 Java SunJCE 的 MGF1 哈希不兼容问题
Node.js publicEncrypt({ oaepHash }) 会将 OAEP hash 和 MGF1 hash
绑定为同一算法,而 Java OAEPWithSHA-256AndMGF1Padding 默认使用
SHA-256(OAEP) + SHA-1(MGF1)。改用 node-forge 独立配置两个哈希,
确保密文可被管理平台正确解密。
This commit is contained in:
@@ -7,10 +7,12 @@
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"node-forge": "^1.3.3",
|
||||
"openpgp": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@furtherverse/tsconfig": "workspace:*",
|
||||
"@types/bun": "catalog:"
|
||||
"@types/bun": "catalog:",
|
||||
"@types/node-forge": "^1.3.14"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
import { constants, createPublicKey, publicEncrypt } from 'node:crypto'
|
||||
import forge from 'node-forge'
|
||||
|
||||
/**
|
||||
* RSA-OAEP encrypt with platform public key.
|
||||
*
|
||||
* Algorithm: RSA/ECB/OAEPWithSHA-256AndMGF1Padding
|
||||
* - OAEP hash: SHA-256
|
||||
* - MGF1 hash: SHA-256
|
||||
* Matches Java's {@code Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")}
|
||||
* with **default SunJCE parameters**:
|
||||
*
|
||||
* | Parameter | Value |
|
||||
* |-----------|--------|
|
||||
* | OAEP hash | SHA-256|
|
||||
* | MGF1 hash | SHA-1 |
|
||||
*
|
||||
* Node.js `crypto.publicEncrypt({ oaepHash })` ties both hashes together,
|
||||
* so we use `node-forge` which allows independent configuration.
|
||||
*
|
||||
* @param plaintext - UTF-8 string to encrypt
|
||||
* @param publicKeyBase64 - Platform public key (X.509 DER, Base64 encoded)
|
||||
* @param publicKeyBase64 - Platform RSA public key (X.509 / SPKI DER, Base64)
|
||||
* @returns Base64-encoded ciphertext
|
||||
*/
|
||||
export const rsaOaepEncrypt = (plaintext: string, publicKeyBase64: string): string => {
|
||||
// Load public key from Base64-encoded DER (X.509 / SubjectPublicKeyInfo)
|
||||
const publicKeyDer = Buffer.from(publicKeyBase64, 'base64')
|
||||
const publicKey = createPublicKey({
|
||||
key: publicKeyDer,
|
||||
format: 'der',
|
||||
type: 'spki',
|
||||
const derBytes = forge.util.decode64(publicKeyBase64)
|
||||
const asn1 = forge.asn1.fromDer(derBytes)
|
||||
const publicKey = forge.pki.publicKeyFromAsn1(asn1) as forge.pki.rsa.PublicKey
|
||||
|
||||
const encrypted = publicKey.encrypt(plaintext, 'RSA-OAEP', {
|
||||
md: forge.md.sha256.create(),
|
||||
mgf1: { md: forge.md.sha1.create() },
|
||||
})
|
||||
|
||||
// Encrypt with RSA-OAEP (SHA-256 for both OAEP hash and MGF1)
|
||||
const encrypted = publicEncrypt(
|
||||
{
|
||||
key: publicKey,
|
||||
padding: constants.RSA_PKCS1_OAEP_PADDING,
|
||||
oaepHash: 'sha256',
|
||||
},
|
||||
Buffer.from(plaintext, 'utf-8'),
|
||||
)
|
||||
|
||||
return encrypted.toString('base64')
|
||||
return forge.util.encode64(encrypted)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user