├── ResignForiOS.zip ├── ResignForiOS ├── ResignForiOS.entitlements ├── ViewController.swift ├── UpdateAppleCer.sh ├── AppDelegate.swift ├── Info.plist ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── StringExtension.swift ├── ProcessTask.swift ├── Tools.swift ├── PlistHelper.swift ├── HProgregressHUD.swift ├── ProfileManager.swift ├── MainSignView.swift ├── CodeSigner.swift └── Base.lproj │ └── Main.storyboard ├── ResignForiOS.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── project.pbxproj ├── README.md └── CoolResign ├── main.swift └── Core.swift /ResignForiOS.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lonely4flow/ResignForiOS/HEAD/ResignForiOS.zip -------------------------------------------------------------------------------- /ResignForiOS/ResignForiOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ResignForiOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ResignForiOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ResignForiOS/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class ViewController: NSViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | } 17 | 18 | 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /ResignForiOS/UpdateAppleCer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | security find-certificate -c "Apple Worldwide Developer Relations Certification Authority" -a -Z | awk '/SHA-1/{system("security delete-certificate -Z "$NF)}' 3 | TEMP="$(mktemp -d -t com.chengcheng.ResignForiOS)" 4 | curl "https://developer.apple.com/certificationauthority/AppleWWDRCA.cer" > "$TEMP/AppleWWDRCA.cer" 5 | security add-certificates "$TEMP/AppleWWDRCA.cer" 6 | rm "$TEMP/AppleWWDRCA.cer" 7 | -------------------------------------------------------------------------------- /ResignForiOS/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | func applicationDidFinishLaunching(_ aNotification: Notification) { 15 | // Insert code here to initialize your application 16 | } 17 | 18 | func applicationWillTerminate(_ aNotification: Notification) { 19 | // Insert code here to tear down your application 20 | } 21 | 22 | func applicationWillBecomeActive(_ notification: Notification) { 23 | 24 | } 25 | 26 | func applicationDidBecomeActive(_ notification: Notification) { 27 | 28 | } 29 | 30 | func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { 31 | sender.windows.first?.makeKeyAndOrderFront(sender) 32 | return true 33 | } 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /ResignForiOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018年 cheng. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /ResignForiOS/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /ResignForiOS/StringExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringExtension.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | extension String { 11 | 12 | var lastPathComponent: String { 13 | get { 14 | return (self as NSString).lastPathComponent 15 | } 16 | } 17 | var pathExtension: String { 18 | get { 19 | return (self as NSString).pathExtension 20 | } 21 | } 22 | var stringByDeletingLastPathComponent: String { 23 | get { 24 | return (self as NSString).deletingLastPathComponent 25 | } 26 | } 27 | var deletePathExtension: String { 28 | get { 29 | return (self as NSString).deletingPathExtension 30 | } 31 | } 32 | 33 | var pathComponents: [String] { 34 | get { 35 | return (self as NSString).pathComponents 36 | } 37 | } 38 | 39 | func appendPathComponent(_ path: String) -> String { 40 | let nsSt = self as NSString 41 | return nsSt.appendingPathComponent(path) 42 | } 43 | 44 | func stringByAppendingPathExtension(_ ext: String) -> String? { 45 | let nsSt = self as NSString 46 | return nsSt.appendingPathExtension(ext) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ResignForiOS/ProcessTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProcessTask.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | import Foundation 9 | struct AppSignerTaskOutput { 10 | var output: String 11 | var status: Int32 12 | init(status: Int32, output: String) { 13 | self.status = status 14 | self.output = output 15 | } 16 | } 17 | extension Process { 18 | func launchSyncronous() -> AppSignerTaskOutput { 19 | self.standardInput = FileHandle.nullDevice 20 | let pipe = Pipe() 21 | self.standardOutput = pipe 22 | self.standardError = pipe 23 | let pipeFile = pipe.fileHandleForReading 24 | self.launch() 25 | 26 | var outPutData = Data() 27 | while self.isRunning { 28 | outPutData.append(pipeFile.availableData) 29 | } 30 | 31 | pipeFile.closeFile(); 32 | self.terminate(); 33 | 34 | let output = String(data: outPutData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) 35 | return AppSignerTaskOutput(status: self.terminationStatus, output: output!) 36 | } 37 | 38 | func execute(_ launchPath: String, workingDirectory: String?, arguments: [String]?) -> AppSignerTaskOutput { 39 | self.launchPath = launchPath 40 | if arguments != nil { 41 | self.arguments = arguments 42 | } 43 | if workingDirectory != nil { 44 | self.currentDirectoryPath = workingDirectory! 45 | } 46 | return self.launchSyncronous() 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /ResignForiOS/Tools.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tools.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AppKit 11 | 12 | public func updateAppleCer() { 13 | let script = "do shell script \"/bin/bash \\\"\(Bundle.main.path(forResource: "UpdateAppleCer", ofType: "sh")!)\\\"\" with administrator privileges" 14 | NSAppleScript(source: script)?.executeAndReturnError(nil) 15 | return 16 | } 17 | 18 | 19 | class Log { 20 | 21 | static let mainBundle = Bundle.main 22 | static let bundleID = mainBundle.bundleIdentifier ?? "com.chengcheng.ResignForiOS" 23 | static let bundleName = mainBundle.infoDictionary!["CFBundleName"] 24 | static let bundleVersion = mainBundle.infoDictionary!["CFBundleShortVersionString"] 25 | static let tempDirectory = NSTemporaryDirectory() 26 | static var logName = Log.tempDirectory.appendPathComponent("\(Log.bundleID)-\(Date().timeIntervalSince1970).log") 27 | 28 | static func write(_ value:String) { 29 | let formatter = DateFormatter() 30 | formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" 31 | 32 | if let outputStream = OutputStream(toFileAtPath: logName, append: true) { 33 | outputStream.open() 34 | let text = "\(formatter.string(from: Date())) \(value)\n" 35 | let data = text.data(using: String.Encoding.utf8, allowLossyConversion: false)! 36 | outputStream.write((data as NSData).bytes.bindMemory(to: UInt8.self, capacity: data.count), maxLength: data.count) 37 | outputStream.close() 38 | } 39 | } 40 | } 41 | 42 | 43 | public func setStatus(_ status: String) { 44 | if !Thread.isMainThread { 45 | DispatchQueue.main.sync { 46 | setStatus(status) 47 | } 48 | } else { 49 | Log.write(status) 50 | print(status) 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 使用说明 2 | ![resign-tool.png](http://upload-images.jianshu.io/upload_images/7079027-fefbb797a49ce89d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 3 | 4 | 如上图所示,可以选择要签名的包文件,这个工具支持ipa,app,xcarchive三种文件作为签名源文件,重签名之后都会生成ipa。 5 | 6 | 7 | | 选项 | 说明 | 8 | | :---------------: | -----| 9 | | Provisioning Profile | 用于选择描述文件,会像xcode一样索所有双击安装在电脑上的描述文件,列举出来,会自动过滤掉过期的。| 10 | | Signing Certificate | 选择对应的证书,同样会检索系统钥匙串安装的可用的证书,会自动过滤掉过期的。| 11 | | new BundleID | 可以重新指定一个新的BundleID。 | 12 | | App Display Name | 可以重新设定一个app名字。 | 13 | | Signing Certificate | 选择对应的证书,同样会检索系统钥匙串安装的可用的证书,会自动过滤掉过期的。| 14 | | App Version | 可以重新指定version。 | 15 | | App Short Version | 可以重新指定Short version。 | 16 | 17 | ### 安装方法 18 | 1.直接下载源码使用xcode运行 19 | 2.从[GitHub仓库releases](https://github.com/HanProjectCoder/ResignForiOS/releases)找最新发布的dmg安装包,下载安装即可 20 | 21 | ### 命令行模式: 22 | 支持使用命令行调起签名功能:(前提是要通过dmg安装到应用目录下) 23 | 命令: 24 | ``` 25 | open -a ResignForiOS --args 26 | ``` 27 | 必加参数: 28 | 29 | | 参数 | 说明 | 30 | | :---------------: | ------ | 31 | | -i | 要重签名的ipa/app/xcarchive文件路径 | 32 | |-p | 描述文件路径 | 33 | |-c | 证书名字,可以在终端使用security find-identity -v -p codesigning命令列出所有在钥匙串的证书,可以挑选出所需签名的证书名字 | 34 | |-o | 输出ipa路径 | 35 | 36 | 37 | 使用举例: 38 | ``` 39 | open -a ResignForiOS --args -i /xxx/xxx.ipa -p /xxx/xxx.mobileprovision -c "xxx: xx." -o /xxx/xxx.ipa 40 | ``` 41 | **注意使用此命令行模式,执行命令之前,要关闭退出之前打开的窗口。** 42 | 43 | 44 | ### 签名失败可能的问题以及解决方案 45 | #### 1.目标机有多个版本xcode,命令行环境下没有select对应的当前的xcode版本: 46 | 检查一下当前命令号环境下的xcode: 47 | ``` 48 | xcode-select --print-path 49 | ``` 50 | 如果发现指定版本不是当前所用xocde,就使用以下命令指定xcode 51 | ``` 52 | sudo xcode-select -switch /Applications/XcodeXXX.app/Contents/Developer  53 | ``` 54 | #### 2.缺少Apple Worldwide Developer Relations Certification Authority证书 55 | 检查一下是否安装了AppleWWDRCA.cer: 56 | ``` 57 | security find-certificate -c "Apple Worldwide Developer Relations Certification Authority" 58 | ``` 59 | 如果提示找不到,就打开 [苹果官方证书下载地址](http://developer.apple.com/certificationauthority/AppleWWDRCA.cer) 点击下载后,双击cer文件即可。OK。 60 | ps:事实上如果缺少AppleWWDRCA.cer,所有申请的开发者证书,在钥匙串里面的显示都会变成不信任证书。 61 | -------------------------------------------------------------------------------- /ResignForiOS/PlistHelper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlistHelper.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | open class PlistHelper: NSObject { 12 | 13 | let defaultsPath = "/usr/bin/defaults" 14 | var plistPath: String? 15 | 16 | public convenience init(plistPath: String?) { 17 | self.init() 18 | self.plistPath = plistPath 19 | } 20 | 21 | var bundleDisplayName: String? { 22 | set { 23 | setValue(newValue!, for: "CFBundleDisplayName") 24 | } 25 | get { 26 | return getValue(for: "CFBundleDisplayName") 27 | } 28 | } 29 | 30 | var bundleIdentifier: String? { 31 | set { 32 | setValue(newValue!, for: "CFBundleIdentifier") 33 | } 34 | get { 35 | return getValue(for: "CFBundleIdentifier") 36 | } 37 | } 38 | 39 | var wkAppBundleIdentifier: String? { 40 | set { 41 | setValue(newValue!, for: "WKCompanionAppBundleIdentifier") 42 | } 43 | get { 44 | return getValue(for: "WKCompanionAppBundleIdentifier") 45 | } 46 | } 47 | var bundleVersion: String? { 48 | set { 49 | setValue(newValue!, for: "CFBundleVersion") 50 | } 51 | get { 52 | return getValue(for: "CFBundleVersion") 53 | } 54 | } 55 | var shortBundleVersion: String? { 56 | set { 57 | setValue(newValue!, for: "CFBundleShortBundleVersion") 58 | } 59 | get { 60 | return getValue(for: "CFBundleShortBundleVersion") 61 | } 62 | } 63 | 64 | var bundleExecutable: String? { 65 | set { 66 | setValue(newValue!, for: "CFBundleExecutable") 67 | } 68 | get { 69 | return getValue(for: "CFBundleExecutable") 70 | } 71 | } 72 | 73 | 74 | func delete(key: String) { 75 | _ = Process().execute(defaultsPath, workingDirectory: nil, arguments: ["delete", plistPath!, key]) 76 | } 77 | func getValue(for key: String) -> String? { 78 | return Process().execute(defaultsPath, workingDirectory: nil, arguments: ["read", plistPath!, key]).output 79 | } 80 | 81 | func exsistValue(for key: String) -> Bool { 82 | return getValue(for: key) != nil 83 | } 84 | 85 | func setValue(_ value: String, for key: String) { 86 | setStatus("\(plistPath!) Changing \(key) to \(value))") 87 | let task = Process().execute(defaultsPath, workingDirectory: nil, arguments: ["write", plistPath!, key, value]) 88 | if task.status != 0 { 89 | setStatus("\(plistPath!) Changing \(key) to \(value)) Error!!!") 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /CoolResign/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // CoolResign 4 | // 5 | // Created by hanxiaoqing on 2018/5/17. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | //https://github.com/stupergenius/Bens-Log/blob/master/blog-projects/swift-command-line/btc.swift 9 | //https://github.com/kylef/Commander 10 | import Foundation 11 | 12 | class CommandWorker: NSObject { 13 | 14 | var inputPath: String? 15 | var profliePath: String? 16 | var cerName: String? 17 | var outputPath: String? 18 | var bundleID: String? 19 | 20 | func work() { 21 | let signer = CodeSigner() 22 | signer.delegate = self 23 | signer.sign(inputFile: inputPath!, provisioningFile: profliePath, newBundleID: bundleID ?? "", newDisplayName: "", newVersion: "", newShortVersion: "", signingCertificate: cerName!, outputFile: outputPath!, openByTerminal: true) 24 | } 25 | } 26 | 27 | extension CommandWorker: CodeSignDelegate { 28 | 29 | func codeSignBegin(workingDir: String) { 30 | print("CodeSign begin with workingDir: \(workingDir)") 31 | } 32 | 33 | func codeSignLogRecord(logDes: String) { 34 | print(logDes) 35 | } 36 | 37 | func codeSignError(errDes: String, tempDir: String) { 38 | print(errDes) 39 | cleanup(tempDir) 40 | } 41 | 42 | func codeSigneEndSuccessed(outPutPath: String, tempDir: String) { 43 | cleanup(tempDir) 44 | print("CodeSigneEndSuccessed, output at \(outPutPath)") 45 | exit(0) 46 | } 47 | 48 | func cleanup(_ dir: String) { 49 | do { 50 | print("Deleting: \(dir)") 51 | try FileManager.default.removeItem(atPath: dir) 52 | } catch { 53 | print("Deleting: \(dir) error") 54 | } 55 | } 56 | } 57 | 58 | 59 | let inputOpt = Option(trigger:.mixed("i","inputfile-path")) 60 | let provisionOpt = Option(trigger:.mixed("p","provision-path")) 61 | let cerNameOpt = Option(trigger:.mixed("c","certificate-name")) 62 | let bundleIDOpt = Option(trigger:.mixed("b","bundleId")) 63 | let outputOpt = Option(trigger:.mixed("o","outputfile-path")) 64 | 65 | let helpOpt = Option(trigger:.mixed("h","help")) 66 | let parser = OptionParser(definitions:[inputOpt, provisionOpt, cerNameOpt, bundleIDOpt,outputOpt,helpOpt]) 67 | 68 | let arguments = CommandLine.arguments 69 | //let arguments = ["CoolResign", "-i", "/Users/hanxiaoqing/Documents/sdkTest_c.xcarchive", "-p", "/Users/hanxiaoqing/Desktop/SvnFolder/NewSDKFolder/provisioning/enterprise/DIS_ALL_NZK3GXHA6L.mobileprovision", "-c", "iPhone Distribution: Babeltime Inc.", "-o", "/Users/hanxiaoqing/Documents/sdkTest_c.ipa", "-b", "com.babeltime.sdkdemo.unity"] 70 | 71 | print("get all args: \(arguments)") 72 | 73 | let spliceArgs = arguments[1 ..< arguments.count] 74 | 75 | do { 76 | let (options, rest) = try parser.parse(Array(spliceArgs)) 77 | 78 | if spliceArgs.count == 0 || options[helpOpt] != nil { 79 | print(parser.helpStringForCommandName("show codesign important args")) 80 | exit(1) 81 | } 82 | 83 | let inputArgKeyCount = options.keys.count 84 | print("input argkey count: \(inputArgKeyCount)") 85 | 86 | // require arg: -i -p -c -o, the "-h, -b" two args neednt required 87 | if inputArgKeyCount < parser.definitions.count - 2 { 88 | print("Input arg keys not enough, Please check !!!") 89 | exit(2) 90 | } 91 | 92 | guard rest.count == inputArgKeyCount else { 93 | print("One or more arguments value not set, Please check !!!") 94 | exit(3) 95 | } 96 | 97 | let worker = CommandWorker() 98 | rest.forEach { argValue in 99 | let index = spliceArgs.index(of: argValue) 100 | let argKey = spliceArgs[index! - 1] 101 | 102 | if inputOpt.matches(argKey) { 103 | worker.inputPath = argValue 104 | } 105 | 106 | if provisionOpt.matches(argKey) { 107 | worker.profliePath = argValue 108 | } 109 | 110 | if cerNameOpt.matches(argKey) { 111 | worker.cerName = argValue 112 | } 113 | 114 | if bundleIDOpt.matches(argKey) { 115 | worker.bundleID = argValue 116 | } 117 | 118 | if outputOpt.matches(argKey) { 119 | worker.outputPath = argValue 120 | } 121 | 122 | } 123 | // 判断xcode-select -p是否有值 124 | // 判断 证书是否存在 125 | 126 | worker.work() 127 | } catch let OptionKitError.invalidOption(description: description) { 128 | exit(4) 129 | print("OptionKit throw error: \n \(description)") 130 | } 131 | 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /ResignForiOS/HProgregressHUD.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | 3 | fileprivate let HDefaultPadding: CGFloat = 4.0; 4 | fileprivate let HDefaultLabelFontSize: CGFloat = 15.0; 5 | fileprivate let HDefaultDetailsLabelFontSize: CGFloat = 12.0; 6 | 7 | 8 | open class HProgregressHUD: NSView { 9 | 10 | open static func showHUDAddedTo(view: NSView, animated: Bool) -> HProgregressHUD { 11 | let hud = HProgregressHUD(with: view) 12 | view.addSubview(hud) 13 | hud.showAnimated(animated) 14 | return hud 15 | } 16 | 17 | open static func hideHUDFor(view: NSView, animated: Bool) -> Bool { 18 | if let hud = HUDFor(view: view) { 19 | hud.hideAnimated(animated) 20 | return true 21 | } else { 22 | return false 23 | } 24 | } 25 | 26 | open static func HUDFor(view: NSView) -> HProgregressHUD? { 27 | for view in view.subviews.reversed() { 28 | if view is HProgregressHUD { 29 | return view as? HProgregressHUD 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | 36 | open func showAnimated(_: Bool) { 37 | alphaValue = 1.0 38 | backgroundView.alphaValue = 0.4 39 | } 40 | 41 | open func hideAnimated(_: Bool) { 42 | alphaValue = 0.0 43 | removeFromSuperview() 44 | } 45 | 46 | open func hideAnimated(_: Bool, afterDelay: Double) { 47 | 48 | } 49 | 50 | open var contentColor: NSColor = .white 51 | 52 | open let backgroundView = HBackgroundView() 53 | 54 | open let bezelView = NSVisualEffectView() 55 | 56 | 57 | open let label: NSTextField = createLabel() 58 | open let detailsLabel: NSTextField = createLabel() 59 | 60 | 61 | fileprivate func commonInit() { 62 | alphaValue = 0.0 63 | wantsLayer = true 64 | layer?.backgroundColor = NSColor.clear.cgColor 65 | 66 | setupViews() 67 | updateIndicators() 68 | } 69 | 70 | public convenience init(with view: NSView) { 71 | self.init(frame: view.bounds) 72 | } 73 | 74 | public override init(frame frameRect: NSRect) { 75 | super.init(frame: CGRect()) 76 | commonInit() 77 | } 78 | required public init?(coder decoder: NSCoder) { 79 | super.init(coder: decoder) 80 | commonInit() 81 | } 82 | open override var isFlipped: Bool { 83 | return true 84 | } 85 | } 86 | 87 | extension HProgregressHUD { 88 | 89 | fileprivate static func createLabel() -> NSTextField { 90 | let label = NSTextField() 91 | label.isBordered = false 92 | label.isEditable = false 93 | label.isSelectable = false 94 | label.font = NSFont.systemFont(ofSize: 13.5) 95 | label.drawsBackground = false 96 | label.alignment = .center 97 | return label 98 | } 99 | 100 | 101 | fileprivate func setupViews() { 102 | let defaultColor = contentColor 103 | 104 | backgroundView.autoresizingMask = [.width, .height] 105 | backgroundView.backgroundColor = NSColor.gray 106 | backgroundView.alphaValue = 0.0 107 | addSubview(backgroundView) 108 | 109 | let materialView = HBackgroundView() 110 | materialView.autoresizingMask = [.width, .height] 111 | materialView.backgroundColor = NSColor.black 112 | materialView.alphaValue = 0.7 113 | 114 | label.textColor = defaultColor 115 | label.font = NSFont.boldSystemFont(ofSize: HDefaultLabelFontSize) 116 | materialView.addSubview(label) 117 | 118 | detailsLabel.textColor = defaultColor 119 | detailsLabel.font = NSFont.boldSystemFont(ofSize: HDefaultDetailsLabelFontSize) 120 | detailsLabel.alignment = .left 121 | materialView.addSubview(detailsLabel) 122 | 123 | bezelView.layer?.cornerRadius = 5.0 124 | bezelView.state = .active 125 | bezelView.blendingMode = .withinWindow 126 | bezelView.alphaValue = 0.7 127 | bezelView.addSubview(materialView) 128 | 129 | addSubview(bezelView) 130 | } 131 | 132 | 133 | fileprivate func updateIndicators() { 134 | 135 | } 136 | 137 | open override func layout() { 138 | super.layout() 139 | 140 | frame = (superview?.bounds)! 141 | backgroundView.frame = bounds 142 | 143 | let verticalMargin: CGFloat = 15.0 144 | let horizontalMargin: CGFloat = 15.0 145 | let limitMinWidth: CGFloat = 200 146 | 147 | label.left = 0.0 148 | label.top = verticalMargin 149 | label.width = limitMinWidth 150 | label.height = 20.0 151 | 152 | 153 | detailsLabel.top = label.bottom + verticalMargin 154 | let detailStringWidth = (detailsLabel.stringValue as NSString).size(withAttributes: [.font: detailsLabel.font]).width 155 | if detailStringWidth < limitMinWidth { 156 | detailsLabel.left = (limitMinWidth - detailStringWidth) * 0.5 157 | detailsLabel.width = detailStringWidth 158 | detailsLabel.height = 20.0 159 | } else { 160 | detailsLabel.left = horizontalMargin 161 | detailsLabel.width = limitMinWidth - detailsLabel.left * 2 162 | detailsLabel.height = heightFor(string: detailsLabel.stringValue, font: detailsLabel.font!, width: detailsLabel.width) + 8 163 | } 164 | 165 | bezelView.width = limitMinWidth 166 | bezelView.height = detailsLabel.bottom + verticalMargin 167 | 168 | if detailsLabel.stringValue.count == 0 || detailsLabel.isHidden { 169 | bezelView.height = label.bottom + verticalMargin 170 | } 171 | 172 | // make the whole content view center 173 | bezelView.makeCenter() 174 | } 175 | 176 | func heightFor(string: String, font: NSFont, width: CGFloat) -> CGFloat { 177 | let textStorage = NSTextStorage(string: string) 178 | let textContainer = NSTextContainer(containerSize: CGSize(width: width, height: CGFloat(MAXFLOAT))) 179 | 180 | let layoutManager = NSLayoutManager() 181 | layoutManager.addTextContainer(textContainer) 182 | 183 | textStorage.addLayoutManager(layoutManager) 184 | textStorage.addAttributes([.font: font], range: NSRange(location: 0, length: textStorage.length)) 185 | textContainer.lineFragmentPadding = 0.0 186 | 187 | layoutManager.glyphRange(for: textContainer) 188 | return layoutManager.usedRect(for: textContainer).size.height 189 | } 190 | 191 | open override func mouseDown(with event: NSEvent) {} 192 | 193 | } 194 | 195 | 196 | fileprivate extension NSView { 197 | 198 | fileprivate func makeCenter(in superView: NSView? = nil) { 199 | var x:CGFloat = 0 200 | var y:CGFloat = 0 201 | if let sv = superView { 202 | x = CGFloat(roundf(Float((sv.frame.width - frame.width)/2.0))) 203 | y = CGFloat(roundf(Float((sv.frame.height - frame.height)/2.0))) 204 | } else if let sv = self.superview { 205 | x = CGFloat(roundf(Float((sv.frame.width - frame.width)/2.0))) 206 | y = CGFloat(roundf(Float((sv.frame.height - frame.height)/2.0))) 207 | } 208 | self.setFrameOrigin(NSMakePoint(x, y)) 209 | } 210 | fileprivate var left: CGFloat { 211 | get { 212 | return self.frame.origin.x 213 | } 214 | set(newLeft) { 215 | var frame = self.frame 216 | frame.origin.x = newLeft 217 | self.frame = frame 218 | } 219 | } 220 | 221 | fileprivate var top:CGFloat { 222 | get { 223 | return self.frame.origin.y 224 | } 225 | 226 | set(newTop) { 227 | var frame = self.frame 228 | frame.origin.y = newTop 229 | self.frame = frame 230 | } 231 | } 232 | 233 | fileprivate var width:CGFloat { 234 | get { 235 | return self.frame.size.width 236 | } 237 | 238 | set(newWidth) { 239 | var frame = self.frame 240 | frame.size.width = newWidth 241 | self.frame = frame 242 | } 243 | } 244 | 245 | fileprivate var height:CGFloat { 246 | get { 247 | return self.frame.size.height 248 | } 249 | 250 | set(newHeight) { 251 | var frame = self.frame 252 | frame.size.height = newHeight 253 | self.frame = frame 254 | } 255 | } 256 | 257 | fileprivate var right:CGFloat { 258 | get { 259 | return self.left + self.width 260 | } 261 | } 262 | 263 | fileprivate var bottom:CGFloat { 264 | get { 265 | return self.top + self.height 266 | } 267 | } 268 | } 269 | 270 | 271 | open class HBackgroundView: NSView { 272 | 273 | public var backgroundColor: NSColor = NSColor.clear { 274 | didSet { 275 | if backgroundColor != oldValue { 276 | layer?.backgroundColor = backgroundColor.cgColor 277 | } 278 | } 279 | } 280 | 281 | public override init(frame frameRect: NSRect) { 282 | super.init(frame: frameRect) 283 | wantsLayer = true 284 | } 285 | 286 | required public init?(coder decoder: NSCoder) { 287 | super.init(coder: decoder) 288 | wantsLayer = true 289 | } 290 | open override var isFlipped: Bool { 291 | return true 292 | } 293 | } 294 | 295 | -------------------------------------------------------------------------------- /ResignForiOS/ProfileManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileManager.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | import Cocoa 9 | import Security 10 | 11 | 12 | open class ProfileManager { 13 | 14 | fileprivate var directoryPath: String? 15 | 16 | init(directoryPath: String? = nil) { 17 | self.directoryPath = directoryPath 18 | } 19 | 20 | open var profilesPath: String { 21 | if let path = directoryPath { 22 | return path 23 | } else { 24 | let libraryDir = FileManager().urls(for: .libraryDirectory, in: .userDomainMask).first! 25 | directoryPath = libraryDir.appendingPathComponent("MobileDevice/Provisioning Profiles").path 26 | return self.profilesPath 27 | } 28 | } 29 | 30 | // MARK: expired Profiles 31 | open var expiredProfiles: [Profile] = [] 32 | 33 | open func updateProfiles() -> [Profile] { 34 | var validProfiles: [Profile] = [] 35 | if let contentFiles = try? FileManager.default.contentsOfDirectory(atPath: profilesPath) { 36 | let mobileprovisions = contentFiles.filter({URL(fileURLWithPath: $0).pathExtension == "mobileprovision"}) 37 | for profile in mobileprovisions { 38 | if let profile = Profile(filePath: profilesPath + "/" + profile) { 39 | if profile.isExpired == false { 40 | validProfiles.append(profile) 41 | } else { 42 | expiredProfiles.append(profile) 43 | } 44 | } 45 | } 46 | } 47 | return validProfiles 48 | } 49 | 50 | 51 | open func profilesMatching(_ bundleID: String, acceptWildcardMatches: Bool = false) -> [Profile] { 52 | 53 | var matches = updateProfiles().filter({ $0.matches(bundleID) }) 54 | if !acceptWildcardMatches { 55 | matches = matches.filter({ !$0.isWildcard }) 56 | } 57 | return matches 58 | } 59 | 60 | } 61 | 62 | 63 | //========================================================================== 64 | // MARK: Profile Constants Keys 65 | //========================================================================== 66 | 67 | fileprivate let creationDateKey = "CreationDate" 68 | fileprivate let expirationDateKey = "ExpirationDate" 69 | 70 | fileprivate let appIDNameKey = "AppIDName" 71 | fileprivate let applicationIdentifierPrefixKey = "ApplicationIdentifierPrefix" 72 | fileprivate let developerCertificatesKey = "DeveloperCertificates" 73 | fileprivate let entitlementsKey = "Entitlements" 74 | fileprivate let nameKey = "Name" 75 | fileprivate let provisionsAllDevicesKey = "ProvisionsAllDevices" 76 | fileprivate let provisionedDevicesKey = "ProvisionedDevices" 77 | fileprivate let teamIdentifierKey = "TeamIdentifier" 78 | fileprivate let teamNameKey = "TeamName" 79 | fileprivate let timeToLiveKey = "TimeToLive" 80 | fileprivate let UUIDKey = "UUID" 81 | fileprivate let versionKey = "Version" 82 | 83 | 84 | //========================================================================== 85 | // MARK: Entitlements Constants Keys 86 | //========================================================================== 87 | 88 | // General 89 | fileprivate let applicationIdentiferKey = "application-identifier" 90 | fileprivate let betaReportsActiveKey = "beta-reports-active" 91 | fileprivate let getTaskAllowKey = "get-task-allow" 92 | fileprivate let keychainAccessGroupsKey = "keychain-access-groups" 93 | fileprivate let teamIdKey = "com.apple.developer.team-identifier" 94 | 95 | // Push 96 | fileprivate let apsEnvironmentKey = "aps-environment" 97 | 98 | // iCloud 99 | fileprivate let ubiquityStoreIdentifierKey = "com.apple.developer.ubiquity-kvstore-identifier" 100 | 101 | 102 | 103 | 104 | //========================================================================== 105 | // MARK: Provisioning Profile 106 | //========================================================================== 107 | 108 | open class Profile: NSObject { 109 | 110 | open var filePath: String? 111 | 112 | // MARK: Profile Properties 113 | open let applicationIdentifierPrefix: [String] 114 | open let appIDName: String? 115 | open var bundleID: String = "" 116 | 117 | open var developerCertificates: [String] = [] 118 | open let entitlements: Entitlements! 119 | open let name: String 120 | open let provisionsAllDevices: Bool 121 | open let provisionedDevices: [String] 122 | open let teamIdentifiers: [String] 123 | open let teamName: String? 124 | open let timeToLive: Int 125 | open let uuid: String! 126 | open let version: Int 127 | 128 | open let creationDate: Date! 129 | open let expirationDate: Date! 130 | 131 | open var isExpired: Bool { 132 | return self.expirationDate.timeIntervalSinceNow < 0 133 | } 134 | 135 | open var isWildcard: Bool { 136 | return entitlements.applicationIdentifer.hasSuffix("*") 137 | } 138 | 139 | open var type: String = "" 140 | 141 | open func matches(_ bundleID: String) -> Bool { 142 | var appID = self.entitlements.applicationIdentifer 143 | if let dotRange = appID?.range(of: ".") { 144 | appID = String(describing: appID?[(appID?.startIndex)! ..< dotRange.upperBound]) 145 | } 146 | if !isWildcard { 147 | return bundleID == appID 148 | } 149 | return bundleID.hasPrefix(appID!.substring(to: appID!.index(before: appID!.endIndex))) || appID == "*" 150 | } 151 | 152 | 153 | // MARK: Initialize with a decoded Profile info Dict 154 | public init?(dictionary: [String: AnyObject]?) { 155 | self.creationDate = dictionary?[creationDateKey] as? Date 156 | self.expirationDate = dictionary?[expirationDateKey] as? Date 157 | self.appIDName = dictionary?[appIDNameKey] as? String 158 | self.applicationIdentifierPrefix = dictionary?[applicationIdentifierPrefixKey] as? [String] ?? [] 159 | self.entitlements = Entitlements(dictionary: dictionary?[entitlementsKey] as? [String: AnyObject]) 160 | 161 | let devCerDatas = dictionary?[developerCertificatesKey] as? [Data] ?? [] 162 | for cer in devCerDatas { 163 | let certificateRef = SecCertificateCreateWithData(nil, cer as CFData) 164 | let summary = SecCertificateCopySubjectSummary(certificateRef!) 165 | self.developerCertificates.append(summary! as String) 166 | } 167 | 168 | let appID = entitlements.applicationIdentifer 169 | let index = appID?.index((appID?.startIndex)!, offsetBy: (applicationIdentifierPrefix.first?.count)! + 1) 170 | self.bundleID = (appID?.substring(from: index!))!.trimmingCharacters(in: CharacterSet(charactersIn: "*")) 171 | 172 | self.name = (dictionary?[nameKey] as? String)! 173 | self.provisionsAllDevices = dictionary?[provisionsAllDevicesKey] as? Bool ?? false 174 | self.provisionedDevices = dictionary?[provisionedDevicesKey] as? [String] ?? [] 175 | self.teamIdentifiers = dictionary?[teamIdentifierKey] as? [String] ?? [] 176 | self.teamName = dictionary?[teamNameKey] as? String 177 | self.timeToLive = dictionary?[timeToLiveKey] as? Int ?? 0 178 | self.uuid = dictionary?[UUIDKey] as? String 179 | self.version = dictionary?[versionKey] as? Int ?? 0 180 | } 181 | 182 | public convenience init?(filePath path: String) { 183 | self.init(dictionary: Profile.decodeProfile(path: path)) 184 | self.filePath = path 185 | } 186 | 187 | public convenience init?(fileURL url: URL) { 188 | self.init(filePath: url.path) 189 | } 190 | 191 | 192 | // MARK: Decode Profile with a path 193 | fileprivate class func decodeProfile(path: String) -> [String: AnyObject]? { 194 | if let profileData = try? Data(contentsOf: URL(fileURLWithPath: path)) { 195 | var optionalDecoder: CMSDecoder? 196 | var optionalDataRef: CFData? 197 | 198 | CMSDecoderCreate(&optionalDecoder) 199 | if let decoderRef = optionalDecoder { 200 | CMSDecoderUpdateMessage(decoderRef, (profileData as NSData).bytes, Int(profileData.count)) 201 | CMSDecoderFinalizeMessage(decoderRef) 202 | CMSDecoderCopyContent(decoderRef, &optionalDataRef) 203 | 204 | let decodedProfileData = optionalDataRef! as Data 205 | let plistDict = try! PropertyListSerialization.propertyList(from: decodedProfileData, options: .mutableContainersAndLeaves, format: nil) 206 | return plistDict as? [String: AnyObject] 207 | } 208 | } 209 | return nil 210 | } 211 | } 212 | 213 | //========================================================================== 214 | // MARK: Entitlements 215 | //========================================================================== 216 | 217 | open class Entitlements { 218 | 219 | open let fullDictionary: [String: AnyObject]! 220 | 221 | // General 222 | open let applicationIdentifer: String! 223 | open let betaReportsActive: Bool 224 | open let getTaskAllow: Bool 225 | open let keychainAccessGroups: [String] 226 | open let teamIdentifier: String? 227 | 228 | // Push 229 | open let apsEnvironment: String 230 | 231 | // iCloud 232 | open let ubiquityStoreIdentifier: String? 233 | 234 | init?(dictionary: [String: AnyObject]?) { 235 | self.fullDictionary = dictionary 236 | 237 | // General 238 | self.applicationIdentifer = dictionary?[applicationIdentiferKey] as? String 239 | self.betaReportsActive = dictionary?[betaReportsActiveKey] as? Bool ?? false 240 | self.getTaskAllow = dictionary?[getTaskAllowKey] as? Bool ?? false 241 | self.keychainAccessGroups = dictionary?[keychainAccessGroupsKey] as? [String] ?? [] 242 | self.teamIdentifier = dictionary?[teamIdKey] as? String 243 | 244 | // Push 245 | self.apsEnvironment = dictionary?[apsEnvironmentKey] as? String ?? "" 246 | 247 | // iCloud 248 | self.ubiquityStoreIdentifier = dictionary?[ubiquityStoreIdentifierKey] as? String 249 | 250 | if (self.fullDictionary == nil || self.applicationIdentifer == nil) { 251 | return nil 252 | } 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /ResignForiOS/MainSignView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainSignView.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | 10 | import Cocoa 11 | 12 | public extension NSPasteboard.PasteboardType { 13 | public static var kUrl: NSPasteboard.PasteboardType { 14 | return self.init(kUTTypeURL as String) 15 | } 16 | public static var kFilenames: NSPasteboard.PasteboardType { 17 | return self.init("NSFilenamesPboardType") 18 | } 19 | public static var kFileUrl: NSPasteboard.PasteboardType { 20 | return self.init(kUTTypeFileURL as String) 21 | } 22 | } 23 | 24 | 25 | class MainSignView: NSView { 26 | 27 | let securityPath = "/usr/bin/security" 28 | let defaults = UserDefaults() 29 | let fileManager = FileManager.default 30 | 31 | //MARK: IBOutlets 32 | @IBOutlet var inputFileField: NSTextField! 33 | 34 | @IBOutlet var profileSelcetPop: NSPopUpButton! 35 | @IBOutlet var codeSignCertsPop: NSPopUpButton! 36 | 37 | @IBOutlet var newBundleIDField: NSTextField! 38 | @IBOutlet var appDisplayName: NSTextField! 39 | @IBOutlet var appShortVersion: NSTextField! 40 | @IBOutlet var appVersion: NSTextField! 41 | 42 | @IBOutlet var StartButton: NSButton! 43 | @IBOutlet var StatusLabel: NSTextField! 44 | 45 | //MARK: Variables 46 | var provisioningProfiles: [Profile] = ProfileManager().updateProfiles() 47 | var codesigningCerts: [String] = [] 48 | 49 | var currSelectInput: String? = nil { 50 | didSet { 51 | if currSelectInput != oldValue { 52 | inputFileField.stringValue = currSelectInput ?? "" 53 | } 54 | } 55 | } 56 | 57 | var currSelectProfile: Profile? { 58 | didSet { 59 | newBundleIDField.stringValue = currSelectProfile?.bundleID ?? "" 60 | } 61 | } 62 | 63 | var currSelectCert: String? = nil 64 | var currSelectOutput: String? 65 | fileprivate var openByTerminal = false 66 | 67 | //MARK: Drag / Drop 68 | fileprivate var fileTypes: [String] = ["ipa","app","mobileprovision", "xcarchive"] 69 | fileprivate var fileTypeIsOk = false 70 | 71 | 72 | override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { 73 | if checkExtension(sender) == true { 74 | fileTypeIsOk = true 75 | return .copy 76 | } else { 77 | fileTypeIsOk = false 78 | return NSDragOperation() 79 | } 80 | } 81 | 82 | override func draggingUpdated(_ sender: NSDraggingInfo) -> NSDragOperation { 83 | if fileTypeIsOk { 84 | return .copy 85 | } else { 86 | return NSDragOperation() 87 | } 88 | } 89 | 90 | override func performDragOperation(_ sender: NSDraggingInfo) -> Bool { 91 | let pasteboard = sender.draggingPasteboard() 92 | if let board = pasteboard.propertyList(forType: .kFilenames) as? NSArray { 93 | if let filePath = board[0] as? String { 94 | fileDropped(filePath) 95 | return true 96 | } 97 | } 98 | return false 99 | } 100 | 101 | func checkExtension(_ drag: NSDraggingInfo) -> Bool { 102 | if let board = drag.draggingPasteboard().propertyList(forType: .kFilenames) as? NSArray, 103 | let path = board[0] as? String { 104 | return fileTypes.contains(path.pathExtension.lowercased()) 105 | } 106 | return false 107 | } 108 | 109 | 110 | func fileDropped(_ filePath: String) { 111 | switch filePath.pathExtension.lowercased() { 112 | case "ipa", "app", "xcarchive": 113 | currSelectInput = filePath 114 | break 115 | default: break 116 | } 117 | } 118 | 119 | override func awakeFromNib() { 120 | super.awakeFromNib() 121 | 122 | populateProvisioningProfiles() 123 | populateCodesigningCerts() 124 | 125 | setStatus("Ready") 126 | 127 | let xcsCmd = "/usr/bin/xcode-select" 128 | let checkCodeTask = Process().execute(xcsCmd, workingDirectory: nil, arguments: ["-p"]) 129 | if checkCodeTask.status != 0 { 130 | _ = Process().execute(xcsCmd, workingDirectory: nil, arguments: ["--install"]) 131 | NSApplication.shared.terminate(self) 132 | } 133 | setStatus("check XCode Task over") 134 | } 135 | 136 | 137 | func populateProvisioningProfiles() { 138 | var items = ["Re-Sign Only"] 139 | for profile in provisioningProfiles { 140 | items.append("\(profile.name) (\(profile.teamIdentifiers.first!))") 141 | } 142 | profileSelcetPop.removeAllItems() 143 | profileSelcetPop.addItems(withTitles:items) 144 | profileSelcetPop.selectItem(at: 0) 145 | } 146 | 147 | 148 | func populateCodesigningCerts() { 149 | codeSignCertsPop.removeAllItems() 150 | let securityResult = Process().execute(securityPath, workingDirectory: nil, arguments: ["find-identity","-v","-p","codesigning"]) 151 | if securityResult.output.count < 1 { 152 | showCodesignCertsErrorAlert() 153 | return 154 | } 155 | codesigningCerts = securityResult.output.split(separator: "\"").map{String($0)}.filter({ $0.contains("iPhone")}) 156 | for cert in self.codesigningCerts { 157 | codeSignCertsPop.addItem(withTitle: cert) 158 | } 159 | } 160 | 161 | 162 | func showCodesignCertsErrorAlert(){ 163 | let alert = NSAlert() 164 | alert.messageText = "No codesigning certificates found" 165 | alert.informativeText = "I can attempt to fix this automatically, would you like me to try?" 166 | alert.addButton(withTitle: "Yes") 167 | alert.addButton(withTitle: "No") 168 | if alert.runModal() == .alertFirstButtonReturn { 169 | updateAppleCer() 170 | populateCodesigningCerts() 171 | } 172 | } 173 | 174 | 175 | @IBAction func doSign(_ sender: NSButton) { 176 | if codesigningCerts.count == 0 { 177 | showCodesignCertsErrorAlert() 178 | } else { 179 | 180 | NSApplication.shared.windows[0].makeFirstResponder(self) 181 | let saveDialog = NSSavePanel() 182 | saveDialog.allowedFileTypes = ["ipa","app"] 183 | saveDialog.nameFieldStringValue = inputFileField.stringValue.lastPathComponent.deletePathExtension 184 | if saveDialog.runModal() == .OK { 185 | currSelectOutput = saveDialog.url!.path 186 | Thread.detachNewThreadSelector(#selector(self.signingThread), toTarget: self, with: nil) 187 | } else { 188 | currSelectOutput = nil 189 | } 190 | } 191 | } 192 | 193 | 194 | @objc func signingThread() { 195 | 196 | //MARK: Set up variables 197 | var newBundleID :String = "" 198 | var newDisplayName :String = "" 199 | var newShortVersion :String = "" 200 | var newVersion :String = "" 201 | var inputFieldValue :String = "" 202 | 203 | DispatchQueue.main.sync { 204 | newBundleID = self.newBundleIDField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) 205 | newDisplayName = self.appDisplayName.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) 206 | newShortVersion = self.appShortVersion.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) 207 | newVersion = self.appVersion.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) 208 | inputFieldValue = self.inputFileField.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) 209 | } 210 | 211 | let inputFilePath :String = currSelectInput ?? inputFieldValue 212 | 213 | // check inputFile path is a dir, return directly 214 | if fileManager.fileExists(atPath: inputFilePath, isDirectory: nil) == false { 215 | DispatchQueue.main.async(execute: { 216 | let alert = NSAlert() 217 | alert.messageText = "Input file not found" 218 | alert.addButton(withTitle: "OK") 219 | alert.informativeText = "The file \(inputFilePath) could not be found" 220 | alert.runModal() 221 | }) 222 | return 223 | } 224 | 225 | // Check provisioningFile 226 | if let currProfile = currSelectProfile { 227 | if currProfile.isExpired == true { 228 | setStatus("Provisioning profile expired") 229 | return 230 | } 231 | } 232 | 233 | let signer = CodeSigner() 234 | signer.delegate = self 235 | signer.sign(inputFile: inputFilePath, provisioningFile: currSelectProfile?.filePath, newBundleID: newBundleID, newDisplayName: newDisplayName, newVersion: newVersion, newShortVersion: newShortVersion, signingCertificate: currSelectCert!, outputFile: currSelectOutput!,openByTerminal: openByTerminal) 236 | } 237 | 238 | 239 | //MARK: IBActions 240 | @IBAction func chooseProvisioningProfile(_ sender: NSPopUpButton) { 241 | let title = sender.selectedItem?.title ?? ""; 242 | let list = provisioningProfiles.filter { (profile) -> Bool in 243 | return title.contains(profile.name) 244 | } 245 | 246 | if list.count == 0 { 247 | newBundleIDField.stringValue = "" 248 | } else { 249 | let selectProfile = list.first! 250 | currSelectProfile = selectProfile 251 | let matchCer = selectProfile.developerCertificates.first 252 | if let matchCer = matchCer, codesigningCerts.contains(matchCer) { 253 | codeSignCertsPop.selectItem(withTitle: matchCer) 254 | currSelectCert = matchCer 255 | } else { 256 | //MARK: todo // 提醒用户没有匹配的证书 257 | } 258 | } 259 | } 260 | 261 | @IBAction func selectInput(_ sender: AnyObject) { 262 | let openDialog = NSOpenPanel() 263 | openDialog.canChooseFiles = true 264 | openDialog.canChooseDirectories = false 265 | openDialog.allowsMultipleSelection = false 266 | openDialog.allowsOtherFileTypes = false 267 | openDialog.allowedFileTypes = ["ipa", "IPA", "app", "APP", "xcarchive"] 268 | openDialog.runModal() 269 | if let filename = openDialog.urls.first { 270 | currSelectInput = filename.path 271 | } 272 | } 273 | 274 | 275 | @IBAction func chooseSigningCertificate (_ sender: NSPopUpButton) { 276 | currSelectCert = sender.selectedItem?.title 277 | setStatus("Set currSelectCert: \(currSelectCert!)") 278 | } 279 | 280 | 281 | @IBAction func openOutputFile(_ sender: NSButton) { 282 | if let outputFile = self.currSelectOutput { 283 | if fileManager.fileExists(atPath: outputFile) { 284 | NSWorkspace.shared.activateFileViewerSelecting([URL(fileURLWithPath: outputFile)]) 285 | } 286 | } 287 | } 288 | 289 | 290 | @IBAction func openLogFile(_ sender: Any) { 291 | NSWorkspace.shared.openFile(Log.logName) 292 | } 293 | 294 | 295 | override init(frame frameRect: NSRect) { 296 | super.init(frame: frameRect) 297 | registerForDraggedTypes([.string, .tiff, .kUrl, .kFilenames]) 298 | } 299 | 300 | required init?(coder: NSCoder) { 301 | super.init(coder: coder) 302 | registerForDraggedTypes([.string, .tiff, .kUrl, .kFilenames]) 303 | } 304 | 305 | } 306 | 307 | extension MainSignView: CodeSignDelegate { 308 | 309 | func codeSignBegin(workingDir: String) { 310 | setStatus("CodeSign begin with workingDir: \(workingDir)") 311 | DispatchQueue.main.async { 312 | let hud = HProgregressHUD.showHUDAddedTo(view: self, animated: true) 313 | hud.label.stringValue = "CodeSigning" 314 | } 315 | } 316 | 317 | func codeSignLogRecord(logDes: String) { 318 | setStatus(logDes) 319 | } 320 | 321 | func codeSignError(errDes: String, tempDir: String) { 322 | setStatus(errDes) 323 | DispatchQueue.main.async { 324 | HProgregressHUD.HUDFor(view: self)?.hideAnimated(true) 325 | } 326 | cleanup(tempDir) 327 | } 328 | 329 | func codeSigneEndSuccessed(outPutPath: String, tempDir: String) { 330 | cleanup(tempDir) 331 | DispatchQueue.main.async { 332 | HProgregressHUD.HUDFor(view: self)?.hideAnimated(true) 333 | } 334 | setStatus("CodeSigneEndSuccessed, output at \(outPutPath)") 335 | } 336 | 337 | func cleanup(_ dir: String) { 338 | do { 339 | setStatus("Deleting: \(dir)") 340 | try fileManager.removeItem(atPath: dir) 341 | } catch { 342 | setStatus("Deleting: \(dir) error") 343 | } 344 | } 345 | } 346 | -------------------------------------------------------------------------------- /CoolResign/Core.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Core.swift 3 | // OptionKit 4 | // 5 | // Created by Salazar, Alexandros on 9/24/14. 6 | // Copyright (c) 2014 nomothetis. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Eventually intends to be a getopt-compatible option parser. 13 | */ 14 | 15 | public enum OptionTrigger : Equatable, CustomDebugStringConvertible, Hashable { 16 | case short(Character) 17 | case long(String) 18 | case mixed(Character, String) 19 | 20 | public var debugDescription:String { 21 | get { 22 | switch self { 23 | case .short(let c): 24 | return "-\(c)" 25 | case .long(let str): 26 | return "--\(str)" 27 | case .mixed(let c, let str): 28 | return "[-\(c)|--\(str)]" 29 | } 30 | } 31 | } 32 | 33 | public var usageDescription:String { 34 | get { 35 | switch self { 36 | case .short(let c): 37 | return "[-\(c)]" 38 | case .long(let str): 39 | return "[--\(str)]" 40 | case .mixed(let c, let str): 41 | return "[-\(c)|--\(str)]" 42 | } 43 | } 44 | } 45 | 46 | public var hashValue:Int { 47 | get { 48 | return self.usageDescription.hashValue 49 | } 50 | } 51 | } 52 | 53 | /// Describes an option for the parser. 54 | /// 55 | /// An Option consists of a trigger and a number of required parameters, which 56 | /// defaults to zero. It also has includes a description, which is empty by default. The 57 | /// description does not affect equality. 58 | public struct Option : Equatable, CustomStringConvertible, CustomDebugStringConvertible, Hashable { 59 | let trigger:OptionTrigger 60 | let numberOfParameters:Int 61 | 62 | /// The description of how the option works. This description has no bearing on equality. 63 | let helpDescription:String 64 | 65 | /// The designated initializer 66 | /// 67 | /// Creates an option definition from a trigger and a required number of parameters. 68 | /// 69 | /// - parameter trigger: the trigger that the parser will use to decide the option is 70 | /// being called. 71 | /// - parameter numberOfParameters: the number of required parameters. Defaults to 0. 72 | /// - parameter helpDescription: the string that will be displayed when the -h flag is triggered. 73 | /// 74 | /// - returns: An OptionDefinition suitable for use by an OptionParser 75 | public init(trigger trig:OptionTrigger, numberOfParameters num:Int = 0, helpDescription desc:String = "") { 76 | self.trigger = trig 77 | self.numberOfParameters = num 78 | self.helpDescription = desc 79 | } 80 | 81 | /// Determines if the given string matches this trigger. 82 | /// 83 | /// - parameter str: the string. 84 | /// - returns: `true` if the string matches this option's trigger, `false` otherwise. 85 | func matches(_ str:String) -> Bool { 86 | switch self.trigger { 87 | case .short(let char): 88 | return str == "-" + String(char) 89 | case .long(let longKey): 90 | return str == "--" + longKey 91 | case .mixed(let char, let longKey): 92 | return (str == "--" + longKey) || str == "-" + String(char) 93 | } 94 | } 95 | 96 | static func isValidOptionString(_ str:String) -> Bool{ 97 | let length = str.characters.count 98 | if length < 2 { 99 | return false 100 | } 101 | 102 | if length == 2 { 103 | if str[str.startIndex] != "-" { 104 | return false 105 | } 106 | 107 | return str[str.characters.index(str.startIndex, offsetBy: 1)] != "-" 108 | } 109 | 110 | /* Okay, count greater than 2. Full option! */ 111 | return str[str.startIndex ... str.characters.index(str.startIndex, offsetBy: 1)] == "--" 112 | } 113 | 114 | public var description: String { 115 | get { 116 | return "\(self.trigger) requires \(self.numberOfParameters) parameters" 117 | } 118 | } 119 | 120 | public var debugDescription:String { 121 | get { 122 | return "{ Opt: \(self.trigger), \(self.numberOfParameters) }" 123 | } 124 | } 125 | 126 | public var hashValue:Int { 127 | get { 128 | return self.debugDescription.hashValue 129 | } 130 | } 131 | } 132 | 133 | private struct OptionData : Equatable, CustomStringConvertible, CustomDebugStringConvertible { 134 | let option:Option 135 | let parameters:[String] 136 | 137 | fileprivate init(definition def:Option, parameters params:[String] = []) { 138 | self.option = def 139 | self.parameters = params 140 | } 141 | 142 | fileprivate var isValid:Bool { 143 | get { 144 | return parameters.count == option.numberOfParameters 145 | } 146 | } 147 | 148 | fileprivate var description: String { 149 | get { 150 | return "\(self.option), parameters \(parameters) are given" 151 | } 152 | } 153 | 154 | fileprivate var debugDescription:String { 155 | get { 156 | return "{ OptionData:\n \(self.option.debugDescription)\n \(self.parameters)}" 157 | } 158 | } 159 | } 160 | 161 | /// Represents the result of a successful parse. 162 | /// 163 | /// The dictionary is a mapping of encountered options to their parameters, where no-parameter 164 | /// options map to an empty array.. The array is the list of remaining parameters. 165 | public typealias ParseData = ([Option:[String]], [String]) 166 | 167 | /// The option parser. 168 | /// 169 | /// This is the workhorse of the library. It is initialized with a list of options and parses an 170 | /// array of strings assumed to be the call paramerers. 171 | public struct OptionParser { 172 | public let definitions:[Option] 173 | 174 | /// Initializes the parser. 175 | /// 176 | /// By default, each parser has an option triggered by `-h` and `--help`. It also provides 177 | /// a console-displayable string of the options via the `helpStringForCommandName` method. 178 | /// 179 | /// - parameter definitions: the option definitions to parse for. 180 | /// - returns: a parser 181 | public init(definitions defs:[Option] = []) { 182 | let helpOption = Option(trigger:.mixed("h", "help"), helpDescription: "Display command help.") 183 | if defs.contains(helpOption) { 184 | self.definitions = defs 185 | } else { 186 | self.definitions = defs + [helpOption] 187 | } 188 | } 189 | 190 | /// Returns a default help string based on the passed command name and the existing options. 191 | /// 192 | /// The string is suitable to be displayed on the command line and consists of multiple lines, 193 | /// all under 80 characters. 194 | /// 195 | /// - parameter commandName: the name of the command. 196 | /// - returns: an English-language string suitable for command-line display. 197 | public func helpStringForCommandName(_ commandName:String) -> String { 198 | let maximumLineWidth = 80 199 | 200 | // The leading string, to properly indent. 201 | var leadingString = " " 202 | for _ in 0.. ParseData { 233 | let normalizedParams = OptionParser.normalizeParameters(parameters) 234 | let firstCall = ([OptionData](), [String]()) 235 | 236 | let (parsedOptions, args) = try normalizedParams.reduce(firstCall) { tuple, next in 237 | 238 | let (optArray, args) = tuple 239 | /* First check if we are in the process of parsing an option with parameters. */ 240 | if let lastOpt = optArray.last { 241 | 242 | /* Since we have parsed an option already, let's check if it needs more parameters. */ 243 | if lastOpt.option.numberOfParameters > lastOpt.parameters.count { 244 | 245 | /* The option expects parameters; parameters cannot look like option triggers. */ 246 | if (Option.isValidOptionString(next)) { 247 | throw OptionKitError.invalidOption(description: "Option \(lastOpt) before option \(next) was declared") 248 | } 249 | 250 | /* Sanity prevails, the next element is not an option trigger. */ 251 | let shortOptArray = optArray[0 ..< optArray.count - 1] 252 | let newOption = OptionData(definition: lastOpt.option, parameters: lastOpt.parameters + [next]) 253 | return (shortOptArray + [newOption], args) 254 | } 255 | 256 | /* No need for more parameters; parse the next option. */ 257 | return try self.parseNewFlag(tuple, flagCandidate: next) 258 | } 259 | 260 | /* This is the first option. Parse it! */ 261 | return try self.parseNewFlag(tuple, flagCandidate: next) 262 | } 263 | 264 | // We need to carry out one last check. Because of the way the above reduce works, it's 265 | // possible the very last option is in fact not valid. There are ways around that, but 266 | // that's actually slower than just checking the last element at the end. 267 | if let lastOpt = parsedOptions.last, !lastOpt.isValid { 268 | throw OptionKitError.invalidOption(description: "Option \(lastOpt)") 269 | } 270 | 271 | var dict = [Option:[String]]() 272 | for opt in parsedOptions { 273 | dict[opt.option] = opt.parameters 274 | } 275 | return (dict, args) 276 | } 277 | 278 | fileprivate func parseNewFlag(_ current: ([OptionData], [String]), flagCandidate:String) throws -> ([OptionData], [String]) { 279 | /* Does the next element want to be a flag? */ 280 | if Option.isValidOptionString(flagCandidate) { 281 | for flag in self.definitions { 282 | if flag.matches(flagCandidate) { 283 | let newOption = OptionData(definition: flag, parameters: []) 284 | return (current.0 + [newOption], current.1) 285 | } 286 | } 287 | 288 | throw OptionKitError.invalidOption(description: "Invalid option: \(flagCandidate)") 289 | } 290 | 291 | return (current.0, current.1 + [flagCandidate]) 292 | } 293 | 294 | static func normalizeParameters(_ parameters:[String]) -> [String] { 295 | return parameters.reduce([String]()) { memo, next in 296 | let index = next.characters.index(next.startIndex, offsetBy: 0) 297 | if next[index] != "-" { 298 | return memo + [next] 299 | } 300 | 301 | let secondIndex = next.characters.index(index, offsetBy: 1) 302 | if next[secondIndex] == "-" { 303 | /* Assume everything that follows is valid. */ 304 | return memo + [next] 305 | } 306 | 307 | /* Okay, we have one or more single-character flags. */ 308 | var params = [String]() 309 | for char in next[secondIndex.. Bool { 321 | switch (lhs, rhs) { 322 | case (.short(let x), .short(let y)): 323 | return x == y 324 | case (.long(let x), .long(let y)): 325 | return x == y 326 | case (.mixed(let x1, let x2), .mixed(let y1, let y2)): 327 | return (x1 == y1) && (x2 == y2) 328 | default: 329 | return false 330 | } 331 | } 332 | 333 | public func ==(lhs:Option, rhs:Option) -> Bool { 334 | return lhs.hashValue == rhs.hashValue 335 | } 336 | 337 | private func ==(lhs:OptionData, rhs:OptionData) -> Bool { 338 | return (lhs.option == rhs.option) && (lhs.parameters == rhs.parameters) 339 | } 340 | 341 | /// MARK: - Error types 342 | 343 | public enum OptionKitError: Error { 344 | case invalidOption(description: String) 345 | } 346 | 347 | -------------------------------------------------------------------------------- /ResignForiOS/CodeSigner.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodeSigner.swift 3 | // ResignForiOS 4 | // 5 | // Created by hanxiaoqing on 2018/1/23. 6 | // Copyright © 2018年 cheng. All rights reserved. 7 | // 8 | 9 | 10 | import Cocoa 11 | 12 | 13 | class Compress: NSObject { 14 | 15 | static let shared = Compress() 16 | private override init() {} 17 | 18 | let unzipPath = "/usr/bin/unzip" 19 | let zipPath = "/usr/bin/zip" 20 | 21 | @discardableResult 22 | func unzip(_ inputFile: String, outputPath: String) -> Bool { 23 | let unzipTask = Process().execute(unzipPath, workingDirectory: nil, arguments: ["-q", inputFile, "-d", outputPath]) 24 | if unzipTask.status != 0 { 25 | setStatus("Error unzip \(inputFile)") 26 | return false 27 | } 28 | return true 29 | } 30 | 31 | @discardableResult 32 | func zip(_ inputPath: String, outputFile: String) -> Bool { 33 | let zipTask = Process().execute(zipPath, workingDirectory: inputPath, arguments: ["-qry", outputFile, "."]) 34 | if zipTask.status != 0 { 35 | setStatus("Error zip path \(inputPath)") 36 | return false 37 | } 38 | return true 39 | } 40 | } 41 | 42 | protocol CodeSignDelegate { 43 | func codeSignBegin(workingDir: String) 44 | func codeSignLogRecord(logDes: String) 45 | func codeSigneEndSuccessed(outPutPath: String, tempDir: String) 46 | func codeSignError(errDes: String, tempDir: String) 47 | } 48 | 49 | 50 | class CodeSigner: NSObject { 51 | 52 | let mktempPath = "/usr/bin/mktemp" 53 | let chmodPath = "/bin/chmod" 54 | let codesignPath = "/usr/bin/codesign" 55 | 56 | let fileManager = FileManager.default 57 | 58 | var delegate: CodeSignDelegate? 59 | 60 | func makeTempFolder() -> String? { 61 | let bundleID = Bundle.main.bundleIdentifier ?? "com.chengcheng.ResignForiOS" 62 | let tempTask = Process().execute(mktempPath, workingDirectory: nil, arguments: ["-d", "-t", bundleID]) 63 | return tempTask.output 64 | } 65 | 66 | func checkInputAndHandel(_ input: String, _ workingDir: String) -> Bool { 67 | let payloadDir = workingDir.appendPathComponent("Payload/") 68 | let inputFileExt = input.pathExtension.lowercased() 69 | if inputFileExt == "ipa" { 70 | delegate?.codeSignLogRecord(logDes: "Unzip \(input) to \(workingDir)") 71 | Compress.shared.unzip(input, outputPath: workingDir) 72 | } else if inputFileExt == "app" { 73 | do { 74 | try fileManager.createDirectory(atPath: payloadDir, withIntermediateDirectories: true, attributes: nil) 75 | try fileManager.copyItem(atPath: input, toPath: payloadDir.appendPathComponent(input.lastPathComponent)) 76 | delegate?.codeSignLogRecord(logDes: "Copying app to \(payloadDir)") 77 | } catch { 78 | delegate?.codeSignLogRecord(logDes: "Error copying app to \(payloadDir)") 79 | return false 80 | } 81 | } else if inputFileExt == "xcarchive" { 82 | do { 83 | try fileManager.createDirectory(atPath: payloadDir, withIntermediateDirectories: true, attributes: nil) 84 | let appPath = input.appendPathComponent("Products/Applications/") 85 | if let files = try? fileManager.contentsOfDirectory(atPath: appPath) { 86 | for file in files { 87 | try fileManager.copyItem(atPath: appPath + "/\(file)", toPath: payloadDir + "/\(file)") 88 | } 89 | } 90 | delegate?.codeSignLogRecord(logDes: "Copying xcarchive to to \(payloadDir)") 91 | } catch { 92 | delegate?.codeSignLogRecord(logDes: "Error copying xcarchive to to \(payloadDir)") 93 | return false 94 | } 95 | } else { 96 | delegate?.codeSignLogRecord(logDes: "Input file not support") 97 | return false 98 | } 99 | return true 100 | } 101 | 102 | 103 | //MARK: Copy Provisioning Profile 104 | func checkProfilePath(_ inputProfile: String?, _ oldProfilePath: String) -> String? { 105 | delegate?.codeSignLogRecord(logDes: "make sure which profile should be using") 106 | guard let inputProfile = inputProfile else { 107 | return oldProfilePath 108 | } 109 | do { 110 | if fileManager.fileExists(atPath: oldProfilePath) { 111 | try fileManager.removeItem(atPath: oldProfilePath) 112 | } 113 | try fileManager.copyItem(atPath: inputProfile, toPath: oldProfilePath) 114 | return inputProfile 115 | } catch { 116 | delegate?.codeSignLogRecord(logDes: "Error copying provisioning profile \(error.localizedDescription)") 117 | return nil 118 | } 119 | } 120 | 121 | func sign(inputFile: String, provisioningFile: String?, newBundleID: String, newDisplayName: String, newVersion: String, newShortVersion: String, signingCertificate : String, outputFile: String, openByTerminal: Bool) { 122 | 123 | //MARK: Create working temp folder 124 | var tempFolder: String = makeTempFolder()! 125 | 126 | let workingDirectory = tempFolder.appendPathComponent("out") 127 | let entitlementsPlist = tempFolder.appendPathComponent("entitlements.plist") 128 | let payloadDirectory = workingDirectory.appendPathComponent("Payload/") 129 | 130 | delegate?.codeSignBegin(workingDir: workingDirectory) 131 | 132 | //MARK: Codesign Test 133 | 134 | //MARK: Create workingDirectory Temp Directory 135 | do { 136 | try fileManager.createDirectory(atPath: workingDirectory, withIntermediateDirectories: true, attributes: nil) 137 | } catch { 138 | let errDescr = "Create workingDir error" 139 | delegate?.codeSignError(errDes: errDescr, tempDir: tempFolder) 140 | return 141 | } 142 | 143 | if checkInputAndHandel(inputFile, workingDirectory) == false { 144 | delegate?.codeSignError(errDes: "CheckInput: \(inputFile) fail", tempDir: tempFolder) 145 | return 146 | } 147 | 148 | // Loop through app bundles in payload directory 149 | let files = try? fileManager.contentsOfDirectory(atPath: payloadDirectory) 150 | var isDirectory: ObjCBool = true 151 | 152 | for file in files! { 153 | fileManager.fileExists(atPath: payloadDirectory.appendPathComponent(file), isDirectory: &isDirectory) 154 | if !isDirectory.boolValue { continue } 155 | 156 | //MARK: Bundle variables setup 157 | let appFilePath = payloadDirectory.appendPathComponent(file) 158 | let infoPlistPath = appFilePath.appendPathComponent("Info.plist") 159 | let provisioningPath = appFilePath.appendPathComponent("embedded.mobileprovision") 160 | 161 | let currInfoPlist = PlistHelper(plistPath: infoPlistPath) 162 | 163 | //MARK: Delete CFBundleResourceSpecification from Info.plist 164 | currInfoPlist.delete(key: "CFBundleResourceSpecification") 165 | 166 | 167 | let profilePath = checkProfilePath(provisioningFile, provisioningPath) 168 | guard let profile = Profile(filePath: profilePath!) else { 169 | delegate?.codeSignError(errDes: "Creat Profile fail", tempDir: tempFolder) 170 | continue 171 | } 172 | 173 | var entitleDic = profile.entitlements.fullDictionary 174 | let xcentPath = appFilePath.appendPathComponent("archived-expanded-entitlements.xcent") 175 | NSDictionary(contentsOfFile: xcentPath)?.forEach { 176 | let key = $0 as! String 177 | let value = $1 as AnyObject 178 | entitleDic?.updateValue(value, forKey: key) 179 | } 180 | (entitleDic! as NSDictionary).write(toFile: entitlementsPlist, atomically: true) 181 | 182 | 183 | //MARK: Make sure that the executable is well... executable. 184 | if let bundleExecutable = currInfoPlist.bundleExecutable { 185 | _ = Process().execute(chmodPath, workingDirectory: nil, arguments: ["755", appFilePath.appendPathComponent(bundleExecutable)]) 186 | } 187 | 188 | //MARK: Change Application ID 189 | if newBundleID != "" { 190 | if let oldAppID = currInfoPlist.bundleIdentifier { 191 | // recursive func 192 | func changeAppexID(_ appexFile: String) { 193 | let appexPlist = PlistHelper(plistPath: appexFile + "/Info.plist") 194 | if let appexBundleID = appexPlist.bundleIdentifier { 195 | let newAppexID = "\(newBundleID)\(appexBundleID.substring(from: oldAppID.endIndex))" 196 | delegate?.codeSignLogRecord(logDes: "Changing \(appexFile) bundleId to \(newAppexID)") 197 | appexPlist.bundleIdentifier = newAppexID 198 | } 199 | if let _ = appexPlist.wkAppBundleIdentifier { 200 | appexPlist.wkAppBundleIdentifier = newBundleID 201 | } 202 | recursiveDirectorySearch(appexFile, extensions: ["app"], found: changeAppexID) 203 | } 204 | // Search appex in current app file to changeAppID 205 | recursiveDirectorySearch(appFilePath, extensions: ["appex"], found: changeAppexID) 206 | } 207 | currInfoPlist.bundleIdentifier = newBundleID 208 | } 209 | 210 | //MARK: Change Display Name 211 | if newDisplayName != "" { 212 | currInfoPlist.bundleDisplayName = newDisplayName 213 | } 214 | 215 | //MARK: Change Version 216 | if newVersion != "" { 217 | currInfoPlist.bundleVersion = newVersion 218 | } 219 | 220 | //MARK: Change Short Version 221 | if newShortVersion != "" { 222 | currInfoPlist.shortBundleVersion = newShortVersion 223 | } 224 | 225 | 226 | //MARK: Codesigning - App 227 | let signableExts = ["dylib","so","0","vis","pvr","framework","appex","app"] 228 | recursiveDirectorySearch(appFilePath, extensions: signableExts) { file in 229 | codeSign(file, certificate: signingCertificate, entitlements: entitlementsPlist) 230 | } 231 | codeSign(appFilePath, certificate: signingCertificate, entitlements: entitlementsPlist) 232 | 233 | //MARK: Codesigning - Verification 234 | let verificationTask = Process().execute(codesignPath, workingDirectory: nil, arguments: ["-v", appFilePath]) 235 | if verificationTask.status != 0 { 236 | //MARK: alert if certificate expired 237 | self.delegate?.codeSignError(errDes: "verifying code sign fail:\(verificationTask.output)", tempDir: tempFolder) 238 | DispatchQueue.main.async(execute: { 239 | let alert = NSAlert() 240 | alert.addButton(withTitle: "OK") 241 | alert.messageText = "Error verifying code signature!" 242 | alert.informativeText = verificationTask.output 243 | alert.alertStyle = .critical 244 | alert.runModal() 245 | }) 246 | return 247 | } 248 | } 249 | 250 | //MARK: Packaging 251 | //Check if output already exists and delete if so 252 | if fileManager.fileExists(atPath: outputFile) { 253 | do { 254 | try fileManager.removeItem(atPath: outputFile) 255 | } catch { 256 | delegate?.codeSignError(errDes: "Delete exists output file fail", tempDir: tempFolder) 257 | return 258 | } 259 | } 260 | 261 | delegate?.codeSignLogRecord(logDes: "Packaging IPA: \(outputFile)") 262 | 263 | Compress.shared.zip(workingDirectory, outputFile: outputFile) 264 | 265 | delegate?.codeSigneEndSuccessed(outPutPath: outputFile, tempDir: tempFolder) 266 | 267 | } 268 | 269 | 270 | //MARK: Codesigning 271 | func codeSign(_ file: String, certificate: String, entitlements: String?) { 272 | delegate?.codeSignLogRecord(logDes: "codeSign:\(file)") 273 | var arguments = ["-vvv","-fs",certificate,"--no-strict"] 274 | if fileManager.fileExists(atPath: entitlements!) { 275 | arguments.append("--entitlements=\(entitlements!)") 276 | } 277 | arguments.append(file) 278 | let codesignTask = Process().execute(codesignPath, workingDirectory: nil, arguments: arguments) 279 | 280 | if codesignTask.status != 0 { 281 | //MARK: alert if certificate expired 282 | delegate?.codeSignLogRecord(logDes: "Error codesigning \(file) error:\(codesignTask.output)") 283 | } 284 | } 285 | 286 | 287 | func recursiveDirectorySearch(_ path: String, extensions: [String], found: ((_ file: String) -> Void)) { 288 | if let files = try? fileManager.contentsOfDirectory(atPath: path) { 289 | var isDirectory: ObjCBool = true 290 | for file in files { 291 | let currentFile = path.appendPathComponent(file) 292 | fileManager.fileExists(atPath: currentFile, isDirectory: &isDirectory) 293 | if isDirectory.boolValue { 294 | recursiveDirectorySearch(currentFile, extensions: extensions, found: found) 295 | } 296 | if extensions.contains(file.pathExtension) { 297 | found(currentFile) 298 | } 299 | } 300 | } 301 | } 302 | 303 | } 304 | -------------------------------------------------------------------------------- /ResignForiOS.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 48; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8A4B79862016D5E2007ACEA4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79852016D5E2007ACEA4 /* AppDelegate.swift */; }; 11 | 8A4B79882016D5E2007ACEA4 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79872016D5E2007ACEA4 /* ViewController.swift */; }; 12 | 8A4B798A2016D5E2007ACEA4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8A4B79892016D5E2007ACEA4 /* Assets.xcassets */; }; 13 | 8A4B798D2016D5E2007ACEA4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 8A4B798B2016D5E2007ACEA4 /* Main.storyboard */; }; 14 | 8A4B799B2016D6F5007ACEA4 /* ProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */; }; 15 | 8A4B799C2016D6F5007ACEA4 /* CodeSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */; }; 16 | 8A4B799D2016D6F5007ACEA4 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */; }; 17 | 8A4B799E2016D6F5007ACEA4 /* PlistHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */; }; 18 | 8A4B799F2016D6F5007ACEA4 /* ProcessTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */; }; 19 | 8A4B79A02016D6F5007ACEA4 /* Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B799A2016D6F5007ACEA4 /* Tools.swift */; }; 20 | 8A4B79A22016D71E007ACEA4 /* UpdateAppleCer.sh in Resources */ = {isa = PBXBuildFile; fileRef = 8A4B79A12016D71E007ACEA4 /* UpdateAppleCer.sh */; }; 21 | 8A4B79A42016DBB6007ACEA4 /* MainSignView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79A32016DBB6007ACEA4 /* MainSignView.swift */; }; 22 | 8A66D1F8203ECAE200A6D27A /* HProgregressHUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A66D1F7203ECAE200A6D27A /* HProgregressHUD.swift */; }; 23 | 8AF9995F20AD85EB00661F05 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF9995E20AD85EB00661F05 /* main.swift */; }; 24 | 8AF9996320AD868F00661F05 /* CodeSigner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */; }; 25 | 8AF9996420AD869100661F05 /* PlistHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */; }; 26 | 8AF9996520AD869800661F05 /* ProcessTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */; }; 27 | 8AF9996620AD869D00661F05 /* ProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */; }; 28 | 8AF9996720AD86A000661F05 /* StringExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */; }; 29 | 8AF9996820AD86A900661F05 /* Tools.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A4B799A2016D6F5007ACEA4 /* Tools.swift */; }; 30 | 8AF9996A20AD8E7000661F05 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AF9996920AD8E7000661F05 /* Core.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXCopyFilesBuildPhase section */ 34 | 8AF9995A20AD85EB00661F05 /* CopyFiles */ = { 35 | isa = PBXCopyFilesBuildPhase; 36 | buildActionMask = 2147483647; 37 | dstPath = /usr/share/man/man1/; 38 | dstSubfolderSpec = 0; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 1; 42 | }; 43 | /* End PBXCopyFilesBuildPhase section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 8A4B79822016D5E2007ACEA4 /* ResignForiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ResignForiOS.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 8A4B79852016D5E2007ACEA4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 48 | 8A4B79872016D5E2007ACEA4 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 49 | 8A4B79892016D5E2007ACEA4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | 8A4B798C2016D5E2007ACEA4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 51 | 8A4B798E2016D5E2007ACEA4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 8A4B798F2016D5E2007ACEA4 /* ResignForiOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ResignForiOS.entitlements; sourceTree = ""; }; 53 | 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProfileManager.swift; sourceTree = ""; }; 54 | 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CodeSigner.swift; sourceTree = ""; }; 55 | 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtension.swift; sourceTree = ""; }; 56 | 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PlistHelper.swift; sourceTree = ""; }; 57 | 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProcessTask.swift; sourceTree = ""; }; 58 | 8A4B799A2016D6F5007ACEA4 /* Tools.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Tools.swift; sourceTree = ""; }; 59 | 8A4B79A12016D71E007ACEA4 /* UpdateAppleCer.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = UpdateAppleCer.sh; sourceTree = ""; }; 60 | 8A4B79A32016DBB6007ACEA4 /* MainSignView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainSignView.swift; sourceTree = ""; }; 61 | 8A66D1F7203ECAE200A6D27A /* HProgregressHUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HProgregressHUD.swift; sourceTree = ""; }; 62 | 8AF9995C20AD85EB00661F05 /* CoolResign */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = CoolResign; sourceTree = BUILT_PRODUCTS_DIR; }; 63 | 8AF9995E20AD85EB00661F05 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 64 | 8AF9996920AD8E7000661F05 /* Core.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Core.swift; sourceTree = ""; }; 65 | /* End PBXFileReference section */ 66 | 67 | /* Begin PBXFrameworksBuildPhase section */ 68 | 8A4B797F2016D5E2007ACEA4 /* Frameworks */ = { 69 | isa = PBXFrameworksBuildPhase; 70 | buildActionMask = 2147483647; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 0; 74 | }; 75 | 8AF9995920AD85EB00661F05 /* Frameworks */ = { 76 | isa = PBXFrameworksBuildPhase; 77 | buildActionMask = 2147483647; 78 | files = ( 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 8A4B79792016D5E2007ACEA4 = { 86 | isa = PBXGroup; 87 | children = ( 88 | 8A4B79842016D5E2007ACEA4 /* ResignForiOS */, 89 | 8AF9995D20AD85EB00661F05 /* CoolResign */, 90 | 8A4B79832016D5E2007ACEA4 /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 8A4B79832016D5E2007ACEA4 /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 8A4B79822016D5E2007ACEA4 /* ResignForiOS.app */, 98 | 8AF9995C20AD85EB00661F05 /* CoolResign */, 99 | ); 100 | name = Products; 101 | sourceTree = ""; 102 | }; 103 | 8A4B79842016D5E2007ACEA4 /* ResignForiOS */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 8A4B79A12016D71E007ACEA4 /* UpdateAppleCer.sh */, 107 | 8A4B79962016D6F5007ACEA4 /* CodeSigner.swift */, 108 | 8A4B79982016D6F5007ACEA4 /* PlistHelper.swift */, 109 | 8A4B79992016D6F5007ACEA4 /* ProcessTask.swift */, 110 | 8A4B79952016D6F5007ACEA4 /* ProfileManager.swift */, 111 | 8A4B79972016D6F5007ACEA4 /* StringExtension.swift */, 112 | 8A4B799A2016D6F5007ACEA4 /* Tools.swift */, 113 | 8A4B79852016D5E2007ACEA4 /* AppDelegate.swift */, 114 | 8A4B79872016D5E2007ACEA4 /* ViewController.swift */, 115 | 8A4B79A32016DBB6007ACEA4 /* MainSignView.swift */, 116 | 8A66D1F7203ECAE200A6D27A /* HProgregressHUD.swift */, 117 | 8A4B79892016D5E2007ACEA4 /* Assets.xcassets */, 118 | 8A4B798B2016D5E2007ACEA4 /* Main.storyboard */, 119 | 8A4B798E2016D5E2007ACEA4 /* Info.plist */, 120 | 8A4B798F2016D5E2007ACEA4 /* ResignForiOS.entitlements */, 121 | ); 122 | path = ResignForiOS; 123 | sourceTree = ""; 124 | }; 125 | 8AF9995D20AD85EB00661F05 /* CoolResign */ = { 126 | isa = PBXGroup; 127 | children = ( 128 | 8AF9996920AD8E7000661F05 /* Core.swift */, 129 | 8AF9995E20AD85EB00661F05 /* main.swift */, 130 | ); 131 | path = CoolResign; 132 | sourceTree = ""; 133 | }; 134 | /* End PBXGroup section */ 135 | 136 | /* Begin PBXNativeTarget section */ 137 | 8A4B79812016D5E2007ACEA4 /* ResignForiOS */ = { 138 | isa = PBXNativeTarget; 139 | buildConfigurationList = 8A4B79922016D5E2007ACEA4 /* Build configuration list for PBXNativeTarget "ResignForiOS" */; 140 | buildPhases = ( 141 | 8A4B797E2016D5E2007ACEA4 /* Sources */, 142 | 8A4B797F2016D5E2007ACEA4 /* Frameworks */, 143 | 8A4B79802016D5E2007ACEA4 /* Resources */, 144 | ); 145 | buildRules = ( 146 | ); 147 | dependencies = ( 148 | ); 149 | name = ResignForiOS; 150 | productName = ResignForiOS; 151 | productReference = 8A4B79822016D5E2007ACEA4 /* ResignForiOS.app */; 152 | productType = "com.apple.product-type.application"; 153 | }; 154 | 8AF9995B20AD85EB00661F05 /* CoolResign */ = { 155 | isa = PBXNativeTarget; 156 | buildConfigurationList = 8AF9996220AD85EB00661F05 /* Build configuration list for PBXNativeTarget "CoolResign" */; 157 | buildPhases = ( 158 | 8AF9995820AD85EB00661F05 /* Sources */, 159 | 8AF9995920AD85EB00661F05 /* Frameworks */, 160 | 8AF9995A20AD85EB00661F05 /* CopyFiles */, 161 | ); 162 | buildRules = ( 163 | ); 164 | dependencies = ( 165 | ); 166 | name = CoolResign; 167 | productName = CoolResign; 168 | productReference = 8AF9995C20AD85EB00661F05 /* CoolResign */; 169 | productType = "com.apple.product-type.tool"; 170 | }; 171 | /* End PBXNativeTarget section */ 172 | 173 | /* Begin PBXProject section */ 174 | 8A4B797A2016D5E2007ACEA4 /* Project object */ = { 175 | isa = PBXProject; 176 | attributes = { 177 | LastSwiftUpdateCheck = 0930; 178 | LastUpgradeCheck = 0920; 179 | ORGANIZATIONNAME = cheng; 180 | TargetAttributes = { 181 | 8A4B79812016D5E2007ACEA4 = { 182 | CreatedOnToolsVersion = 9.2; 183 | ProvisioningStyle = Manual; 184 | SystemCapabilities = { 185 | com.apple.Sandbox = { 186 | enabled = 0; 187 | }; 188 | }; 189 | }; 190 | 8AF9995B20AD85EB00661F05 = { 191 | CreatedOnToolsVersion = 9.3; 192 | ProvisioningStyle = Automatic; 193 | }; 194 | }; 195 | }; 196 | buildConfigurationList = 8A4B797D2016D5E2007ACEA4 /* Build configuration list for PBXProject "ResignForiOS" */; 197 | compatibilityVersion = "Xcode 8.0"; 198 | developmentRegion = en; 199 | hasScannedForEncodings = 0; 200 | knownRegions = ( 201 | en, 202 | Base, 203 | ); 204 | mainGroup = 8A4B79792016D5E2007ACEA4; 205 | productRefGroup = 8A4B79832016D5E2007ACEA4 /* Products */; 206 | projectDirPath = ""; 207 | projectRoot = ""; 208 | targets = ( 209 | 8A4B79812016D5E2007ACEA4 /* ResignForiOS */, 210 | 8AF9995B20AD85EB00661F05 /* CoolResign */, 211 | ); 212 | }; 213 | /* End PBXProject section */ 214 | 215 | /* Begin PBXResourcesBuildPhase section */ 216 | 8A4B79802016D5E2007ACEA4 /* Resources */ = { 217 | isa = PBXResourcesBuildPhase; 218 | buildActionMask = 2147483647; 219 | files = ( 220 | 8A4B798A2016D5E2007ACEA4 /* Assets.xcassets in Resources */, 221 | 8A4B79A22016D71E007ACEA4 /* UpdateAppleCer.sh in Resources */, 222 | 8A4B798D2016D5E2007ACEA4 /* Main.storyboard in Resources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | /* End PBXResourcesBuildPhase section */ 227 | 228 | /* Begin PBXSourcesBuildPhase section */ 229 | 8A4B797E2016D5E2007ACEA4 /* Sources */ = { 230 | isa = PBXSourcesBuildPhase; 231 | buildActionMask = 2147483647; 232 | files = ( 233 | 8A4B79882016D5E2007ACEA4 /* ViewController.swift in Sources */, 234 | 8A4B79A42016DBB6007ACEA4 /* MainSignView.swift in Sources */, 235 | 8A4B799F2016D6F5007ACEA4 /* ProcessTask.swift in Sources */, 236 | 8A4B799B2016D6F5007ACEA4 /* ProfileManager.swift in Sources */, 237 | 8A66D1F8203ECAE200A6D27A /* HProgregressHUD.swift in Sources */, 238 | 8A4B799D2016D6F5007ACEA4 /* StringExtension.swift in Sources */, 239 | 8A4B799C2016D6F5007ACEA4 /* CodeSigner.swift in Sources */, 240 | 8A4B799E2016D6F5007ACEA4 /* PlistHelper.swift in Sources */, 241 | 8A4B79862016D5E2007ACEA4 /* AppDelegate.swift in Sources */, 242 | 8A4B79A02016D6F5007ACEA4 /* Tools.swift in Sources */, 243 | ); 244 | runOnlyForDeploymentPostprocessing = 0; 245 | }; 246 | 8AF9995820AD85EB00661F05 /* Sources */ = { 247 | isa = PBXSourcesBuildPhase; 248 | buildActionMask = 2147483647; 249 | files = ( 250 | 8AF9996320AD868F00661F05 /* CodeSigner.swift in Sources */, 251 | 8AF9996620AD869D00661F05 /* ProfileManager.swift in Sources */, 252 | 8AF9996720AD86A000661F05 /* StringExtension.swift in Sources */, 253 | 8AF9996420AD869100661F05 /* PlistHelper.swift in Sources */, 254 | 8AF9996820AD86A900661F05 /* Tools.swift in Sources */, 255 | 8AF9996520AD869800661F05 /* ProcessTask.swift in Sources */, 256 | 8AF9996A20AD8E7000661F05 /* Core.swift in Sources */, 257 | 8AF9995F20AD85EB00661F05 /* main.swift in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin PBXVariantGroup section */ 264 | 8A4B798B2016D5E2007ACEA4 /* Main.storyboard */ = { 265 | isa = PBXVariantGroup; 266 | children = ( 267 | 8A4B798C2016D5E2007ACEA4 /* Base */, 268 | ); 269 | name = Main.storyboard; 270 | sourceTree = ""; 271 | }; 272 | /* End PBXVariantGroup section */ 273 | 274 | /* Begin XCBuildConfiguration section */ 275 | 8A4B79902016D5E2007ACEA4 /* Debug */ = { 276 | isa = XCBuildConfiguration; 277 | buildSettings = { 278 | ALWAYS_SEARCH_USER_PATHS = NO; 279 | CLANG_ANALYZER_NONNULL = YES; 280 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 281 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 282 | CLANG_CXX_LIBRARY = "libc++"; 283 | CLANG_ENABLE_MODULES = YES; 284 | CLANG_ENABLE_OBJC_ARC = YES; 285 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 286 | CLANG_WARN_BOOL_CONVERSION = YES; 287 | CLANG_WARN_COMMA = YES; 288 | CLANG_WARN_CONSTANT_CONVERSION = YES; 289 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 290 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 291 | CLANG_WARN_EMPTY_BODY = YES; 292 | CLANG_WARN_ENUM_CONVERSION = YES; 293 | CLANG_WARN_INFINITE_RECURSION = YES; 294 | CLANG_WARN_INT_CONVERSION = YES; 295 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 296 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 297 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 298 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 299 | CLANG_WARN_STRICT_PROTOTYPES = YES; 300 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 301 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 302 | CLANG_WARN_UNREACHABLE_CODE = YES; 303 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 304 | CODE_SIGN_IDENTITY = "-"; 305 | COPY_PHASE_STRIP = NO; 306 | DEBUG_INFORMATION_FORMAT = dwarf; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | ENABLE_TESTABILITY = YES; 309 | GCC_C_LANGUAGE_STANDARD = gnu11; 310 | GCC_DYNAMIC_NO_PIC = NO; 311 | GCC_NO_COMMON_BLOCKS = YES; 312 | GCC_OPTIMIZATION_LEVEL = 0; 313 | GCC_PREPROCESSOR_DEFINITIONS = ( 314 | "DEBUG=1", 315 | "$(inherited)", 316 | ); 317 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 318 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 319 | GCC_WARN_UNDECLARED_SELECTOR = YES; 320 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 321 | GCC_WARN_UNUSED_FUNCTION = YES; 322 | GCC_WARN_UNUSED_VARIABLE = YES; 323 | MACOSX_DEPLOYMENT_TARGET = 10.10; 324 | MTL_ENABLE_DEBUG_INFO = YES; 325 | ONLY_ACTIVE_ARCH = YES; 326 | SDKROOT = macosx; 327 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 328 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 329 | }; 330 | name = Debug; 331 | }; 332 | 8A4B79912016D5E2007ACEA4 /* Release */ = { 333 | isa = XCBuildConfiguration; 334 | buildSettings = { 335 | ALWAYS_SEARCH_USER_PATHS = NO; 336 | CLANG_ANALYZER_NONNULL = YES; 337 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 339 | CLANG_CXX_LIBRARY = "libc++"; 340 | CLANG_ENABLE_MODULES = YES; 341 | CLANG_ENABLE_OBJC_ARC = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 347 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 348 | CLANG_WARN_EMPTY_BODY = YES; 349 | CLANG_WARN_ENUM_CONVERSION = YES; 350 | CLANG_WARN_INFINITE_RECURSION = YES; 351 | CLANG_WARN_INT_CONVERSION = YES; 352 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 353 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 355 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 356 | CLANG_WARN_STRICT_PROTOTYPES = YES; 357 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 358 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 359 | CLANG_WARN_UNREACHABLE_CODE = YES; 360 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 361 | CODE_SIGN_IDENTITY = "-"; 362 | COPY_PHASE_STRIP = NO; 363 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 364 | ENABLE_NS_ASSERTIONS = NO; 365 | ENABLE_STRICT_OBJC_MSGSEND = YES; 366 | GCC_C_LANGUAGE_STANDARD = gnu11; 367 | GCC_NO_COMMON_BLOCKS = YES; 368 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 369 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 370 | GCC_WARN_UNDECLARED_SELECTOR = YES; 371 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 372 | GCC_WARN_UNUSED_FUNCTION = YES; 373 | GCC_WARN_UNUSED_VARIABLE = YES; 374 | MACOSX_DEPLOYMENT_TARGET = 10.10; 375 | MTL_ENABLE_DEBUG_INFO = NO; 376 | SDKROOT = macosx; 377 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 378 | }; 379 | name = Release; 380 | }; 381 | 8A4B79932016D5E2007ACEA4 /* Debug */ = { 382 | isa = XCBuildConfiguration; 383 | buildSettings = { 384 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 385 | CODE_SIGN_STYLE = Manual; 386 | COMBINE_HIDPI_IMAGES = YES; 387 | DEVELOPMENT_TEAM = ""; 388 | INFOPLIST_FILE = ResignForiOS/Info.plist; 389 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 390 | PRODUCT_BUNDLE_IDENTIFIER = com.chengcheng.ResignForiOS; 391 | PRODUCT_NAME = "$(TARGET_NAME)"; 392 | PROVISIONING_PROFILE_SPECIFIER = ""; 393 | SWIFT_VERSION = 4.0; 394 | }; 395 | name = Debug; 396 | }; 397 | 8A4B79942016D5E2007ACEA4 /* Release */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 401 | CODE_SIGN_STYLE = Manual; 402 | COMBINE_HIDPI_IMAGES = YES; 403 | DEVELOPMENT_TEAM = ""; 404 | INFOPLIST_FILE = ResignForiOS/Info.plist; 405 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 406 | PRODUCT_BUNDLE_IDENTIFIER = com.chengcheng.ResignForiOS; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | PROVISIONING_PROFILE_SPECIFIER = ""; 409 | SWIFT_VERSION = 4.0; 410 | }; 411 | name = Release; 412 | }; 413 | 8AF9996020AD85EB00661F05 /* Debug */ = { 414 | isa = XCBuildConfiguration; 415 | buildSettings = { 416 | CLANG_ENABLE_OBJC_WEAK = YES; 417 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 418 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 419 | CODE_SIGN_STYLE = Automatic; 420 | MACOSX_DEPLOYMENT_TARGET = 10.13; 421 | PRODUCT_NAME = "$(TARGET_NAME)"; 422 | SWIFT_VERSION = 4.2; 423 | }; 424 | name = Debug; 425 | }; 426 | 8AF9996120AD85EB00661F05 /* Release */ = { 427 | isa = XCBuildConfiguration; 428 | buildSettings = { 429 | CLANG_ENABLE_OBJC_WEAK = YES; 430 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 431 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 432 | CODE_SIGN_STYLE = Automatic; 433 | MACOSX_DEPLOYMENT_TARGET = 10.13; 434 | PRODUCT_NAME = "$(TARGET_NAME)"; 435 | SWIFT_VERSION = 4.2; 436 | }; 437 | name = Release; 438 | }; 439 | /* End XCBuildConfiguration section */ 440 | 441 | /* Begin XCConfigurationList section */ 442 | 8A4B797D2016D5E2007ACEA4 /* Build configuration list for PBXProject "ResignForiOS" */ = { 443 | isa = XCConfigurationList; 444 | buildConfigurations = ( 445 | 8A4B79902016D5E2007ACEA4 /* Debug */, 446 | 8A4B79912016D5E2007ACEA4 /* Release */, 447 | ); 448 | defaultConfigurationIsVisible = 0; 449 | defaultConfigurationName = Release; 450 | }; 451 | 8A4B79922016D5E2007ACEA4 /* Build configuration list for PBXNativeTarget "ResignForiOS" */ = { 452 | isa = XCConfigurationList; 453 | buildConfigurations = ( 454 | 8A4B79932016D5E2007ACEA4 /* Debug */, 455 | 8A4B79942016D5E2007ACEA4 /* Release */, 456 | ); 457 | defaultConfigurationIsVisible = 0; 458 | defaultConfigurationName = Release; 459 | }; 460 | 8AF9996220AD85EB00661F05 /* Build configuration list for PBXNativeTarget "CoolResign" */ = { 461 | isa = XCConfigurationList; 462 | buildConfigurations = ( 463 | 8AF9996020AD85EB00661F05 /* Debug */, 464 | 8AF9996120AD85EB00661F05 /* Release */, 465 | ); 466 | defaultConfigurationIsVisible = 0; 467 | defaultConfigurationName = Release; 468 | }; 469 | /* End XCConfigurationList section */ 470 | }; 471 | rootObject = 8A4B797A2016D5E2007ACEA4 /* Project object */; 472 | } 473 | -------------------------------------------------------------------------------- /ResignForiOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | Default 532 | 533 | 534 | 535 | 536 | 537 | 538 | Left to Right 539 | 540 | 541 | 542 | 543 | 544 | 545 | Right to Left 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | Default 557 | 558 | 559 | 560 | 561 | 562 | 563 | Left to Right 564 | 565 | 566 | 567 | 568 | 569 | 570 | Right to Left 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 873 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | --------------------------------------------------------------------------------