Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

initial commit

  • Loading branch information...
commit bddbcd1bd6a53eb8d4c4f9d8620a71223cc71cea 0 parents
@roddi authored
16 ValidateStoreReceipt.m
@@ -0,0 +1,16 @@
+#import <Foundation/Foundation.h>
+
+#import "validatereceipt.h"
+
+int main (int argc, const char * argv[]) {
+ NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+
+ // put the example receipt on the desktop (or change that path)
+ if (!validateReceiptAtPath(@"~/Desktop/receipt.cer"))
+ exit(173);
+
+ // insert code here...
+ NSLog(@"Hello, World!");
+ [pool drain];
+ return 0;
+}
245 ValidateStoreReceipt.xcodeproj/project.pbxproj
@@ -0,0 +1,245 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 45;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 8DD76F9A0486AA7600D96B5E /* ValidateStoreReceipt.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* ValidateStoreReceipt.m */; settings = {ATTRIBUTES = (); }; };
+ 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 08FB779EFE84155DC02AAC07 /* Foundation.framework */; };
+ DD5EBFFF1273472D00361AC6 /* validatereceipt.m in Sources */ = {isa = PBXBuildFile; fileRef = DD5EBFFE1273472D00361AC6 /* validatereceipt.m */; };
+ DD5EC0011273474F00361AC6 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD5EC0001273474F00361AC6 /* IOKit.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 8DD76F9E0486AA7600D96B5E /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 8;
+ dstPath = /usr/share/man/man1/;
+ dstSubfolderSpec = 0;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 1;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 08FB7796FE84155DC02AAC07 /* ValidateStoreReceipt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ValidateStoreReceipt.m; sourceTree = "<group>"; };
+ 08FB779EFE84155DC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = "<absolute>"; };
+ 32A70AAB03705E1F00C91783 /* ValidateStoreReceipt_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValidateStoreReceipt_Prefix.pch; sourceTree = "<group>"; };
+ 8DD76FA10486AA7600D96B5E /* ValidateStoreReceipt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ValidateStoreReceipt; sourceTree = BUILT_PRODUCTS_DIR; };
+ DD5EBFFD1273472D00361AC6 /* validatereceipt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = validatereceipt.h; sourceTree = "<group>"; };
+ DD5EBFFE1273472D00361AC6 /* validatereceipt.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = validatereceipt.m; sourceTree = "<group>"; };
+ DD5EC0001273474F00361AC6 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8DD76F9B0486AA7600D96B5E /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8DD76F9C0486AA7600D96B5E /* Foundation.framework in Frameworks */,
+ DD5EC0011273474F00361AC6 /* IOKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 08FB7794FE84155DC02AAC07 /* ValidateStoreReceipt */ = {
+ isa = PBXGroup;
+ children = (
+ 08FB7795FE84155DC02AAC07 /* Source */,
+ C6859EA2029092E104C91782 /* Documentation */,
+ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */,
+ 1AB674ADFE9D54B511CA2CBB /* Products */,
+ DD5EC0001273474F00361AC6 /* IOKit.framework */,
+ );
+ name = ValidateStoreReceipt;
+ sourceTree = "<group>";
+ };
+ 08FB7795FE84155DC02AAC07 /* Source */ = {
+ isa = PBXGroup;
+ children = (
+ DD5EBFFD1273472D00361AC6 /* validatereceipt.h */,
+ DD5EBFFE1273472D00361AC6 /* validatereceipt.m */,
+ 32A70AAB03705E1F00C91783 /* ValidateStoreReceipt_Prefix.pch */,
+ 08FB7796FE84155DC02AAC07 /* ValidateStoreReceipt.m */,
+ );
+ name = Source;
+ sourceTree = "<group>";
+ };
+ 08FB779DFE84155DC02AAC07 /* External Frameworks and Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 08FB779EFE84155DC02AAC07 /* Foundation.framework */,
+ );
+ name = "External Frameworks and Libraries";
+ sourceTree = "<group>";
+ };
+ 1AB674ADFE9D54B511CA2CBB /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8DD76FA10486AA7600D96B5E /* ValidateStoreReceipt */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ C6859EA2029092E104C91782 /* Documentation */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Documentation;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 8DD76F960486AA7600D96B5E /* ValidateStoreReceipt */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "ValidateStoreReceipt" */;
+ buildPhases = (
+ 8DD76F990486AA7600D96B5E /* Sources */,
+ 8DD76F9B0486AA7600D96B5E /* Frameworks */,
+ 8DD76F9E0486AA7600D96B5E /* CopyFiles */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ValidateStoreReceipt;
+ productInstallPath = "$(HOME)/bin";
+ productName = ValidateStoreReceipt;
+ productReference = 8DD76FA10486AA7600D96B5E /* ValidateStoreReceipt */;
+ productType = "com.apple.product-type.tool";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 08FB7793FE84155DC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "ValidateStoreReceipt" */;
+ compatibilityVersion = "Xcode 3.1";
+ developmentRegion = English;
+ hasScannedForEncodings = 1;
+ knownRegions = (
+ English,
+ Japanese,
+ French,
+ German,
+ );
+ mainGroup = 08FB7794FE84155DC02AAC07 /* ValidateStoreReceipt */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 8DD76F960486AA7600D96B5E /* ValidateStoreReceipt */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8DD76F990486AA7600D96B5E /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8DD76F9A0486AA7600D96B5E /* ValidateStoreReceipt.m in Sources */,
+ DD5EBFFF1273472D00361AC6 /* validatereceipt.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+ 1DEB927508733DD40010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ COPY_PHASE_STRIP = NO;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = ValidateStoreReceipt_Prefix.pch;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = "-lcrypto";
+ PRODUCT_NAME = ValidateStoreReceipt;
+ RUN_CLANG_STATIC_ANALYZER = YES;
+ };
+ name = Debug;
+ };
+ 1DEB927608733DD40010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = ValidateStoreReceipt_Prefix.pch;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ INSTALL_PATH = /usr/local/bin;
+ OTHER_LDFLAGS = "-lcrypto";
+ PRODUCT_NAME = ValidateStoreReceipt;
+ RUN_CLANG_STATIC_ANALYZER = YES;
+ };
+ name = Release;
+ };
+ 1DEB927908733DD40010E9CD /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_GC = supported;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ RUN_CLANG_STATIC_ANALYZER = YES;
+ SDKROOT = macosx10.5;
+ };
+ name = Debug;
+ };
+ 1DEB927A08733DD40010E9CD /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_ENABLE_OBJC_GC = supported;
+ GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ PREBINDING = NO;
+ RUN_CLANG_STATIC_ANALYZER = YES;
+ SDKROOT = macosx10.5;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 1DEB927408733DD40010E9CD /* Build configuration list for PBXNativeTarget "ValidateStoreReceipt" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB927508733DD40010E9CD /* Debug */,
+ 1DEB927608733DD40010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 1DEB927808733DD40010E9CD /* Build configuration list for PBXProject "ValidateStoreReceipt" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 1DEB927908733DD40010E9CD /* Debug */,
+ 1DEB927A08733DD40010E9CD /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}
7 ValidateStoreReceipt_Prefix.pch
@@ -0,0 +1,7 @@
+//
+// Prefix header for all source files of the 'ValidateStoreReceipt' target in the 'ValidateStoreReceipt' project.
+//
+
+#ifdef __OBJC__
+ #import <Foundation/Foundation.h>
+#endif
20 validatereceipt.h
@@ -0,0 +1,20 @@
+//
+// validatereceipt.h
+//
+// Created by Ruotger Skupin on 23.10.10.
+// Copyright 2010 Matthew Stevens, Ruotger Skupin, Apple. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+
+extern NSString *kReceiptBundleIdentifer;
+extern NSString *kReceiptBundleIdentiferData;
+extern NSString *kReceiptVersion;
+extern NSString *kReceiptOpaqueValue;
+extern NSString *kReceiptHash;
+
+
+NSDictionary * dictionaryWithAppStoreReceipt(NSString * path);
+BOOL validateReceiptAtPath(NSString * path);
+
259 validatereceipt.m
@@ -0,0 +1,259 @@
+//
+// validatereceipt.m
+//
+// Created by Ruotger Skupin on 23.10.10.
+// Copyright 2010 Matthew Stevens, Ruotger Skupin, Apple. All rights reserved.
+//
+
+#import "validatereceipt.h"
+
+// link with Foundation.framework, IOKit.framework and libCrypto (via -lcrypto)
+
+#import <IOKit/IOKitLib.h>
+#import <Foundation/Foundation.h>
+
+#include <openssl/pkcs7.h>
+#include <openssl/objects.h>
+#include <openssl/sha.h>
+
+
+NSString *kReceiptBundleIdentifer = @"BundleIdentifier";
+NSString *kReceiptBundleIdentiferData = @"BundleIdentifierData";
+NSString *kReceiptVersion = @"Version";
+NSString *kReceiptOpaqueValue = @"OpaqueValue";
+NSString *kReceiptHash = @"Hash";
+
+const BOOL doDebug = YES;
+
+
+NSDictionary * dictionaryWithAppStoreReceipt(NSString * path)
+{
+ enum ATTRIBUTES
+ {
+ ATTR_START = 1,
+ BUNDLE_ID,
+ VERSION,
+ OPAQUE_VALUE,
+ HASH,
+ ATTR_END
+ };
+
+ // Expected input is a PKCS7 container with signed data containing
+ // an ASN.1 SET of SEQUENCE structures. Each SEQUENCE contains
+ // two INTEGERS and an OCTET STRING.
+
+ const char * receiptPath = [[path stringByStandardizingPath] fileSystemRepresentation];
+ FILE *fp = fopen(receiptPath, "rb");
+ if (fp == NULL)
+ return nil;
+
+ PKCS7 *p7 = d2i_PKCS7_fp(fp, NULL);
+ fclose(fp);
+
+ if (!PKCS7_type_is_signed(p7)) {
+ PKCS7_free(p7);
+ return nil;
+ }
+
+ if (!PKCS7_type_is_data(p7->d.sign->contents)) {
+ PKCS7_free(p7);
+ return nil;
+ }
+
+ ASN1_OCTET_STRING *octets = p7->d.sign->contents->d.data;
+ unsigned char *p = octets->data;
+ unsigned char *end = p + octets->length;
+
+ int type = 0;
+ int xclass = 0;
+ long length = 0;
+
+ ASN1_get_object(&p, &length, &type, &xclass, end - p);
+ if (type != V_ASN1_SET) {
+ PKCS7_free(p7);
+ return nil;
+ }
+
+ NSMutableDictionary *info = [NSMutableDictionary dictionary];
+
+ while (p < end) {
+ ASN1_get_object(&p, &length, &type, &xclass, end - p);
+ if (type != V_ASN1_SEQUENCE)
+ break;
+
+ const unsigned char *seq_end = p + length;
+
+ int attr_type = 0;
+ int attr_version = 0;
+
+ // Attribute type
+ ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
+ if (type == V_ASN1_INTEGER && length == 1) {
+ attr_type = p[0];
+ }
+ p += length;
+
+ // Attribute version
+ ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
+ if (type == V_ASN1_INTEGER && length == 1) {
+ attr_version = p[0]; // <-- yes, this is never read, the analyzer complains but I wont change it...
+ }
+ p += length;
+
+ // Only parse attributes we're interested in
+ if (attr_type > ATTR_START && attr_type < ATTR_END) {
+ NSString *key;
+
+ ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
+ if (type == V_ASN1_OCTET_STRING) {
+
+ // Bytes
+ if (attr_type == BUNDLE_ID || attr_type == OPAQUE_VALUE || attr_type == HASH) {
+ NSData *data = [NSData dataWithBytes:p length:length];
+
+ switch (attr_type) {
+ case BUNDLE_ID:
+ // This is included for hash generation
+ key = kReceiptBundleIdentiferData;
+ break;
+ case OPAQUE_VALUE:
+ key = kReceiptOpaqueValue;
+ break;
+ case HASH:
+ key = kReceiptHash;
+ break;
+ }
+
+ [info setObject:data forKey:key];
+ }
+
+ // Strings
+ if (attr_type == BUNDLE_ID || attr_type == VERSION) {
+ int str_type = 0;
+ long str_length = 0;
+ unsigned char *str_p = p;
+ ASN1_get_object(&str_p, &str_length, &str_type, &xclass, seq_end - str_p);
+ if (str_type == V_ASN1_UTF8STRING) {
+ NSString *string = [[[NSString alloc] initWithBytes:str_p
+ length:str_length
+ encoding:NSUTF8StringEncoding] autorelease];
+
+ switch (attr_type) {
+ case BUNDLE_ID:
+ key = kReceiptBundleIdentifer;
+ break;
+ case VERSION:
+ key = kReceiptVersion;
+ break;
+ }
+
+ [info setObject:string forKey:key];
+ }
+ }
+ }
+ p += length;
+ }
+
+ // Skip any remaining fields in this SEQUENCE
+ while (p < seq_end) {
+ ASN1_get_object(&p, &length, &type, &xclass, seq_end - p);
+ p += length;
+ }
+ }
+
+ PKCS7_free(p7);
+
+ return info;
+}
+
+
+
+// Returns a CFData object, containing the machine's GUID.
+CFDataRef copy_mac_address(void)
+{
+ kern_return_t kernResult;
+ mach_port_t master_port;
+ CFMutableDictionaryRef matchingDict;
+ io_iterator_t iterator;
+ io_object_t service;
+ CFDataRef macAddress = nil;
+
+ kernResult = IOMasterPort(MACH_PORT_NULL, &master_port);
+ if (kernResult != KERN_SUCCESS) {
+ printf("IOMasterPort returned %d\n", kernResult);
+ return nil;
+ }
+
+ matchingDict = IOBSDNameMatching(master_port, 0, "en0");
+ if(!matchingDict) {
+ printf("IOBSDNameMatching returned empty dictionary\n");
+ return nil;
+ }
+
+ kernResult = IOServiceGetMatchingServices(master_port, matchingDict, &iterator);
+ if (kernResult != KERN_SUCCESS) {
+ printf("IOServiceGetMatchingServices returned %d\n", kernResult);
+ return nil;
+ }
+
+ while((service = IOIteratorNext(iterator)) != 0)
+ {
+ io_object_t parentService;
+
+ kernResult = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parentService);
+ if(kernResult == KERN_SUCCESS)
+ {
+ if(macAddress) CFRelease(macAddress);
+
+ macAddress = IORegistryEntryCreateCFProperty(parentService, CFSTR("IOMACAddress"), kCFAllocatorDefault, 0);
+ IOObjectRelease(parentService);
+ }
+ else {
+ printf("IORegistryEntryGetParentEntry returned %d\n", kernResult);
+ }
+
+ IOObjectRelease(service);
+ }
+
+ return macAddress;
+}
+
+
+BOOL validateReceiptAtPath(NSString * path)
+{
+ NSDictionary * receipt = dictionaryWithAppStoreReceipt(path);
+
+ if (!receipt)
+ return NO;
+
+ NSData * guidData = (NSData*)copy_mac_address();
+
+ if ([NSGarbageCollector defaultCollector])
+ [[NSGarbageCollector defaultCollector] enableCollectorForPointer:guidData];
+ else
+ [guidData autorelease];
+
+ if (!guidData)
+ return NO;
+
+ if (doDebug)
+ {
+ // Overwrite with example GUID for use with example receipt
+ unsigned char guid[] = { 0x00, 0x17, 0xf2, 0xc4, 0xbc, 0xc0 };
+ guidData = [NSData dataWithBytes:guid length:sizeof(guid)];
+ }
+
+ NSMutableData *input = [NSMutableData data];
+ [input appendData:guidData];
+ [input appendData:[receipt objectForKey:kReceiptOpaqueValue]];
+ [input appendData:[receipt objectForKey:kReceiptBundleIdentiferData]];
+
+ NSMutableData *hash = [NSMutableData dataWithLength:SHA_DIGEST_LENGTH];
+ SHA1([input bytes], [input length], [hash mutableBytes]);
+ if ([hash isEqualToData:[receipt objectForKey:kReceiptHash]])
+ {
+ return YES;
+ }
+
+ return NO;
+}
Please sign in to comment.
Something went wrong with that request. Please try again.