菜鸟教程小白 发表于 2022-12-13 15:55:26

ios - 在运行时获取配置文件和证书的详细信息


                                            <p><p>我想在我的应用中获取并显示我的配置文件和分发证书的详细信息(例如到期日期和注册公司)。我已经尝试过<a href="https://stackoverflow.com/questions/18961267/get-the-expiration-date-of-a-provisioning-profile-at-run-time" rel="noreferrer noopener nofollow">this</a>但它在我的应用程序中无法正常工作。它最初为 <code>profilePath</code> 本身提供 nil。 </p>

<p>我正在使用 swift 2.3 和 Xcode 8.2.1。我试图将该代码混合并匹配到我的应用程序中,因为我无法将其完全转换为 swift(卡在 <code>sscanf</code> 方法中)。任何帮助表示赞赏。 </p></p>
                                    <br><hr><h1><strong>Best Answer-推荐答案</ strong></h1><br>
                                            <p><p>我无法访问 Xcode 8/Swift 3.2,但这里是在 Swift 4 中执行您想要的操作所需的代码。我已经在几个可用的配置文件/证书上对其进行了测试,并且它会获取您请求的信息。 </p>

<p><strong>配置文件</strong></p>

<pre><code>func getProvisioningProfileExpirationDate() -&gt; Date?
{
    self.getCertificateExpirationDate()

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

      let pattern = &#34;&lt;key&gt;ExpirationDate&lt;/key&gt;.*&lt;date&gt;(.*)&lt;/date&gt;&#34;
      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 = &#34;yyyy-MM-dd&#39;T&#39;HH:mm:ssZ&#34;
      print( &#34;Profile expires: \(dateFormatter.date(from: expirationDateString)!)&#34; )

      return dateFormatter.date(from: expirationDateString)!

    }

    return nil
}
</code></pre>

<p>我们需要进行一些操作,因为 Embedded.mobileprovision 文件如果不从十六进制转换就无法读取,然后只提取 plist 标记之间的内容。 </p>

<pre><code>func extractPlist( fromMobileProvisionDataString:String ) -&gt; String
{
    // Remove brackets at beginning and end
    var range = Range(NSMakeRange(0, 1), in: fromMobileProvisionDataString)
    var plistDataString = fromMobileProvisionDataString.replacingCharacters(in:range!, with: &#34;&#34;)
    range = Range(NSMakeRange(plistDataString.count-1, 1), in: plistDataString)
    plistDataString.replaceSubrange(range!, with: &#34;&#34;)

    // Remove spaces
    plistDataString = plistDataString.replacingOccurrences(of: &#34; &#34;, with: &#34;&#34;)

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

    // I tried using regular expressions and normal NSString operations to get this, but it simply wouldn&#39;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 = &#34;&#34;;
    var inPlist = false;
    for word in profileWords
    {
      if( word.contains(&#34;&lt;plist&#34;) ) { inPlist = true }

      if( inPlist ) {plistString.append(&#34; &#34;); plistString.append( word ) }

      if (word.contains(&#34;&lt;/plist&#34;)) { inPlist = false }
    }
    return plistString;
}

func hexStringtoAscii(_ hexString : String) -&gt; String {

    let pattern = &#34;(0x)?({2})&#34;
    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)
}
</code></pre>

<p>我已经验证这可以从物理设备上的 embedded.mobileprovision 文件中提取到期日期。从配置文件 plist 数据中提取其他元素是微不足道的。 </p>

<p><strong>证书:</strong> </p>

<p>要获取证书信息,我可以使用以下方法使其工作:</p>

<pre><code>func getCertificateExpirationDate() -&gt; Date?
{

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

      // Trying to extract thecert information aswell, but haven&#39;t gotten it to work.

      let certPattern = &#34;&lt;key&gt;DeveloperCertificates&lt;/key&gt;\\s*&lt;array&gt;\\s*&lt;data&gt;([^&lt;]*)&lt;/data&gt;&#34;
      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( &#34;Certificate name: \(description)&#34;)

      let certDate = self.extractCertExpirationDate(fromDecodedCertDataString: decodedString!)
      print( &#34;Certificate expires: \(certDate)&#34;)

      let certOrg = self.extractCertOrg(fromDecodedCertDataString: decodedString!)
      print( &#34;Certificate organization: \(certOrg)&#34;)

      return certDate
    }
    return nil
}

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

    var foundWWDRCA = false;
    var certStartDate = &#34;&#34;
    var certEndDate = &#34;&#34;
    var certOrg = &#34;&#34;

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

    let dateFormatter = DateFormatter()
    dateFormatter.locale = Locale.current
    dateFormatter.dateFormat = &#34;yyMMddHHmmssZ&#34;
    return dateFormatter.date(from: certEndDate)!
}

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

    var foundWWDRCA = false;
    var certStartDate = &#34;&#34;
    var certEndDate = &#34;&#34;
    var certOrg = &#34;&#34;

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

      if( word.contains(&#34;Apple Worldwide Developer Relations Certification Authority&#34;) ) { foundWWDRCA = true }
    }
    return certOrg
}
</code></pre>

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

<p>对于证书信息,我仍然认为最安全的方法是使用openssl库来解密DER编码的x509证书,但是在base64解码证书数据后我能够做的解析似乎提取了你需要的信息. </p></p>
                                   
                                                <p style="font-size: 20px;">关于ios - 在运行时获取配置文件和证书的详细信息,我们在Stack Overflow上找到一个类似的问题:
                                                        <a href="https://stackoverflow.com/questions/49188337/" rel="noreferrer noopener nofollow" style="color: red;">
                                                                https://stackoverflow.com/questions/49188337/
                                                        </a>
                                                </p>
                                       
页: [1]
查看完整版本: ios - 在运行时获取配置文件和证书的详细信息