Poking Around in Mac App Store Receipts

published 19 Mar 2013

Yngve Åström asked if anyone knew how to tell which Apple ID installed an app store app on the MacEnterprise mailing list:

Is it just me or have anyone been able to read something useful out of the app/Contents/_MASReceipt/receipt? I’m looking for a way to find out which account was used to by an app, the _MASreceipt library looked like the right place to look. Doesn’t matter how I trie to read the receipt all I get is bits of readable info that makes very little sense to me. Looks like binary peaces of certs but nothing close to an AppStore account. The /Users/myuser/Library/Preferences/com.apple.storeagent.plist AppleID will tell me what account I’m using now but that’s it. Where can I find which account was used to by a particular app? In this case Server.app… Is it even possible to find out?

Let’s poke around and see what’s inside those receipt files the Mac App Store puts inside every app bundle. The receipt file itself is a PKCS #7 container, as defined by RFC2315, with its payload encoded using ASN.1. We can look at its certificates using openssl’s pkcs7 command:

$ openssl pkcs7 -inform der -in /Applications/Xcode.app/Contents/_MASReceipt/receipt -print_certs -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            18:59:43:21:72:74:9c:fc
        Signature Algorithm: sha1WithRSAEncryption
        Issuer: C=US, O=Apple Inc., OU=Apple Worldwide Developer Relations, CN=Apple Worldwide Developer Relations Certification Authority
        Validity
            Not Before: Nov 11 21:58:01 2010 GMT
            Not After : Nov 11 21:58:01 2015 GMT
        Subject: CN=Mac App Store Receipt Signing, OU=Apple Worldwide Developer Relations, O=Apple Inc., C=US
[...]

You’ll see that it contains a certificate chain with Apple’s root CA, the developer CA, and the receipt signing certificate. There’s a signed payload in the receipt as well, which we can see if we dump the ASN.1 data using openssl’s asn1parse command:

$ openssl asn1parse -inform der -in /Applications/Xcode.app/Contents/_MASReceipt/receipt
    0:d=0  hl=4 l=4701 cons: SEQUENCE          
    4:d=1  hl=2 l=   9 prim: OBJECT            :pkcs7-signedData
   15:d=1  hl=4 l=4686 cons: cont [ 0 ]        
   19:d=2  hl=4 l=4682 cons: SEQUENCE          
   23:d=3  hl=2 l=   1 prim: INTEGER           :01
   26:d=3  hl=2 l=  11 cons: SET               
   28:d=4  hl=2 l=   9 cons: SEQUENCE          
   30:d=5  hl=2 l=   5 prim: OBJECT            :sha1
   37:d=5  hl=2 l=   0 prim: NULL              
   39:d=3  hl=4 l= 526 cons: SEQUENCE          
   43:d=4  hl=2 l=   9 prim: OBJECT            :pkcs7-data
   54:d=4  hl=4 l= 511 cons: cont [ 0 ]        
   58:d=5  hl=4 l= 507 prim: OCTET STRING      [HEX DUMP]:318201F7300A0201120201
0104021600300A02011302010104020C00300B02010E0201010403020101300C02010A0201010404
1602342B300C02010B0201010404020207D3300C02010D0201010404020227D9300E020101020101
040602041DABD29B300E020104020101040602043D83F593300E0201090201010406020450323132
300E0201100201010406020400DE7D2B300F02010302010104070C05342E362E31301002010F0201
01040802063C079CDD8EF6301B02010002010104130C1150726F64756374696F6E52656365697074
301C02010202010104140C12636F6D2E6170706C652E64742E58636F6465301C0201050201010414
80594D99A2FC354866BD2E624356CAA402371E8B301E02010802010104161614323031332D30332D
31355430363A31373A32365A301E02010C02010104161614323031332D30332D31355430363A3137
3A32365A3047020107020101043FA2F2AB232A8EE238211B3976878A67518DE97316756762D19CBD
6BAF021269B097CB4557693759139ECBDCC0526F788B2FB055E68B60B3555320FA64AE5905306102
01060201010459706C1F223E8C5CA8B556E292C13C6A1669C0BF7BC47B7B268C017B2D37F6BEA7BF
D4B4FFF5FF56DBEC269CCA6EEABFC6BC24C0185FFD7F9538088B0E6A99DA608B3129C81A64966151
E4C5B3931ECBF73EE7040949A1F46884
  569:d=3  hl=4 l=3669 cons: cont [ 0 ]        
  573:d=4  hl=4 l=1387 cons: SEQUENCE          
  577:d=5  hl=4 l=1107 cons: SEQUENCE          
  581:d=6  hl=2 l=   3 cons: cont [ 0 ]        
  583:d=7  hl=2 l=   1 prim: INTEGER           :02
  586:d=6  hl=2 l=   8 prim: INTEGER           :1859432172749CFC
  596:d=6  hl=2 l=  13 cons: SEQUENCE          
  598:d=7  hl=2 l=   9 prim: OBJECT            :sha1WithRSAEncryption
