如何用JavaScript解密ApplePay Token?求等价OpenSSL调用及NPM包
嘿,我来帮你把Ruby里的Apple Pay Token解密逻辑移植到JavaScript里!你提到的那些OpenSSL调用,用Node.js内置的crypto模块就能完全对应上,不用额外装第三方包(毕竟你在能获取NPM包的环境,Node.js是最适配的选择)。下面我一步步拆解对应关系和代码:
核心Ruby操作的JS等价映射
先把你给出的Ruby代码里的关键组件拆解清楚,再对应到JS的实现:
OpenSSL::Cipher.new("aes-256-gcm").decrypt:初始化AES-256-GCM解密器OpenSSL::PKey::EC.new(Base64.decode64(ephemeral_public_key)):解析Base64编码的EC临时公钥Digest::SHA256.new:SHA256哈希摘要生成OpenSSL::X509::Store.new:X509证书链验证
1. AES-256-GCM解密器初始化
Ruby里的解密器初始化,对应Node.js crypto模块的createDecipheriv方法(注意GCM模式必须手动传入IV和认证标签,Ruby会隐式处理的部分JS需要显式声明):
const crypto = require('crypto'); // 假设你已经从Apple Pay Token中提取出了密钥、IV、认证标签 const decipher = crypto.createDecipheriv('aes-256-gcm', aesKey, ivBuffer); decipher.setAuthTag(authTagBuffer); // GCM模式必须设置认证标签,否则解密会失败
2. 解析EC临时公钥
Ruby里解析Base64编码的EC公钥,对应Node.js的crypto.createPublicKey方法。Apple Pay的临时公钥一般是SPKI格式的DER编码Base64,所以直接按下面的方式解析:
const ephemeralPublicKeyBase64 = '从Payment Token里拿到的临时公钥Base64'; const publicKey = crypto.createPublicKey({ key: Buffer.from(ephemeralPublicKeyBase64, 'base64'), format: 'der', type: 'spki' });
3. SHA256摘要计算
Ruby的Digest::SHA256.new对应Node.js的crypto.createHash,用法非常直观:
const hash = crypto.createHash('sha256'); hash.update('需要计算哈希的内容'); // 可以传字符串或Buffer const sha256Digest = hash.digest('hex'); // 输出十六进制格式,也可以用'base64'
4. X509证书存储与验证
Ruby的OpenSSL::X509::Store.new用于验证Apple Pay的证书链,在Node.js里可以用crypto.X509Certificate类来实现:
const fs = require('fs'); const crypto = require('crypto'); // 读取Apple官方的根CA证书(你需要提前下载好) const appleRootCA = fs.readFileSync('apple_root_ca.pem'); // 解析从Payment Token里拿到的Apple Pay证书 const applePayCert = new crypto.X509Certificate(applePayCertBuffer); // 验证证书链是否合法,验证通过返回true,失败则抛出错误 applePayCert.verify(appleRootCA);
完整解密流程补充
顺便给你补一下Apple Pay Token解密的完整核心步骤,对应Gala项目里的逻辑:
- 从Payment Token中提取临时公钥、加密的对称密钥、IV、认证标签、加密的支付数据
- 用你的商户EC私钥和临时公钥执行ECDH密钥交换,生成共享密钥:
const merchantPrivateKey = crypto.createPrivateKey('你的商户EC私钥PEM/DER'); const ecdh = crypto.createECDH('prime256v1'); // Apple Pay用的是P-256曲线 ecdh.setPrivateKey(merchantPrivateKey); const sharedSecret = ecdh.computeSecret(publicKey); - 对共享密钥做SHA256哈希,得到最终的AES解密密钥:
const aesKey = crypto.createHash('sha256').update(sharedSecret).digest(); - 用前面初始化的AES-256-GCM解密器解密支付数据:
let decrypted = decipher.update(encryptedPaymentData); decrypted = Buffer.concat([decrypted, decipher.final()]);
额外提示
如果是在浏览器环境下,你需要用Web Crypto API,但你说的是NPM环境,Node.js的crypto模块性能更好且更可靠,完全满足需求。如果需要更上层的封装,也可以考虑node-forge,但内置模块已经足够。
内容的提问来源于stack exchange,提问作者Matt Hudson

