How to detect that a provisioning profile is for development or distribution, programmatically

后端 未结 4 2118
庸人自扰
庸人自扰 2020-12-08 14:41

I would like to detect if a given provisioning profile is a development profile or a distribution (adhoc or app store) profile. I need to do this purely programmatically.

相关标签:
4条回答
  • 2020-12-08 14:54

    This was something I tackled in one of my own build systems for much the same purpose...let's take a trip back in time to Day 1 of the then 'iPhone Developer Program'. If you were around the community at that time, you may remember that the toolchain was...shall we say less friendly...than it is today.

    When you wanted to build for the AppStore or for AdHoc builds you had to make this curious entitlements.plist file, then paste a blob of XML into the body of that file. You then ran the build and at that time what appeared to be magic occurred and the sheer presence of that file made the build work, allowed you to manually construct your IPA, and carry on with business as usual. Now that we are a few years older and hopefully a bit wiser than in those early days of the SDK, we have come to recognize that the magic XML blob wasn't actually so magical at all -- the 'get-task-allow' key is a setting to indicate if the binary should allow other processes (like perhaps a debugger) to attach to the binary. When signing apps using a Development Provisioning Profile, this key will be set to 'true' (and thus allow LLDB to attach and interact with your app)...and naturally when signing apps using a Distribution Provisioning Profile, this key will be set to 'false'.

    Apple has provided some updates in Tech Note TN2250 about reading the XML (and by extension the entitlements) out of Provisioning Profiles:

    security cms -D -i /path/to/the.app/embedded.mobileprovision

    This will return the XML in the Provisioning profile -- from there you can parse out the key value pair for 'get-task-allow' and use that value to determine if the Provisioning Profile is Development or Distribution.

    I absolutely agree that it would be nice to have a tool that would tell us that directly so we don't have to sniff through the profile for clues, but at the same time, at least we have a highly reliable, albeit roundabout way to make that distinction before running off and making a build we can't use.

    Good luck and let me know if you need any more clarification or have other questions.

    0 讨论(0)
  • 2020-12-08 14:55

    Based on Bryan Musial great answer I wrote some code that allow you to check "get-task-allow" directly from application at runtime. In my case I'm using this boolean to only log in debug apps :

    + (BOOL)isDevelopmentApp
    {
        // Special case of simulator
        if (isSimulator)
        {
            return YES;
        }
    
        // There is no provisioning profile in AppStore Apps
        NSString *profilePath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
    
        // Check provisioning profile existence
        if (profilePath)
        {
            // Get hex representation
            NSData *profileData = [NSData dataWithContentsOfFile:profilePath];
            NSString *profileString = [NSString stringWithFormat:@"%@", profileData];
    
            // Remove brackets at beginning and end
            profileString = [profileString stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@""];
            profileString = [profileString stringByReplacingCharactersInRange:NSMakeRange(profileString.length - 1, 1) withString:@""];
    
            // Remove spaces
            profileString = [profileString stringByReplacingOccurrencesOfString:@" " withString:@""];
    
            // Convert hex values to readable characters
            NSMutableString *profileText = [NSMutableString new];
            for (int i = 0; i < profileString.length; i += 2)
            {
                NSString *hexChar = [profileString substringWithRange:NSMakeRange(i, 2)];
                int value = 0;
                sscanf([hexChar cStringUsingEncoding:NSASCIIStringEncoding], "%x", &value);
                [profileText appendFormat:@"%c", (char)value];
            }
    
            // Remove whitespaces and new lines characters
            NSArray *profileWords = [profileText componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
            NSString *profileClearText = [profileWords componentsJoinedByString:@""];
    
            // Look for debug value
            NSRange debugRange = [profileClearText rangeOfString:@"<key>get-task-allow</key><true/>"];
            if (debugRange.location != NSNotFound)
            {
                return YES;
            }
        }
    
        // Return NO by default to avoid security leaks
        return NO;
    }
    
    0 讨论(0)
  • 2020-12-08 14:59

    Here is a version for Swift 3, based on @steipete's answer:

    static func isDevelopmentProvisioningProfile() -> Bool {
    #if IOS_SIMULATOR
        return true
    #else
        // there will be no provisioning profile in AppStore Apps
        guard let fileName = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision") else {
            return false
        }
    
        let fileURL = URL(fileURLWithPath: fileName)
        // the documentation says this file is in UTF-8, but that failed
        // on my machine. ASCII encoding worked ¯\_(ツ)_/¯
        guard let data = try? String(contentsOf: fileURL, encoding: .ascii) else {
            return false
        }
    
        let cleared: String = data.components(separatedBy: .whitespacesAndNewlines).joined()
        return cleared.contains("<key>get-task-allow</key><true/>")
    #endif
    }
    

    If curious, get-task-allow is a flag that the build uses to determine whether you should be able to hook up a debugger and other processes like that - so it's quite accurate for whether it is a dev build or no.

    0 讨论(0)
  • 2020-12-08 15:12

    I've build a more concise and efficient version of Toom's code:

    I'll maintain code snippets like this in a gist, you might find a more up to date version here: https://gist.github.com/steipete/7668246

    static BOOL PSPDFIsDevelopmentBuild(void) {
    #if TARGET_IPHONE_SIMULATOR
    return YES;
    #else
    static BOOL isDevelopment = NO;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // There is no provisioning profile in AppStore Apps.
        NSData *data = [NSData dataWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"]];
        if (data) {
            const char *bytes = [data bytes];
            NSMutableString *profile = [[NSMutableString alloc] initWithCapacity:data.length];
            for (NSUInteger i = 0; i < data.length; i++) {
                [profile appendFormat:@"%c", bytes[i]];
            }
            // Look for debug value, if detected we're a development build.
            NSString *cleared = [[profile componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet] componentsJoinedByString:@""];
            isDevelopment = [cleared rangeOfString:@"<key>get-task-allow</key><true/>"].length > 0;
        }
    });
    return isDevelopment;
    #endif
    }
    
    0 讨论(0)
提交回复
热议问题