[...]

The first big blob of hex is a payload signed with sha1. This payload is also encoded using ASN.1, so let’s decode it and save it as a separate file, payload.asn:

$ openssl asn1parse -inform der -in /Applications/Xcode.app/Contents/_MASReceipt/receipt | grep -m 1 'OCTET STRING' | cut -d: -f4 | xxd -r -p > payload.asn

With it saved to disk let’s see what asn1parse has to say about it:

$ openssl asn1parse -inform der -in payload.asn
    0:d=0  hl=4 l= 503 cons: SET               
    4:d=1  hl=2 l=  10 cons: SEQUENCE          
    6:d=2  hl=2 l=   1 prim: INTEGER           :12
    9:d=2  hl=2 l=   1 prim: INTEGER           :01
   12:d=2  hl=2 l=   2 prim: OCTET STRING      [HEX DUMP]:1600
   16:d=1  hl=2 l=  10 cons: SEQUENCE          
   18:d=2  hl=2 l=   1 prim: INTEGER           :13
   21:d=2  hl=2 l=   1 prim: INTEGER           :01
   24:d=2  hl=2 l=   2 prim: OCTET STRING      [HEX DUMP]:0C00
   28:d=1  hl=2 l=  11 cons: SEQUENCE          
   30:d=2  hl=2 l=   1 prim: INTEGER           :0E
   33:d=2  hl=2 l=   1 prim: INTEGER           :01
   36:d=2  hl=2 l=   3 prim: OCTET STRING      [HEX DUMP]:020101
[...]

We can see that the payload is composed of a set of attributes, defined by two integers and an octet string. The first integer is the attribute type, the second its version (so far always 1), and the octet string its value. How the octet string is interpreted depends on the attribute type, and Apple has reserved most for private use, but a few are public:

Type Definition Value Interpretation
2Bundle identifierUTF8STRING.
3Application versionUTF8STRING.
4Opaque valueA series of bytes.
5SHA-1 hash20-byte SHA-1 digest value.
17In-app purchase receiptFurther down the ASN.1 rabbit hole.

Bundle ID and app version are self explanatory, and the SHA-1 hash is used to verify that the app bundle hasn’t been modified. IAPs have their own receipt following the same principle as the main receipt, leaving only the mysteriously named “Opaque value”:

[...]
  114:d=1  hl=2 l=  14 cons: SEQUENCE          
  116:d=2  hl=2 l=   1 prim: INTEGER           :04
  119:d=2  hl=2 l=   1 prim: INTEGER           :01
  122:d=2  hl=2 l=   6 prim: OCTET STRING      [HEX DUMP]:02043D840EE2
[...]

From Apple’s documentation we can see that it’s used together with the computer’s GUID in computing the verification hash. The ASN.1 specification tells us that it’s an integer (0x02) and that it’s four bytes long (0x04). Let’s print it as a decimal integer:

$ printf "%d\n" 0x$(openssl asn1parse -inform der -in payload.asn | grep -A 2 ':04$' | tail -1 | cut -d: -f4 | cut -c5-)
1032064738

Still pretty opaque. Haven’t I seen that integer somewhere else though? Indeed I have:

$ defaults read com.apple.storeagent
{
    AccountKind = 0;
    AccountURLBagType = production;
    AppleID = "magervalp@mac.com";
    CreditDisplayString = "";
    DSPersonID = 1032064738;
    DownloadLocation = "/Users/pelle/Library/Application Support/AppStore";
    EligibilityCheckDate = "2013-03-18 22:26:53 +0000";
    ISBadgeValues =     {
    };
    ISLastUpdatesQueueCheck = "2013-03-15 06:17:25 +0000";
    KnownAccounts =     (
                {
            AccountKind = 0;
            AccountURLBagType = production;
            CreditDisplayString = "";
            DSPersonID = 1032064738;
        }
    );
    LastAuthTime = "2013-03-14 21:18:37 +0000";
    LastSynchedStoreFront = "143456,12";
    PurchasesInflight = 0;
    Storefront = "143456-17,13";
    UserNotificationDate = "2013-03-15 06:45:07 +0000";
}

Bingo - it’s the numeric app store user ID tied to my Apple ID. While we can’t directly determine which Apple ID installed a certain app, we can build a list of DSPersonIDs and their corresponding Apple IDs and get it that way.

References: