ios - 如何使用 Bouncy CaSTLe 读取 App Store In App Purchase 收据? (PKCS7)

标签 ios encryption in-app-purchase bouncycastle pkcs#7

我有一张从我​​的 iOS 应用程序获得的 PKCS7 收据。苹果says this is a PKCS7 structure ,其中包含有关过去经常性购买的信息。

我有 raw receipt here , 以 Base64 编码。

我已将此负载连同我的 key 发送给 Apple and got this response .根据 WWDC 视频和文档,我相信我应该能够直接阅读这张收据,而无需将其发送给苹果。

我猜 BC 中的 PEMReader 是解析它的正确起点,但我不确定如何实际使用它。我已经扫描了字符串“PKCS”的 BC 源代码,并查看了单元测试,但是我所看到的只是从 PEMReader 转换为另一种格式。

 using (var stream1 = new MemoryStream(receipt.Data))
 using (var stream2 = new StreamReader(stream1))
 {
       var pp = new PemReader(stream2);
       pp.ReadObject();
 }

问题

  • 如何使用 Bouncy CaSTLe 验证从 Apple Store 生成的原始收据负载?

self 注意:我打算用它来检查实际的二进制文件,看看 ApplicationUsername 是否包含在收据中,但由于某种原因,在发布服务器时未在 JSON 结果中返回。 (苹果方面的错误?)

最佳答案

我使用 Java 7BouncyCaSTLe 1.56 制作了这个。

对于下面的代码,请考虑 pemString 是您提供的 PEM 字符串。但我不得不做一些修改:

  • 格式(每 64 个字符换行)- 我已经编写了一个小程序来执行此操作
  • 包含BEGINEND 标题

所以我的 PEM 看起来像:

-----BEGIN PKCS7-----
MIIv5gYJKoZIhvcNAQcCoIIv1zCCL9MCAQExCzAJBgUrDgMCGgUAMIIfhwYJKoZI
hvcNAQcBoIIfeASCH3Qxgh9wMAoCAQgCAQEEAhYAMAoCARQCAQEEAgwAMAsCAQEC
AQEEAwIBADALAgELAgEBBAMCAQAwCwIBDwIBAQQDAgEAMAsCARACAQEEAwIBADAL
....
gdTu2uzkTyT+vcBlaLHK1ZpjKozsBds7ys6Q4EFp7OLxtJTj7saEDYXCNQtXBjwl
UfSGvQkXeIbsaqSPvOVIE83K3ki5i64gccA=
-----END PKCS7-----

对于下面的代码,我遵循了苹果文档中的定义:

ReceiptAttribute ::= SEQUENCE {
    type    INTEGER,
    version INTEGER,
    value   OCTET STRING
}

Payload ::= SET OF ReceiptAttribute

代码:

import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Object;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.DLSet;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;

String pemString = // PEM String as described above

PemReader reader = new PemReader(new StringReader(pemString));
PemObject pemObject = reader.readPemObject();
reader.close();

CMSSignedData s = new CMSSignedData(pemObject.getContent());
byte[] content = (byte[]) s.getSignedContent().getContent();

ASN1InputStream in = new ASN1InputStream(content);

// Payload: a SET of ReceiptAttribute
DLSet set = (DLSet) DLSet.fromByteArray(in.readObject().getEncoded());
int size = set.size();
for (int i = 0; i < size; i++) {
    // ReceiptAttribute is a SEQUENCE
    DLSequence seq = (DLSequence) set.getObjectAt(i);

    // value is the third element of the sequence
    DEROctetString oct = (DEROctetString) seq.getObjectAt(2);
    ASN1Object obj = readObject(oct.getOctets()); // *** see comments below ***
}

in.close();

// readObject method
public ASN1Object readObject(byte[] b) throws IOException {
    ASN1InputStream in = null;
    try {
        in = new ASN1InputStream(b);
        return in.readObject();
    } catch (Exception e) {
        // if error occurs, just return the octet string
        return new DEROctetString(b);
    } finally {
        in.close();
    }
}

变量 obj 将是 ReceiptAttribute 的内容,它可以变化很多 - 我见过 DERIA5String DERUTF8StringASN1Integer 等等。由于我不知道该字段的所有可能值,因此我认为您应该检查每个值。

关于ios - 如何使用 Bouncy CaSTLe 读取 App Store In App Purchase 收据? (PKCS7),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43325117/

相关文章:

ios - 我将如何在加载 tableView 时运行一些代码?

iphone - 如何为 View 设置背景图像?

ios - 有多个 UITextView 时 UIMenuController 菜单自动消失

c - 嵌入式系统的对称加密算法

security - 好的,现在我已经加密了我的数据,该在哪里隐藏 key ?

ios - 如何使用本地文件在单个 UIViewController 上显示多个 UIWebView?

windows - 证书存储在 Windows 证书存储区时如何解密信息

iphone - 对于非续订订阅,注册应该始终是可选的吗?

android - Android 上的应用内计费(购买确认,json 字符串)

android - 如何检查用户是否购买了订阅