在运行时获取配置文件和证书的详细信息 [英] Get details of provisioning profile and certificate at runtime

查看:524
本文介绍了在运行时获取配置文件和证书的详细信息的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在我的应用中获取并显示我的配置文件和分发证书的详细信息(例如到期日期和注册公司)。我已经尝试过这个但是它在我的应用程序中无法正常工作。它最初为 profilePath 提供nil。

I want to fetch and display the details (such as expiry date and registered company) of my provisioning profile and distribution certificate in my app. I have already tried this but it doesn't work correctly in my app. It give nil for profilePath initially itself.

我使用的是swift 2.3和Xcode 8.2.1。我试图将该代码混合并匹配到我的应用程序中,因为我无法将其完全转换为swift(卡在 sscanf 方法)。任何帮助表示赞赏。

I am using swift 2.3 and Xcode 8.2.1. I have tried to mix and match that code into my app as I could not convert it to swift entirely (got stuck at sscanf method). Any help is appreciated.

推荐答案

我无法访问Xcode 8 / Swift 3.2,但这里是你需要的代码Swift 4.我已经在我可以使用的几个配置文件/证书上测试了它,它获取了您要求的信息。

I don't have access to Xcode 8 / Swift 3.2, but here is the code needed to do what you want in Swift 4. I've tested it on a couple of profiles / certs I have available to me, and it gets the information you are requesting.

供应资料

func getProvisioningProfileExpirationDate() -> Date?
{
    self.getCertificateExpirationDate()

    let profilePath: String? = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision")
    if( profilePath != nil )
    {
        let plistData = NSData(contentsOfFile: profilePath!)
        let plistDataString = String(format: "%@", plistData!)
        var plistString: String = extractPlist(fromMobileProvisionDataString:plistDataString)

        let pattern = "<key>ExpirationDate</key>.*<date>(.*)</date>"
        let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
        let textCheckingResult : NSTextCheckingResult = regex.firstMatch(in: plistString, options: NSRegularExpression.MatchingOptions(rawValue: UInt(0)), range: NSMakeRange(0, plistString.characters.count))!
        let matchRange : NSRange = textCheckingResult.range(at: 1)
        let expirationDateString : String = (plistString as NSString).substring(with: matchRange)


        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale.current
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
        print( "Profile expires: \(dateFormatter.date(from: expirationDateString)!)" )

        return dateFormatter.date(from: expirationDateString)!

    }

    return nil
}

我们需要做一些操作,因为embedded.mobileprovision文件如果不从十六进制转换它是不可读的,然后只取出plist标签之间的东西。

We need to do some manipulation, since the embedded.mobileprovision file is not readable without converting it from hex, and then pulling out just the stuff between the plist tags.

func extractPlist( fromMobileProvisionDataString:String ) -> String
{
    // Remove brackets at beginning and end
    var range = Range(NSMakeRange(0, 1), in: fromMobileProvisionDataString)
    var plistDataString = fromMobileProvisionDataString.replacingCharacters(in:range!, with: "")
    range = Range(NSMakeRange(plistDataString.count-1, 1), in: plistDataString)
    plistDataString.replaceSubrange(range!, with: "")

    // Remove spaces
    plistDataString = plistDataString.replacingOccurrences(of: " ", with: "")

    // convert hex to ascii
    let profileText = hexStringtoAscii( plistDataString )

    // I tried using regular expressions and normal NSString operations to get this, but it simply wouldn't work, so I went with this ugly method.
    // return extractPlistText(fromProfileString:profileText)

    // Remove whitespaces and new lines characters and splits into individual lines.
    let profileWords = profileText.components(separatedBy: CharacterSet.newlines)

    var plistString = "";
    var inPlist = false;
    for word in profileWords
    {
        if( word.contains("<plist") ) { inPlist = true }

        if( inPlist ) {  plistString.append(" "); plistString.append( word ) }

        if (word.contains("</plist")) { inPlist = false }
    }
    return plistString;
}

func hexStringtoAscii(_ hexString : String) -> String {

    let pattern = "(0x)?([0-9a-f]{2})"
    let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
    let nsString = hexString as NSString
    let matches = regex.matches(in: hexString, options: [], range: NSMakeRange(0, nsString.length))
    let characters = matches.map {
        Character(UnicodeScalar(UInt32(nsString.substring(with: $0.range(at: 2)), radix: 16)!)!)
    }
    return String(characters)
}

我已经验证了这一点用于从物理设备上的embedded.mobileprovision文件中提取到期日期。从配置文件plist数据中提取其他元素是微不足道的。

I've verified this works to pull out the expiration date from the embedded.mobileprovision file on a physical device. It would be trivial to pull out other elements from the profile plist data.

证书:

要获取证书信息,我能够获得使用以下方法工作:

To get the certificate information, I was able to get it to work using the following:

