Apple Pay - 如何将商家 public 密钥与支付令牌中的 publicKeyHash 进行比较?

Apple Pay - How to compare merchant public key with publicKeyHash from payment token?

我正在处理 Apple Pay 支付令牌解密。 根据第 2 步中的此说明 Payment Token Format Reference。我需要使用支付令牌 header 中的 publicKeyHash 字段来确定哪个 商家证书被Apple使用。

pulbicKeyHash 是商家证书的 X.509 编码 public 密钥字节的 SHA–256 哈希,Base64 编码为字符串。

我有一张商户证。所以我假设如果我将我的证书的 public 密钥的 sha-256 哈希和 Base64 编码,我将获得与我在支付令牌的 publicKeyHash 字段中收到的相同的值。

但我不知道应该对证书的哪个特定部分进行哈希处理。 苹果提供的初始商户证书为.cer格式。 我已将 public 密钥从中提取为 .pem 格式。比我已经尝试过的都采用 public 键的散列 -> base64encode(字符串在 -----BEGIN CERTIFICATE----- 和 -----END CERTIFICATE----- 之间)和 获取 base64 解码的 .pem 的哈希,我认为它应该是 .der 和 base 64 编码它。

并且两者都未能匹配从 Apple Pay 收到的价值。它的长度也不同,我的 base64 编码哈希长度为 88 个字符,publicKeyHash 字段的长度为 44 个字符。

当我尝试对 publicKeyHash 进行 base 64 解码时,我得到了不可读的字符,例如“D�đ����$��f����@c����$���� WP��" 但根据 Apple 文档,应该有 sha-256 哈希,不能包含此类符号。

谁能解释一下我应该执行哪些具体步骤才能完成此商家证书检查?

在我的案例中,主要问题和解决方案是使用支付处理证书的public密钥散列和NOT Merchant Identity Certificate 的 public 密钥哈希, 我试图与支付令牌中的 PublicKeyHash 进行比较。 以我的借口,我可以说 Apple Documentation 中的以下文本非常模棱两可:

publicKeyHash SHA–256 hash, Base64 encoded as a string Hash of the X.509 encoded public key bytes of the merchant’s certificate.

因为我们有两种证书商家和支付处理。对我来说很明显,文档中的商家证书是商家 ID 证书。

仅在重新阅读支付处理证书描述后

Payment Processing Certificate. A certificate used to securely transfer payment data. Apple Pay servers use the payment processing certificate’s public key to encrypt the payment data. Use the private key to decrypt the data when processing payments.

Apple Pay JS 文档中我意识到我的错误。

所以我希望我的经验可以帮助别人不要踩到同一个耙子)

这个问题和接受的答案在细节上仍然有点模糊,所以这里是 java 中用于检查 token.paymentData.header.publicKeyHash 是否匹配 Apple Pay 支付处理证书的精确测试方法:

private static void checkPublicKeyHash(String publicKeyHash, X509Certificate paymentProcessingCertificate)
        throws NoSuchAlgorithmException, CertificateException {

    String certHash = Base64.getEncoder().encodeToString(
            MessageDigest.getInstance("SHA-256").digest(
                    paymentProcessingCertificate.getPublicKey().getEncoded()));
    if (!Objects.equals(publicKeyHash, certHash)) {
        throw new DigestException(String.format(
                "publicKeyHash %s doesn't match Payment Processing Certificate hash %s",
                publicKeyHash, certHash));
    }
}

很遗憾我无法找到直接从证书中提取哈希的 openssl 命令。因此,您必须先创建 public 密钥才能获得 public 密钥哈希。有两种方法可以提取 public 密钥。

步骤 1

一个。来自你的ecc私钥(支付处理私钥)

openssl ec -in ecc_private_key.key -pubout -out ec_public_key.pem

乙。来自apple pay portal下载的证书(上传支付处理csr后)

openssl x509 -inform der -in apple_pay.cer -pubkey -noout > apple_pay_public_key.pem

两者都会为您提供以下格式的 public 密钥

-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENGbyXUzeZTdeyyNuXyc0nMzXmnLl
xMwd/t/sCZr3RPhytPbZpR/V4/xHqN/MVzozzq30I0/eUefbThEBl236Og==
-----END PUBLIC KEY-----

步骤 2
您可以使用以下代码从 public 键上方提取 base64 哈希,记住删除 headers/footers 和换行符。

我希望我能弄清楚如何使用 openssl 工具从 public 密钥获取哈希,但无论如何遵循 c# 代码对我来说都是有效的。它非常简单易用,可以移植到 java/python/php 或您喜欢的任何地方。或者只需在 ideone.com

在线使用以下代码
String publicKeyBase64 = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENGbyXUzeZTdeyyNuXyc0nMzXmnLlxMwd/t/sCZr3RPhytPbZpR/V4/xHqN/MVzozzq30I0/eUefbThEBl236Og==";

byte[] publicKey = Convert.FromBase64String(publicKeyBase64);
SHA256 sha256 = SHA256Managed.Create();
byte[] hash = sha256.ComputeHash(publicKey);
String publicKeyHash = Convert.ToBase64String(hash);

Console.WriteLine("Result: {0}", publicKeyHash);

请记住,您的系统应该能够在任何给定时间接受多个密钥,而不仅仅是验证您需要根据从设备收到的 publicKeyHash 加载正确的私钥(iphone/ipad etc) 考虑到当前证书即将到期(或者您出于任何原因正在撤销)的情况,否则您的系统可能无法在短时间内接受交易。根据我的遭遇,在门户中按下激活后,苹果花了一个多小时才激活新的支付处理密钥。

首先,原始问题的答案似乎相隔几个月。其次,所有答案似乎都缺少一点关键信息; Payment Token Format Reference 第 2 步的唯一原因是您可以使用多个付款处理证书。如果你这样做,那么苹果可能会使用任何人来加密数据。 如果您只有一个付款处理证书,那么您可以跳过此步骤,只使用它的私钥。毕竟第二步的最终结果是得到支付处理证书的私钥,用于加密支付数据。