验证苹果支付支付令牌签名
Validating apple pay payment token signature
由于某些原因,我不得不在没有支付平台的情况下处理apple pay payment token。根据official document,我需要先“验证签名”。并且签名是 base64 编码的分离 PKCS #7 签名。我想用 node.js 或 openssl.
验证它
由于非常方便的加密工具 node-forge 尚不支持“ECDSA with sha256”(link),而且我找不到其他替代品。我转向了openssl。通过一些调查,签名似乎以“CMS signed data”格式打包。所以我发现 openssl 手册中的这个命令应该能够完成这项工作:
openssl cms -verify -inform DER -in signature.der -content content.txt
Apple 文档说“确保签名是 ephemeralPublicKey、数据、transactionId 和 applicationData 键的串联值的有效 ECDSA 签名”。因此,我通过从我的测试令牌中连接这些字段来生成我的测试内容。但结果是:
Verification failure
C0:25:34:08:01:00:00:00:error:CMS routines:CMS_SignerInfo_verify_content:verification failure:crypto/cms/cms_sd.c:901:
C0:25:34:08:01:00:00:00:error:CMS routines:CMS_verify:content verify error:crypto/cms/cms_smime.c:399:
cms_sd.c:901 是验证 igest 的签名者所以我认为这个命令是我需要的。那我做错了什么?
更新:我发现PKI.js可以完成这项工作。详细答案如下。
下面来自 PKI.js example 的代码片段展示了如何解析现有的 CMSSignedData。
const testData = "<TOKEN_SIGNATURE>";
const cmsSignedBuffer = stringToArrayBuffer(fromBase64(testData));
const asn1 = asn1js.fromBER(cmsSignedBuffer);
const cmsContentSimpl = new ContentInfo({ schema: asn1.result });
const cmsSignedSimpl = new SignedData({ schema: cmsContentSimpl.content });
在此之后,我们可以提取证书和签名者信息以进行进一步处理。
检查 OID:
cmsSignedSimpl.certificates[0].extensions.find(x => x.extnID === '1.2.840.113635.100.6.29') // leaf certificate
cmsSignedSimpl.certificates[1].extensions.find(x => x.extnID === '1.2.840.113635.100.6.2.14') // intermediate certificate
验证签名和信任链:
cmsSignedSimpl.verify({
signer: 0,
trustedCerts: [AppleRootCA_G3],
data: signedData,
checkChain: true, // check x509 chain of trust
extendedMode: true, // enable to show signature validation result
});
查看演唱时间:
const attrs = cmsSignedSimpl.signerInfos[0].signedAttrs.attributes;
const signingTimeAttr = attrs.find(x => x.type === '1.2.840.113549.1.9.5');
const signingTime = new Date(signingTimeAttr.value[0].toDate());
由于某些原因,我不得不在没有支付平台的情况下处理apple pay payment token。根据official document,我需要先“验证签名”。并且签名是 base64 编码的分离 PKCS #7 签名。我想用 node.js 或 openssl.
验证它由于非常方便的加密工具 node-forge 尚不支持“ECDSA with sha256”(link),而且我找不到其他替代品。我转向了openssl。通过一些调查,签名似乎以“CMS signed data”格式打包。所以我发现 openssl 手册中的这个命令应该能够完成这项工作:
openssl cms -verify -inform DER -in signature.der -content content.txt
Apple 文档说“确保签名是 ephemeralPublicKey、数据、transactionId 和 applicationData 键的串联值的有效 ECDSA 签名”。因此,我通过从我的测试令牌中连接这些字段来生成我的测试内容。但结果是:
Verification failure
C0:25:34:08:01:00:00:00:error:CMS routines:CMS_SignerInfo_verify_content:verification failure:crypto/cms/cms_sd.c:901:
C0:25:34:08:01:00:00:00:error:CMS routines:CMS_verify:content verify error:crypto/cms/cms_smime.c:399:
cms_sd.c:901 是验证 igest 的签名者所以我认为这个命令是我需要的。那我做错了什么?
更新:我发现PKI.js可以完成这项工作。详细答案如下。
下面来自 PKI.js example 的代码片段展示了如何解析现有的 CMSSignedData。
const testData = "<TOKEN_SIGNATURE>";
const cmsSignedBuffer = stringToArrayBuffer(fromBase64(testData));
const asn1 = asn1js.fromBER(cmsSignedBuffer);
const cmsContentSimpl = new ContentInfo({ schema: asn1.result });
const cmsSignedSimpl = new SignedData({ schema: cmsContentSimpl.content });
在此之后,我们可以提取证书和签名者信息以进行进一步处理。
检查 OID:
cmsSignedSimpl.certificates[0].extensions.find(x => x.extnID === '1.2.840.113635.100.6.29') // leaf certificate
cmsSignedSimpl.certificates[1].extensions.find(x => x.extnID === '1.2.840.113635.100.6.2.14') // intermediate certificate
验证签名和信任链:
cmsSignedSimpl.verify({
signer: 0,
trustedCerts: [AppleRootCA_G3],
data: signedData,
checkChain: true, // check x509 chain of trust
extendedMode: true, // enable to show signature validation result
});
查看演唱时间:
const attrs = cmsSignedSimpl.signerInfos[0].signedAttrs.attributes;
const signingTimeAttr = attrs.find(x => x.type === '1.2.840.113549.1.9.5');
const signingTime = new Date(signingTimeAttr.value[0].toDate());