func getCertificateExpirationDate() -> Date?
{

    let profilePath: String? = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision")
    if( profilePath != nil )
    {
        let plistData = NSData(contentsOfFile: profilePath!)
        let plistDataString = String(format: "%@", plistData!)
        var plistString: String = extractPlist(fromMobileProvisionDataString:plistDataString)

        // Trying to extract thecert information aswell, but haven't gotten it to work.

        let certPattern = "<key>DeveloperCertificates</key>\\s*<array>\\s*<data>([^<]*)</data>"
        let certRegex = try! NSRegularExpression(pattern: certPattern, options: .caseInsensitive)
        let certCheckingResult : NSTextCheckingResult = certRegex.firstMatch(in: plistString, options: NSRegularExpression.MatchingOptions(rawValue: UInt(0)), range: NSMakeRange(0, plistString.characters.count))!
        let certMatchRange : NSRange = certCheckingResult.range(at: 1)
        let certDataString : String = (plistString as NSString).substring(with: certMatchRange)

        let decodedData = Data(base64Encoded: certDataString, options: [])

        let decodedString = String( data: decodedData!, encoding: .ascii )

        let cfData = decodedData as! CFData
        let certificate: SecCertificate = SecCertificateCreateWithData(nil, cfData)!
        var description: CFString = SecCertificateCopySubjectSummary(certificate)!
        print( "Certificate name: \(description)")

        let certDate = self.extractCertExpirationDate(fromDecodedCertDataString: decodedString!)
        print( "Certificate expires: \(certDate)")

        let certOrg = self.extractCertOrg(fromDecodedCertDataString: decodedString!)
        print( "Certificate organization: \(certOrg)")

        return certDate
    }
    return nil
}

func extractCertExpirationDate( fromDecodedCertDataString: String ) -> Date
{
    // Remove new lines characters and split into individual lines.
    let certWords = fromDecodedCertDataString.components(separatedBy: CharacterSet.newlines)

    var foundWWDRCA = false;
    var certStartDate = ""
    var certEndDate = ""
    var certOrg = ""

    for word in certWords
    {
        if( foundWWDRCA && (certStartDate.isEmpty || certEndDate.isEmpty))
        {
            var certData = word.prefix(13)
            if( certStartDate.isEmpty && !certData.isEmpty )
            {
                certStartDate = String( certData );
            }
            else if( certEndDate.isEmpty && !certData.isEmpty )
            {
                certEndDate = String( certData );
            }
        }
        if( word.contains("Apple Worldwide Developer Relations Certification Authority") ) { foundWWDRCA = true }
    }

    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale.current
    dateFormatter.dateFormat = "yyMMddHHmmssZ"
    return dateFormatter.date(from: certEndDate)!
}

func extractCertOrg( fromDecodedCertDataString: String ) -> String
{
    // Remove new lines characters and split into individual lines.
    let certWords = fromDecodedCertDataString.components(separatedBy: CharacterSet.newlines)

    var foundWWDRCA = false;
    var certStartDate = ""
    var certEndDate = ""
    var certOrg = ""

    for word in certWords
    {
        if( foundWWDRCA && (certStartDate.isEmpty || certEndDate.isEmpty))
        {
            var certData = word.prefix(13)
            if( certStartDate.isEmpty && !certData.isEmpty )
            {
                certStartDate = String( certData );
            }
            else if( certEndDate.isEmpty && !certData.isEmpty )
            {
                certEndDate = String( certData );
            }
        }
        else if( foundWWDRCA && word.contains("\u{17}") && certOrg.isEmpty)
        {
            var orgString = word.suffix(word.count-1)
            certOrg = String( orgString.prefix(orgString.count - 1))
        }

        if( word.contains("Apple Worldwide Developer Relations Certification Authority") ) { foundWWDRCA = true }
    }
    return certOrg
}

请注意,这仅检查安装时与应用程序捆绑在一起的配置文件/证书。它不会检查设备上的其他可能有效的配置文件。因此,即使嵌入的配置文件已过期,如果在设备上安装配置文件的其他机制(设备管理,使用较新的通配符配置文件安装另一个应用程序等),应用程序仍有可能仍然可以运行。但是,如果用于对应用程序签名的证书已过期,则即使设备上存在较新的配置文件,也不会运行该证书。

Note that this only checks the provisioning profile / cert that is bundled with the app when installed. It will not check other, potentially valid, profiles on the device. So even if the embedded profile has expired, there is a chance the app could still run, if there are other mechanisms for installing profiles on the device that are used (device management, installing another app with newer wildcard provisioning profile, etc. ). However, if the certificate used to sign the app has expired, it will not run, even if a newer provisioning profile exists on the device.

对于证书信息,我仍然认为最安全的方法是使用openssl库来描述DER编码的x509证书,但我能够在之后进行解析base64解码证书数据似乎可以提取您需要的信息。

For the certificate information, I still think the safest way would be to use the openssl library to decript the DER encoded x509 certificate, but the parsing I was able to do after base64 decoding the certificate data seems to pull the information you need.

这篇关于在运行时获取配置文件和证书的详细信息的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