Skip to content

Instantly share code, notes, and snippets.

@perlmunger
Last active August 20, 2024 14:39

Revisions

  1. perlmunger revised this gist Feb 3, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion WrapperImplementation.swift
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    guard let url = Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision"), let data = try? Data(contentsOf: url), let string = String(data: data, encoding: String.Encoding.isoLatin1) else {
    return
    }
    }

    // Extract the relevant part of the data string from the start of the opening plist tag
    // to the ending one.
  2. perlmunger revised this gist Jan 31, 2020. No changes.
  3. perlmunger revised this gist Jan 31, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion SecCertificateWrapper.swift
    Original file line number Diff line number Diff line change
    @@ -40,7 +40,7 @@ struct SecCertificateWrapper : Comparable {
    return (self.format(notValidBeforeDate), self.format(notValidAfterDate))
    }

    // Some convenience properties for access the notValidBefore and notValidAfter
    // Some convenience properties for access to the notValidBefore and notValidAfter
    // dates in various formats
    var notValidAfterUnixDate : Double {
    return self.validDates?.notValidAfter.timeIntervalSince1970 ?? 0
  4. perlmunger revised this gist Jan 31, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions SecCertificateWrapper.swift
    Original file line number Diff line number Diff line change
    @@ -7,6 +7,7 @@ struct SecCertificateWrapper : Comparable {
    // array (see WrapperImplementation.swift)
    init(data:Data) {
    self.cert = SecCertificateCreateWithData(nil, data as CFData)!
    // Use this later for parsing the date details from the cert
    self.data = data
    }

  5. perlmunger revised this gist Jan 31, 2020. 1 changed file with 4 additions and 3 deletions.
    7 changes: 4 additions & 3 deletions WrapperImplementation.swift
    Original file line number Diff line number Diff line change
    @@ -26,14 +26,15 @@ if scanner.scanUpTo("<plist", into: nil) {
    let ttl = mobileProvision["TimeToLive"] as! Int
    let name = mobileProvision["Name"] as! String

    // Grab the nested entitlements dictionary
    // Grab the nested entitlements dictionary and relevant properties
    let entitlements = mobileProvision["Entitlements"] as! [String:Any]
    var taskAllow = entitlements["get-task-allow"] as? Bool ?? false
    let appId = entitlements["application-identifier"] as! String
    let teamId = entitlements["com.apple.developer.team-identifier"] as! String

    // Iterate through the developer certificates array sorted by notValidAfter date
    // and create and print SecCertificateWrapper objects
    // Iterate through the DeveloperCertificates array sorted by notValidAfter date
    // (see Comparable implementation in the SecCertificateWrapper.swift file) and
    // create and print SecCertificateWrapper objects
    if let certData = mobileProvision["DeveloperCertificates"] as? [Data] {
    for cert in certData.map( { SecCertificateWrapper(data: $0) }).sorted() {
    debugPrint("Name: \(cert.name), Not Valid After Date: \(cert.notValidAfterUnixDateAsString)")
  6. perlmunger revised this gist Jan 31, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion SecCertificateWrapper.swift
    Original file line number Diff line number Diff line change
    @@ -18,7 +18,7 @@ struct SecCertificateWrapper : Comparable {
    // Return a tuple with both notValidBefore and notValidAfter dates
    var validDates : (notValidBefore:Date, notValidAfter:Date)? {
    guard let decodedString = String( data: self.data, encoding: .ascii ) else { return nil }
    var foundWWDRCA = false;
    var foundWWDRCA = false
    var notValidBeforeDate = ""
    var notValidAfterDate = ""

  7. perlmunger revised this gist Jan 31, 2020. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions SecCertificateWrapper.swift
    Original file line number Diff line number Diff line change
    @@ -15,7 +15,7 @@ struct SecCertificateWrapper : Comparable {
    return SecCertificateCopySubjectSummary(cert)! as String
    }

    // Return a tuple with both notValidBefore and notValidAfterDates
    // Return a tuple with both notValidBefore and notValidAfter dates
    var validDates : (notValidBefore:Date, notValidAfter:Date)? {
    guard let decodedString = String( data: self.data, encoding: .ascii ) else { return nil }
    var foundWWDRCA = false;
    @@ -39,8 +39,8 @@ struct SecCertificateWrapper : Comparable {
    return (self.format(notValidBeforeDate), self.format(notValidAfterDate))
    }

    // Some convenience properties for access the two dates in various
    // formats
    // Some convenience properties for access the notValidBefore and notValidAfter
    // dates in various formats
    var notValidAfterUnixDate : Double {
    return self.validDates?.notValidAfter.timeIntervalSince1970 ?? 0
    }
  8. perlmunger revised this gist Jan 31, 2020. 2 changed files with 28 additions and 10 deletions.
    12 changes: 11 additions & 1 deletion SecCertificateWrapper.swift
    Original file line number Diff line number Diff line change
    @@ -3,15 +3,19 @@ struct SecCertificateWrapper : Comparable {
    var data:Data
    var cert:SecCertificate

    // Initialize with a data object from the "DeveloperCertificates"
    // array (see WrapperImplementation.swift)
    init(data:Data) {
    self.cert = SecCertificateCreateWithData(nil, data as CFData)!
    self.data = data
    }


    // The certificate name
    var name : String {
    return SecCertificateCopySubjectSummary(cert)! as String
    }

    // Return a tuple with both notValidBefore and notValidAfterDates
    var validDates : (notValidBefore:Date, notValidAfter:Date)? {
    guard let decodedString = String( data: self.data, encoding: .ascii ) else { return nil }
    var foundWWDRCA = false;
    @@ -35,6 +39,8 @@ struct SecCertificateWrapper : Comparable {
    return (self.format(notValidBeforeDate), self.format(notValidAfterDate))
    }

    // Some convenience properties for access the two dates in various
    // formats
    var notValidAfterUnixDate : Double {
    return self.validDates?.notValidAfter.timeIntervalSince1970 ?? 0
    }
    @@ -52,17 +58,21 @@ struct SecCertificateWrapper : Comparable {
    }

    // MARK: - Utilities
    // Implement comparable by providing a less than operator
    static func < (lhs: SecCertificateWrapper, rhs: SecCertificateWrapper) -> Bool {
    guard let lhsDate = lhs.validDates?.notValidAfter, let rhsDate = rhs.validDates?.notValidAfter else { return true }
    return lhsDate < rhsDate
    }

    // Create a static data formatter just for convenience
    static let certificateDateFormatter : DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyMMddHHmmssZ"
    return formatter
    }()

    // Provide a format function to help shorten the code where date
    // formatting is performed
    func format(_ date:String) -> Date {
    return SecCertificateWrapper.certificateDateFormatter.date(from: date)!
    }
    26 changes: 17 additions & 9 deletions WrapperImplementation.swift
    Original file line number Diff line number Diff line change
    @@ -1,31 +1,39 @@
    // This implementation file shows how to extract details for the provisioning profile
    // but also demonstrates how to grab the certificate info from the DeveloperCertificates
    // arry found in the plist

    guard let url = Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision"), let data = try? Data(contentsOf: url), let string = String(data: data, encoding: String.Encoding.isoLatin1) else {
    return
    }

    // Extract the relevant part of the data string from the start of the opening plist tag
    // to the ending one.
    let scanner = Scanner(string: string)
    if scanner.scanUpTo("<plist", into: nil) {
    var plistString:NSString? = ""
    if scanner.scanUpTo("</plist>", into: &plistString) {
    plistString = (plistString! as String) + "</plist>" as NSString

    if let plistData = plistString?.data(using: String.Encoding.isoLatin1.rawValue) {
    // Load the property list into a dictionary
    if let mobileProvision = try? PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String:Any] {

    // Grab all of the profile properties
    let appName = mobileProvision["AppIDName"] as! String
    let creationDate = mobileProvision["CreationDate"] as! Date
    let expirationDate = mobileProvision["ExpirationDate"] as! Date
    let entitlements = mobileProvision["Entitlements"] as! [String:Any]
    var taskAllow = false
    if let allow = entitlements["get-task-allow"] as? Bool{
    taskAllow = allow
    }
    let appId = entitlements["application-identifier"] as! String
    let teamId = entitlements["com.apple.developer.team-identifier"] as! String
    let teamName = mobileProvision["TeamName"] as! String
    let ttl = mobileProvision["TimeToLive"] as! Int
    let name = mobileProvision["Name"] as! String

    // Iterate through the developer certificates array and create and print
    // SecCertificateWrapper objects
    // Grab the nested entitlements dictionary
    let entitlements = mobileProvision["Entitlements"] as! [String:Any]
    var taskAllow = entitlements["get-task-allow"] as? Bool ?? false
    let appId = entitlements["application-identifier"] as! String
    let teamId = entitlements["com.apple.developer.team-identifier"] as! String

    // Iterate through the developer certificates array sorted by notValidAfter date
    // and create and print SecCertificateWrapper objects
    if let certData = mobileProvision["DeveloperCertificates"] as? [Data] {
    for cert in certData.map( { SecCertificateWrapper(data: $0) }).sorted() {
    debugPrint("Name: \(cert.name), Not Valid After Date: \(cert.notValidAfterUnixDateAsString)")
  9. perlmunger revised this gist Jan 31, 2020. 2 changed files with 0 additions and 0 deletions.
    File renamed without changes.
  10. perlmunger renamed this gist Jan 31, 2020. 1 changed file with 0 additions and 0 deletions.
  11. perlmunger revised this gist Jan 31, 2020. No changes.
  12. perlmunger revised this gist Jan 31, 2020. No changes.
  13. perlmunger created this gist Jan 31, 2020.
    38 changes: 38 additions & 0 deletions Implementation.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,38 @@
    guard let url = Bundle.main.url(forResource: "embedded", withExtension: "mobileprovision"), let data = try? Data(contentsOf: url), let string = String(data: data, encoding: String.Encoding.isoLatin1) else {
    return
    }

    let scanner = Scanner(string: string)
    if scanner.scanUpTo("<plist", into: nil) {
    var plistString:NSString? = ""
    if scanner.scanUpTo("</plist>", into: &plistString) {
    plistString = (plistString! as String) + "</plist>" as NSString

    if let plistData = plistString?.data(using: String.Encoding.isoLatin1.rawValue) {
    if let mobileProvision = try? PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String:Any] {
    let appName = mobileProvision["AppIDName"] as! String
    let creationDate = mobileProvision["CreationDate"] as! Date
    let expirationDate = mobileProvision["ExpirationDate"] as! Date
    let entitlements = mobileProvision["Entitlements"] as! [String:Any]
    var taskAllow = false
    if let allow = entitlements["get-task-allow"] as? Bool{
    taskAllow = allow
    }
    let appId = entitlements["application-identifier"] as! String
    let teamId = entitlements["com.apple.developer.team-identifier"] as! String
    let teamName = mobileProvision["TeamName"] as! String
    let ttl = mobileProvision["TimeToLive"] as! Int
    let name = mobileProvision["Name"] as! String

    // Iterate through the developer certificates array and create and print
    // SecCertificateWrapper objects
    if let certData = mobileProvision["DeveloperCertificates"] as? [Data] {
    for cert in certData.map( { SecCertificateWrapper(data: $0) }).sorted() {
    debugPrint("Name: \(cert.name), Not Valid After Date: \(cert.notValidAfterUnixDateAsString)")

    }
    }
    }
    }
    }
    }
    70 changes: 70 additions & 0 deletions SecCertificateWrapper.swift
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,70 @@
    struct SecCertificateWrapper : Comparable {

    var data:Data
    var cert:SecCertificate

    init(data:Data) {
    self.cert = SecCertificateCreateWithData(nil, data as CFData)!
    self.data = data
    }

    var name : String {
    return SecCertificateCopySubjectSummary(cert)! as String
    }

    var validDates : (notValidBefore:Date, notValidAfter:Date)? {
    guard let decodedString = String( data: self.data, encoding: .ascii ) else { return nil }
    var foundWWDRCA = false;
    var notValidBeforeDate = ""
    var notValidAfterDate = ""

    decodedString.enumerateLines { (line, _) in

    if foundWWDRCA && (notValidBeforeDate.isEmpty || notValidAfterDate.isEmpty) {
    let certificateData = line.prefix(13)
    if notValidBeforeDate.isEmpty && !certificateData.isEmpty {
    notValidBeforeDate = String(certificateData)
    } else if notValidAfterDate.isEmpty && !certificateData.isEmpty {
    notValidAfterDate = String(certificateData)
    }
    }

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

    return (self.format(notValidBeforeDate), self.format(notValidAfterDate))
    }

    var notValidAfterUnixDate : Double {
    return self.validDates?.notValidAfter.timeIntervalSince1970 ?? 0
    }

    var notValidAfterUnixDateAsString : String {
    return String(self.notValidAfterUnixDate)
    }

    var notValidBeforeUnixDate : Double {
    return self.validDates?.notValidBefore.timeIntervalSince1970 ?? 0
    }

    var notValidBeforeUnixDateAsString : String {
    return String(self.notValidBeforeUnixDate)
    }

    // MARK: - Utilities
    static func < (lhs: SecCertificateWrapper, rhs: SecCertificateWrapper) -> Bool {
    guard let lhsDate = lhs.validDates?.notValidAfter, let rhsDate = rhs.validDates?.notValidAfter else { return true }
    return lhsDate < rhsDate
    }

    static let certificateDateFormatter : DateFormatter = {
    let formatter = DateFormatter()
    formatter.dateFormat = "yyMMddHHmmssZ"
    return formatter
    }()

    func format(_ date:String) -> Date {
    return SecCertificateWrapper.certificateDateFormatter.date(from: date)!
    }

    }