Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOS In-App-Purchase Verifying receipts locally in Swift

codelynx
September 14, 2019
34

iOS In-App-Purchase Verifying receipts locally in Swift

There aren't so much living example how to verify receipts of In-App-Purchase locally preferably in Swift. Some how, reusing the same verification code are not recommended for security reason. Therefore, it is hard to implement whole set of receipt verification code in your hand. This slide is not intended for encouraging write less secure code, but guide you how to implement a whole set of verification code in Swift. You may have to consider how to make your code to become more secure before your releasing day.

codelynx

September 14, 2019
Tweet

Transcript

  1. Verifying In-App-
    Purchase Receipts
    Locally in Swift
    [email protected]
    iOS Tokyo Meetup September 2019

    View Slide

  2. About me

    View Slide

  3. Kaz Yoshikawa
    • Electricwoods LLC / Digital Lynx Systems Inc.
    • e-mail: [email protected]
    • twitter: @codelynx1
    • Working History
    • Adobe Systems (Tokyo)
    • Lionbridge (Tokyo)
    • Quark (Tokyo / Denver)
    • Hummingbird Communications (Mt. View, USA)
    • Fact International (Vancouver, Canada)
    • Perle Systems (Toronto, Canada), etc.

    View Slide

  4. How to verify
    In-App-Purchase
    Receipts “Locally”?
    Show me the code.

    View Slide

  5. Verifying receipts via server
    • There are many articles about how to verify receipt through your trusted
    server
    • iOSͷֹ݄՝ۚϨγʔτݕূΛαʔόʔαΠυͰߦ͏ͱ͖ͷTips·ͱΊ
    • iOS In-App Purchase࣮૷Ͱඞͣ஌͓͖͍ͬͯͨӅΕͨ᠘
    • Apple App Store Receipt Validation with Swift and Go
    • Validating in-app purchases in your iOS app
    • ϨγʔτͷverifyͱSandbox
    • ࣗಈߪಡ՝ۚʹ͍ͭͯʲiOSฤʳ

    View Slide

  6. Verifying receipts locally
    • Very few articles available:
    • Suggestions like:
    • Use “ASN1C” to access ASN.1 format
    • Verify the receipt is signed by Apple
    • Use OpenSSL
    • They do not show any working code.
    • Hey ! show me the code.

    View Slide

  7. Disclaimer
    • This presentation material is not for:
    • How to write secure code
    • How to prevent from code cracking
    • Discouraging to write less secure code

    View Slide

  8. Steps to verify
    • Obtain Apple’s root certificate
    • Your app receive a receipt
    • Verify if the receipt is signed by Apple
    • Extract purchases from the receipt
    • Verify if a purchase is expired or cancelled
    • Unlock contents if necessary

    View Slide

  9. Obtain Apple’s root
    certificate
    Apple Root Certificates
    Apple Inc. Root Certificate
    Apple Computer, Inc. Root Certificate
    Apple Root CA - G2 Root Certificate
    Apple Root CA - G3 Root Certificate
    Apple Intermediate Certificates
    Apple IST CA 2 - G1 Certficate
    Apple IST CA 4 - G1 Certficate
    Apple IST CA 5 - G1 Certficate
    Apple IST CA 8 - G1 Certficate
    Application Integration Certificate
    Application Integration 2 Certificate
    Application Integration - G3 Certificate
    Developer Authentication Certificate
    Developer ID Certificate
    Software Update Certificate
    Timestamp Certificate
    WWDR Certificate (Expiring 02/07/23)
    WWDR Certificate (Expiring 02/14/16)
    Worldwide Developer Relations - G2 Certificate
    Apple PKI
    Apple established the Apple PKI in support of the generation, issuance, distribution,
    revocation, administration, and management of public/private cryptographic keys that are
    contained in CA-signed X.509 Certificates.
    https://www.apple.com/certificateauthority/

    View Slide

  10. Find hash (sha256) of the
    “AppleIncRootCertificate.cer”
    $ shasum -a 256 AppleIncRootCertificate.cer
    b0b1730ecbc7ff4505142c49f1295e6eda6bcaed7e2c68c5be91b5a11001f024
    AppleIncRootCertificate.cer
    Use this to ensure AppleIncRootCertificate.cer is genuine

    View Slide

  11. Load the Root Certificate
    var appleIncRootCertificate: Data {
    if let url = Bundle.main.url(forResource: "AppleIncRootCertificate",
    withExtension: "cer"),
    let data = try? Data(contentsOf: url) {
    // make sure the certificate is not fake one
    let sha256 = Data(base64Encoded:
    "sLFzDsvH/0UFFCxJ8Slebtpryu1+LGjFvpG1oRAB8CQ=")
    if data.sha256 == sha256 {
    return data
    }
    }
    fatalError("error: failed to read the certificate.")
    }
    Check hash against hardcoded base64 hash value

    View Slide

  12. Receiving receipts
    Bundle.main.appStoreReceiptURL
    if let url = Bundle.main.appStoreReceiptURL {
    do {
    let receiptData = try Data(contentsOf: url)
    // ...
    }
    catch {
    }
    }

    View Slide

  13. Digging ASN1 Format

    View Slide

  14. Check if it is signed by Apple
    let pkcs7 = try PKCS7(data: reciptData)
    let appleX509cert = try X509Certificate(data: self.appleIncRootCertificate)
    guard let appleKey = appleX509cert.publicKey?.key
    else { print("x509 public key not found."); return }
    print(appleKey as NSData)
    // check if one of these certificates is signed by apple
    let signedByApple: Bool = {
    print("certificates:")
    for certificate in pkcs7.certificates {
    if let signedKey = certificate.publicKey?.key {
    print(signedKey as NSData)
    if signedKey == appleKey {
    return true
    }
    }
    }
    return false
    }()

    View Slide

  15. Check all purchases
    if let inAppPurchases = receipt.inAppPurchases {
    for purchase in inAppPurchases {
    guard let productIdentifier = purchase.productId else { continue }
    print(productIdentifier)
    print(purchase.purchaseDate ?? "n/a")
    print(purchase.originalPurchaseDate ?? "n/a")
    print(purchase.cancellationDate ?? "n/a")
    print(purchase.expiresDate ?? "n/a")
    // ...
    }
    }
    What to check is not covered by this presentation.

    View Slide

  16. Recap

    View Slide

  17. Qiita
    https://qiita.com/codelynx/items/a88805b47b7e40ef3782

    View Slide

  18. View Slide