public static string FindCompatibleProvision(string CFBundleIdentifier, out bool bNameMatch, bool bCheckCert = true, bool bCheckIdentifier = true, bool bCheckDistro = true) { bNameMatch = false; // remap the gamename if necessary string GameName = Program.GameName; if (GameName == "UE4Game") { if (Config.ProjectFile.Length > 0) { GameName = Path.GetFileNameWithoutExtension(Config.ProjectFile); } } // ensure the provision directory exists if (!Directory.Exists(Config.ProvisionDirectory)) { Directory.CreateDirectory(Config.ProvisionDirectory); } if (Config.bProvision) { if (File.Exists(Config.ProvisionDirectory + "/" + Config.Provision)) { return(Config.ProvisionDirectory + "/" + Config.Provision); } } #region remove after we provide an install mechanism CacheMobileProvisions(); #endregion // cache the provision library Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision p = MobileProvisionParser.ParseFile(Provision); ProvisionLibrary.Add(Provision, p); if (p.FileName.Contains(p.UUID) && !File.Exists(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"))) { File.Copy(Provision, Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision")); p = MobileProvisionParser.ParseFile(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision")); ProvisionLibrary.Add(Path.Combine(Config.ProvisionDirectory, "UE4_" + p.UUID + ".mobileprovision"), p); } } Program.Log("Searching for mobile provisions that match the game '{0}' (distribution: {3}) with CFBundleIdentifier='{1}' in '{2}'", GameName, CFBundleIdentifier, Config.ProvisionDirectory, Config.bForDistribution); // check the cache for a provision matching the app id (com.company.Game) // First checking for a contains match and then for a wildcard match for (int Phase = -1; Phase < 3; ++Phase) { if (Phase == -1 && string.IsNullOrEmpty(Config.ProvisionUUID)) { continue; } foreach (KeyValuePair <string, MobileProvision> Pair in ProvisionLibrary) { string DebugName = Path.GetFileName(Pair.Key); MobileProvision TestProvision = Pair.Value; // make sure the file is not managed by Xcode if (Path.GetFileName(TestProvision.FileName).ToLower().Equals(TestProvision.UUID.ToLower() + ".mobileprovision")) { continue; } Program.LogVerbose(" Phase {0} considering provision '{1}' named '{2}'", Phase, DebugName, TestProvision.ProvisionName); if (TestProvision.ProvisionName == "iOS Team Provisioning Profile: " + CFBundleIdentifier) { Program.LogVerbose(" Failing as provisioning is automatic"); continue; } // check to see if the platform is the same as what we are looking for if (!string.IsNullOrEmpty(TestProvision.Platform) && TestProvision.Platform != Config.OSString && !string.IsNullOrEmpty(Config.OSString)) { //Program.LogVerbose(" Failing platform {0} Config: {1}", TestProvision.Platform, Config.OSString); continue; } // Validate the name bool bPassesNameCheck = false; if (Phase == -1) { bPassesNameCheck = TestProvision.UUID == Config.ProvisionUUID; bNameMatch = bPassesNameCheck; } else if (Phase == 0) { bPassesNameCheck = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bNameMatch = bPassesNameCheck; } else if (Phase == 1) { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesNameCheck = CFBundleIdentifier.StartsWith(CompanyName); } } } else { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); bPassesNameCheck = CompanyName == "*"; } } if (!bPassesNameCheck && bCheckIdentifier) { Program.LogVerbose(" .. Failed phase {0} name check (provision app ID was {1})", Phase, TestProvision.ApplicationIdentifier); continue; } if (Config.bForDistribution) { // Check to see if this is a distribution provision. get-task-allow must be false for distro profiles. // TestProvision.ProvisionedDeviceIDs.Count==0 is not a valid check as ad-hoc distro profiles do list devices. bool bDistroProv = !TestProvision.bDebug; if (!bDistroProv) { Program.LogVerbose(" .. Failed distribution check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } else { if (bCheckDistro) { bool bPassesDebugCheck = TestProvision.bDebug; if (!bPassesDebugCheck) { Program.LogVerbose(" .. Failed debugging check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } else { if (!TestProvision.bDebug) { Config.bForceStripSymbols = true; } } } // Check to see if the provision is in date DateTime CurrentUTCTime = DateTime.UtcNow; bool bPassesDateCheck = (CurrentUTCTime >= TestProvision.CreationDate) && (CurrentUTCTime < TestProvision.ExpirationDate); if (!bPassesDateCheck) { Program.LogVerbose(" .. Failed time period check (valid from {0} to {1}, but UTC time is now {2})", TestProvision.CreationDate, TestProvision.ExpirationDate, CurrentUTCTime); continue; } // check to see if we have a certificate for this provision bool bPassesHasMatchingCertCheck = false; if (bCheckCert) { X509Certificate2 Cert = CodeSignatureBuilder.FindCertificate(TestProvision); bPassesHasMatchingCertCheck = (Cert != null); if (bPassesHasMatchingCertCheck && Config.bCert) { bPassesHasMatchingCertCheck &= (CryptoAdapter.GetFriendlyNameFromCert(Cert) == Config.Certificate); } } else { bPassesHasMatchingCertCheck = true; } if (!bPassesHasMatchingCertCheck) { Program.LogVerbose(" .. Failed to find a matching certificate that was in date"); continue; } // Made it past all the tests Program.LogVerbose(" Picked '{0}' with AppID '{1}' and Name '{2}' as a matching provision for the game '{3}'", DebugName, TestProvision.ApplicationIdentifier, TestProvision.ProvisionName, GameName); return(Pair.Key); } } // check to see if there is already an embedded provision string EmbeddedMobileProvisionFilename = Path.Combine(Config.RepackageStagingDirectory, "embedded.mobileprovision"); Program.Warning("Failed to find a valid matching mobile provision, will attempt to use the embedded mobile provision instead if present"); return(EmbeddedMobileProvisionFilename); }
/// <summary> /// Tries to find a matching certificate on this machine from the the serial number of one of the /// certificates in the mobile provision (the one in the mobileprovision is missing the public/private key pair) /// </summary> public static X509Certificate2 FindCertificate(MobileProvision ProvisionToWorkFrom) { Program.LogVerbose(" Looking for a certificate that matches the application identifier '{0}'", ProvisionToWorkFrom.ApplicationIdentifier); X509Certificate2 Result = null; if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { // run certtool y to get the currently installed certificates CertToolData = ""; Process CertTool = new Process(); CertTool.StartInfo.FileName = "/usr/bin/security"; CertTool.StartInfo.UseShellExecute = false; CertTool.StartInfo.Arguments = "find-identity -p codesigning -v"; CertTool.StartInfo.RedirectStandardOutput = true; CertTool.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedCertToolProcessCall); CertTool.Start(); CertTool.BeginOutputReadLine(); CertTool.WaitForExit(); if (CertTool.ExitCode == 0) { foreach (X509Certificate2 SourceCert in ProvisionToWorkFrom.DeveloperCertificates) { X509Certificate2 ValidInTimeCert = null; // see if certificate can be found by serial number string CertHash = SourceCert.GetCertHashString(); if (CertToolData.Contains(CertHash)) { ValidInTimeCert = SourceCert; } if (ValidInTimeCert != null) { int StartIndex = SourceCert.SubjectName.Name.IndexOf("CN=") + 3; int EndIndex = SourceCert.SubjectName.Name.IndexOf(", ", StartIndex); SourceCert.FriendlyName = SourceCert.SubjectName.Name.Substring(StartIndex, EndIndex - StartIndex); // Found a cert in the valid time range, quit now! Result = ValidInTimeCert; break; } } } } else { // Open the personal certificate store on this machine X509Store Store = new X509Store(); Store.Open(OpenFlags.ReadOnly); // Try finding a matching certificate from the serial number (the one in the mobileprovision is missing the public/private key pair) foreach (X509Certificate2 SourceCert in ProvisionToWorkFrom.DeveloperCertificates) { X509Certificate2Collection FoundCerts = Store.Certificates.Find(X509FindType.FindBySerialNumber, SourceCert.SerialNumber, false); Program.LogVerbose(" .. Provision entry SN '{0}' matched {1} installed certificate(s)", SourceCert.SerialNumber, FoundCerts.Count); X509Certificate2 ValidInTimeCert = null; foreach (X509Certificate2 TestCert in FoundCerts) { //@TODO: Pretty sure the certificate information from the library is in local time, not UTC and this works as expected, but it should be verified! DateTime EffectiveDate = TestCert.NotBefore; DateTime ExpirationDate = TestCert.NotAfter; DateTime Now = DateTime.Now; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); Program.LogVerbose(" .. .. Installed certificate '{0}' is {1} (range '{2}' to '{3}')", TestCert.FriendlyName, bCertTimeIsValid ? "valid (choosing it)" : "EXPIRED", TestCert.GetEffectiveDateString(), TestCert.GetExpirationDateString()); if (bCertTimeIsValid) { ValidInTimeCert = TestCert; break; } } if (ValidInTimeCert != null) { // Found a cert in the valid time range, quit now! Result = ValidInTimeCert; break; } } Store.Close(); } if (Result == null) { Program.LogVerbose(" .. Failed to find a valid certificate that was in date"); } return(Result); }
public static string FindCompatibleProvision(string CFBundleIdentifier, out bool bNameMatch, bool bCheckCert = true, bool bCheckIdentifier = true) { bNameMatch = false; // remap the gamename if necessary string GameName = Program.GameName; if (GameName == "UE4Game") { if (Config.ProjectFile.Length > 0) { GameName = Path.GetFileNameWithoutExtension(Config.ProjectFile); } } // ensure the provision directory exists if (!Directory.Exists(Config.ProvisionDirectory)) { Directory.CreateDirectory(Config.ProvisionDirectory); } if (Config.bProvision) { if (File.Exists(Config.ProvisionDirectory + "/" + Config.Provision)) { return(Config.ProvisionDirectory + "/" + Config.Provision); } } #region remove after we provide an install mechanism // copy all of the provisions from the game directory to the library if (!String.IsNullOrEmpty(Config.ProjectFile)) { var ProjectFileBuildIOSPath = Path.GetDirectoryName(Config.ProjectFile) + "/Build/IOS/"; if (Directory.Exists(ProjectFileBuildIOSPath)) { foreach (string Provision in Directory.EnumerateFiles(ProjectFileBuildIOSPath, "*.mobileprovision", SearchOption.AllDirectories)) { if (!File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)) || File.GetLastWriteTime(Config.ProvisionDirectory + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision)) { FileInfo DestFileInfo; if (File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision))) { DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } File.Copy(Provision, Config.ProvisionDirectory + Path.GetFileName(Provision), true); DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } } } } // copy all of the provisions from the engine directory to the library { if (Directory.Exists(Config.EngineBuildDirectory)) { foreach (string Provision in Directory.EnumerateFiles(Config.EngineBuildDirectory, "*.mobileprovision", SearchOption.AllDirectories)) { if (!File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision)) || File.GetLastWriteTime(Config.ProvisionDirectory + Path.GetFileName(Provision)) < File.GetLastWriteTime(Provision)) { FileInfo DestFileInfo; if (File.Exists(Config.ProvisionDirectory + Path.GetFileName(Provision))) { DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } File.Copy(Provision, Config.ProvisionDirectory + Path.GetFileName(Provision), true); DestFileInfo = new FileInfo(Config.ProvisionDirectory + Path.GetFileName(Provision)); DestFileInfo.Attributes = DestFileInfo.Attributes & ~FileAttributes.ReadOnly; } } } } #endregion // cache the provision library Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision p = MobileProvisionParser.ParseFile(Provision); ProvisionLibrary.Add(Provision, p); } Program.Log("Searching for mobile provisions that match the game '{0}' with CFBundleIdentifier='{1}' in '{2}'", GameName, CFBundleIdentifier, Config.ProvisionDirectory); // check the cache for a provision matching the app id (com.company.Game) // First checking for a contains match and then for a wildcard match for (int Phase = 0; Phase < 3; ++Phase) { foreach (KeyValuePair <string, MobileProvision> Pair in ProvisionLibrary) { string DebugName = Path.GetFileName(Pair.Key); MobileProvision TestProvision = Pair.Value; Program.LogVerbose(" Phase {0} considering provision '{1}' named '{2}'", Phase, DebugName, TestProvision.ProvisionName); // Validate the name bool bPassesNameCheck = false; if (Phase == 0) { bPassesNameCheck = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bNameMatch = bPassesNameCheck; } else if (Phase == 1) { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesNameCheck = CFBundleIdentifier.StartsWith(CompanyName); } } } else { if (TestProvision.ApplicationIdentifier.Contains("*")) { string CompanyName = TestProvision.ApplicationIdentifier.Substring(TestProvision.ApplicationIdentifierPrefix.Length + 1); bPassesNameCheck = CompanyName == "*"; } } if (!bPassesNameCheck && bCheckIdentifier) { Program.LogVerbose(" .. Failed phase {0} name check (provision app ID was {1})", Phase, TestProvision.ApplicationIdentifier); continue; } if (Config.bForDistribution) { // check to see if this is a distribution provision bool bDistroProv = (TestProvision.ProvisionedDeviceIDs.Count == 0) && !TestProvision.bDebug; if (!bDistroProv) { Program.LogVerbose(" .. Failed distribution check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } else { // check to see if we pass the debug check for non-distribution bool bPassesDebugCheck = TestProvision.bDebug; if (!bPassesDebugCheck) { Program.LogVerbose(" .. Failed debugging check (mode={0}, get-task-allow={1}, #devices={2})", Config.bForDistribution, TestProvision.bDebug, TestProvision.ProvisionedDeviceIDs.Count); continue; } } // Check to see if the provision is in date DateTime CurrentUTCTime = DateTime.UtcNow; bool bPassesDateCheck = (CurrentUTCTime >= TestProvision.CreationDate) && (CurrentUTCTime < TestProvision.ExpirationDate); if (!bPassesDateCheck) { Program.LogVerbose(" .. Failed time period check (valid from {0} to {1}, but UTC time is now {2})", TestProvision.CreationDate, TestProvision.ExpirationDate, CurrentUTCTime); continue; } // check to see if we have a certificate for this provision bool bPassesHasMatchingCertCheck = false; if (bCheckCert) { X509Certificate2 Cert = CodeSignatureBuilder.FindCertificate(TestProvision); bPassesHasMatchingCertCheck = (Cert != null); if (bPassesHasMatchingCertCheck && Config.bCert) { bPassesHasMatchingCertCheck &= (Cert.FriendlyName == Config.Certificate); } } else { bPassesHasMatchingCertCheck = true; } if (!bPassesHasMatchingCertCheck) { Program.LogVerbose(" .. Failed to find a matching certificate that was in date"); continue; } // Made it past all the tests Program.LogVerbose(" Picked '{0}' with AppID '{1}' and Name '{2}' as a matching provision for the game '{3}'", DebugName, TestProvision.ApplicationIdentifier, TestProvision.ProvisionName, GameName); return(Pair.Key); } } // check to see if there is already an embedded provision string EmbeddedMobileProvisionFilename = Path.Combine(Config.RepackageStagingDirectory, "embedded.mobileprovision"); Program.Warning("Failed to find a valid matching mobile provision, will attempt to use the embedded mobile provision instead if present"); return(EmbeddedMobileProvisionFilename); }
/// <summary> /// Finds all valid installed provisions /// </summary> public static void FindProvisions(string CFBundleIdentifier) { // cache the provision library string SelectedProvision = ""; string SelectedCert = ""; string SelectedFile = ""; int FoundName = -1; Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string Provision in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision p = MobileProvisionParser.ParseFile(Provision); DateTime EffectiveDate = p.CreationDate; DateTime ExpirationDate = p.ExpirationDate; DateTime Now = DateTime.UtcNow; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); bool bValid = false; X509Certificate2 Cert = FindCertificate(p); if (Cert != null) { bValid = (Cert.NotBefore < Now) && (Cert.NotAfter > Now); } bool bPassesNameCheck = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bool bPassesCompanyCheck = false; bool bPassesWildCardCheck = false; if (p.ApplicationIdentifier.Contains("*")) { string CompanyName = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesCompanyCheck = CFBundleIdentifier.StartsWith(CompanyName); } else { bPassesWildCardCheck = true; } } bool bDistribution = ((p.ProvisionedDeviceIDs.Count == 0) && !p.bDebug); string Validity = "VALID"; if (!bCertTimeIsValid) { Validity = "EXPIRED"; } else if (!bValid) { Validity = "NO_CERT"; } else if (!bPassesNameCheck && !bPassesWildCardCheck && !bPassesCompanyCheck) { Validity = "NO_MATCH"; } if ((string.IsNullOrWhiteSpace(SelectedProvision) || FoundName < 2) && Validity == "VALID" && !bDistribution) { int Prev = FoundName; if (bPassesNameCheck) { FoundName = 2; } else if (bPassesCompanyCheck && FoundName < 1) { FoundName = 1; } else if (bPassesWildCardCheck && FoundName == -1) { FoundName = 0; } if (FoundName != Prev) { SelectedProvision = p.ProvisionName; SelectedFile = Path.GetFileName(Provision); SelectedCert = Cert.FriendlyName; } } Program.LogVerbose("PROVISION-File:{0},Name:{1},Validity:{2},StartDate:{3},EndDate:{4},Type:{5}", Path.GetFileName(Provision), p.ProvisionName, Validity, EffectiveDate.ToString(), ExpirationDate.ToString(), bDistribution ? "DISTRIBUTION" : "DEVELOPMENT"); } Program.LogVerbose("MATCHED-Provision:{0},File:{1},Cert:{2}", SelectedProvision, SelectedFile, SelectedCert); }
/// <summary> /// Finds all valid installed certificates /// </summary> public static void FindCertificates() { if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { // run certtool y to get the currently installed certificates CertToolData = ""; Process CertTool = new Process(); CertTool.StartInfo.FileName = "/usr/bin/security"; CertTool.StartInfo.UseShellExecute = false; CertTool.StartInfo.Arguments = "find-certificate -a -c \"iPhone\" -p"; CertTool.StartInfo.RedirectStandardOutput = true; CertTool.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedCertToolProcessCall); CertTool.Start(); CertTool.BeginOutputReadLine(); CertTool.WaitForExit(); if (CertTool.ExitCode == 0) { string header = "-----BEGIN CERTIFICATE-----\n"; string footer = "-----END CERTIFICATE-----"; int start = CertToolData.IndexOf(header); while (start != -1) { start += header.Length; int end = CertToolData.IndexOf(footer, start); string base64 = CertToolData.Substring(start, (end - start)); byte[] certData = Convert.FromBase64String(base64); X509Certificate2 cert = new X509Certificate2(certData); DateTime EffectiveDate = cert.NotBefore; DateTime ExpirationDate = cert.NotAfter; DateTime Now = DateTime.UtcNow; string Subject = cert.Subject; int SubjStart = Subject.IndexOf("CN=") + 3; int SubjEnd = Subject.IndexOf(",", SubjStart); cert.FriendlyName = Subject.Substring(SubjStart, (SubjEnd - SubjStart)); bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); Program.LogVerbose("CERTIFICATE-Name:{0},Validity:{1},StartDate:{2},EndDate:{3}", cert.FriendlyName, bCertTimeIsValid ? "VALID" : "EXPIRED", EffectiveDate.ToString("o"), ExpirationDate.ToString("o")); start = CertToolData.IndexOf(header, start); } } } else { // Open the personal certificate store on this machine X509Store Store = new X509Store(); Store.Open(OpenFlags.ReadOnly); // Try finding all certificates that match either iPhone Developer or iPhone Distribution X509Certificate2Collection FoundCerts = Store.Certificates.Find(X509FindType.FindBySubjectName, "iPhone Developer", false); foreach (X509Certificate2 TestCert in FoundCerts) { DateTime EffectiveDate = TestCert.NotBefore; DateTime ExpirationDate = TestCert.NotAfter; DateTime Now = DateTime.Now; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); Program.LogVerbose("CERTIFICATE-Name:{0},Validity:{1},StartDate:{2},EndDate:{3}", TestCert.FriendlyName, bCertTimeIsValid ? "VALID" : "EXPIRED", EffectiveDate.ToString("o"), ExpirationDate.ToString("o")); } FoundCerts = Store.Certificates.Find(X509FindType.FindBySubjectName, "iPhone Distribution", false); foreach (X509Certificate2 TestCert in FoundCerts) { DateTime EffectiveDate = TestCert.NotBefore; DateTime ExpirationDate = TestCert.NotAfter; DateTime Now = DateTime.Now; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); Program.LogVerbose("CERTIFICATE-Name:{0},Validity:{1},StartDate:{2},EndDate:{3}", TestCert.FriendlyName, bCertTimeIsValid ? "VALID" : "EXPIRED", EffectiveDate.ToString("o"), ExpirationDate.ToString("o")); } Store.Close(); } }
static int Main(string[] args) { // remember the working directory at start, as the game path could be relative to this path string InitialCurrentDirectory = Environment.CurrentDirectory; // set the working directory to the location of the application (so relative paths always work) Environment.CurrentDirectory = Path.GetDirectoryName(Application.ExecutablePath); AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); // A simple, top-level try-catch block try { if (!ParseCommandLine(ref args)) { Log("Usage: iPhonePackager <Command> <GameName> [RPCCommand &| Switch]"); Log(""); Log("Common commands:"); Log(" ... RepackageIPA GameName"); Log(" ... PackageIPA GameName"); Log(" ... PackageApp GameName"); Log(" ... Deploy PathToIPA"); Log(" ... RepackageFromStage GameName"); Log(" ... Devices"); Log(" ... Validate"); Log(" ... Install"); Log(""); Log("Configuration switches:"); Log(" -stagedir <path> sets the directory to copy staged files from (defaults to none)"); Log(" -project <path> path to the project being packaged"); Log(" -provisioning <uuid> uuid of the provisioning selected"); Log(" -compress=fast|best|none packaging compression level (defaults to none)"); Log(" -strip strip symbols during packaging"); Log(" -config game configuration (e.g., Shipping, Development, etc...)"); Log(" -distribution packaging for final distribution"); Log(" -codebased packaging a c++ code based project"); Log(" -createstub packaging stub IPA for later repackaging"); Log(" -mac <MacName> overrides the machine to use for any Mac operations"); Log(" -arch <Architecture> sets the architecture to use (blank for default, -simulator for simulator builds)"); Log(" -device <DeviceID> sets the device to install the IPA on"); Log(""); Log("Commands: RPC, Clean"); Log(" StageMacFiles, GetIPA, Deploy, Install, Uninstall"); Log(""); Log("RPC Commands: SetExec, InstallProvision, MakeApp, DeleteIPA, Copy, Kill, Strip, Zip, GenDSYM"); Log(""); Log("Sample commandlines:"); Log(" ... iPhonePackager Deploy UDKGame Release"); Log(" ... iPhonePackager RPC SwordGame Shipping MakeApp"); return((int)ErrorCodes.Error_Arguments); } Log("Executing iPhonePackager " + String.Join(" ", args)); Log("CWD: " + Directory.GetCurrentDirectory()); Log("Initial Dir: " + InitialCurrentDirectory); Log("Env CWD: " + Environment.CurrentDirectory); // Ensure shipping configuration for final distributions if (Config.bForDistribution && (GameConfiguration != "Shipping")) { Program.Warning("Distribution builds should be made in the Shipping configuration!"); } // process the GamePath (if could be ..\Samples\MyDemo\ or ..\Samples\MyDemo\MyDemo.uproject GameName = Path.GetFileNameWithoutExtension(GamePath); if (GameName.Equals("UE4", StringComparison.InvariantCultureIgnoreCase) || GameName.Equals("Engine", StringComparison.InvariantCultureIgnoreCase)) { GameName = "UE4Game"; } // setup configuration if (!Config.Initialize(InitialCurrentDirectory, GamePath)) { return((int)ErrorCodes.Error_Arguments); } switch (MainCommand.ToLowerInvariant()) { case "validate": // check to see if iTunes is installed string dllPath = ""; if (Environment.OSVersion.Platform == PlatformID.MacOSX || Environment.OSVersion.Platform == PlatformID.Unix) { ProcessStartInfo StartInfo = new ProcessStartInfo("/usr/bin/xcode-select", "--print-path"); StartInfo.UseShellExecute = false; StartInfo.RedirectStandardOutput = true; StartInfo.CreateNoWindow = true; using (Process LocalProcess = Process.Start(StartInfo)) { StreamReader OutputReader = LocalProcess.StandardOutput; // trim off any extraneous new lines, helpful for those one-line outputs dllPath = OutputReader.ReadToEnd().Trim(); } } else { dllPath = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "iTunesMobileDeviceDLL", null) as string; if (String.IsNullOrEmpty(dllPath) || !File.Exists(dllPath)) { dllPath = Microsoft.Win32.Registry.GetValue("HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Apple Inc.\\Apple Mobile Device Support\\Shared", "MobileDeviceDLL", null) as string; } } if (String.IsNullOrEmpty(dllPath) || (!File.Exists(dllPath) && !Directory.Exists(dllPath))) { Error("iTunes Not Found!!", (int)ErrorCodes.Error_SDKNotFound); } else { // validate there is a useable provision and cert MobileProvision Provision; X509Certificate2 Cert; bool bHasOverrides; bool bNameMatch; bool foundPlist = CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bHasOverrides, out bNameMatch); if (!foundPlist) { Error("Could not find a valid plist file!!", (int)ErrorCodes.Error_InfoPListNotFound); } else if (!Config.bAutomaticSigning) { if (Provision == null && Cert == null) { Error("No Provision or cert found!!", (int)ErrorCodes.Error_ProvisionAndCertificateNotFound); } else if (Provision == null) { Error("No Provision found!!", (int)ErrorCodes.Error_ProvisionNotFound); } else if (Cert == null) { Error("No Signing Certificate found!!", (int)ErrorCodes.Error_CertificateNotFound); } } else { if (Config.TeamID == null) { Error("No TeamID for automatic signing!!", (int)ErrorCodes.Error_ProvisionNotFound); } } } break; case "packageapp": if (CheckArguments()) { if (Config.bCreateStubSet) { Error("packageapp cannot be used with the -createstub switch"); Program.ReturnCode = (int)ErrorCodes.Error_Arguments; } else { // Create the .app on the Mac CompileTime.CreateApplicationDirOnMac(); } } break; case "repackagefromstage": if (CheckArguments()) { if (Config.bCreateStubSet) { Error("repackagefromstage cannot be used with the -createstub switches"); Program.ReturnCode = (int)ErrorCodes.Error_Arguments; } else { bool bProbablyCreatedStub = Utilities.GetEnvironmentVariable("ue.IOSCreateStubIPA", true); if (!bProbablyCreatedStub) { Warning("ue.IOSCreateStubIPA is currently FALSE, which means you may be repackaging with an out of date stub IPA!"); } CookTime.RepackageIPAFromStub(); } } break; // this is the "super fast just move executable" mode for quick programmer iteration case "dangerouslyfast": if (CheckArguments()) { CompileTime.DangerouslyFastMode(); } break; case "packageipa": if (CheckArguments()) { CompileTime.PackageIPAOnMac(); } break; case "install": GameName = ""; if (Config.bProvision) { ToolsHub.TryInstallingMobileProvision(Config.Provision, false); } if (Config.bCert) { ToolsHub.TryInstallingCertificate_PromptForKey(Config.Certificate, false); } CodeSignatureBuilder.FindCertificates(); CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName); break; case "certificates": { CodeSignatureBuilder.FindCertificates(); CodeSignatureBuilder.FindProvisions(Config.OverrideBundleName); } break; case "resigntool": RunInVisualMode(delegate { return(new GraphicalResignTool()); }); break; case "certrequest": RunInVisualMode(delegate { return(new GenerateSigningRequestDialog()); }); break; case "gui": RunInVisualMode(delegate { return(ToolsHub.CreateShowingTools()); }); break; case "devices": ListDevices(); break; case "signing_match": { MobileProvision Provision; X509Certificate2 Cert; bool bNameMatch; bool bHasOverrideFile; if (CodeSignatureBuilder.FindRequiredFiles(out Provision, out Cert, out bHasOverrideFile, out bNameMatch) && Cert != null) { // print out the provision and cert name Program.LogVerbose("CERTIFICATE-{0},PROVISION-{1}", Cert.FriendlyName, Path.GetFileName(Provision.FileName)); } else { Program.LogVerbose("No matching Signing Data found!"); } } break; default: // Commands by themself default to packaging for the device if (CheckArguments()) { ExecuteCommand(MainCommand, MainRPCCommand); } break; } } catch (Exception Ex) { Error("Application exception: " + Ex.ToString()); if (ReturnCode == 0) { Program.ReturnCode = (int)ErrorCodes.Error_Unknown; } } finally { if (DeploymentHelper.DeploymentServerProcess != null) { DeploymentHelper.DeploymentServerProcess.Close(); } } Environment.ExitCode = ReturnCode; return(ReturnCode); }
/// <summary> /// Finds all valid installed certificates /// </summary> public static void FindCertificates() { string[] ValidCertificatePrefixes = { "iPhone Developer", "iPhone Distribution", "Apple Development", "Apple Distribution" }; X509Certificate2Collection FoundCerts = new X509Certificate2Collection(); if (Environment.OSVersion.Platform == PlatformID.Unix || Environment.OSVersion.Platform == PlatformID.MacOSX) { foreach (string SearchPrefix in ValidCertificatePrefixes) { // run certtool y to get the currently installed certificates CertToolData = ""; Process CertTool = new Process(); CertTool.StartInfo.FileName = "/usr/bin/security"; CertTool.StartInfo.UseShellExecute = false; CertTool.StartInfo.Arguments = string.Format("find-certificate -a -c \"{0}\" -p", SearchPrefix); CertTool.StartInfo.RedirectStandardOutput = true; CertTool.OutputDataReceived += new DataReceivedEventHandler(OutputReceivedCertToolProcessCall); CertTool.Start(); CertTool.BeginOutputReadLine(); CertTool.WaitForExit(); if (CertTool.ExitCode == 0) { string header = "-----BEGIN CERTIFICATE-----\n"; string footer = "-----END CERTIFICATE-----"; int start = CertToolData.IndexOf(header); while (start != -1) { start += header.Length; int end = CertToolData.IndexOf(footer, start); string base64 = CertToolData.Substring(start, (end - start)); byte[] certData = Convert.FromBase64String(base64); X509Certificate2 cert = new X509Certificate2(certData); FoundCerts.Add(cert); start = CertToolData.IndexOf(header, start); } } } } else { // Open the personal certificate store on this machine X509Store Store = new X509Store(); Store.Open(OpenFlags.ReadOnly); foreach (string SearchPrefix in ValidCertificatePrefixes) { FoundCerts.AddRange(Store.Certificates.Find(X509FindType.FindBySubjectName, SearchPrefix, false)); } Store.Close(); } foreach (X509Certificate2 TestCert in FoundCerts) { DateTime EffectiveDate = TestCert.NotBefore.ToUniversalTime(); DateTime ExpirationDate = TestCert.NotAfter.ToUniversalTime(); DateTime Now = DateTime.UtcNow; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); Program.LogVerbose("CERTIFICATE-Name:{0},Validity:{1},StartDate:{2},EndDate:{3}", CryptoAdapter.GetFriendlyNameFromCert(TestCert), bCertTimeIsValid ? "VALID" : "EXPIRED", EffectiveDate.ToString("o"), ExpirationDate.ToString("o")); } }
/// <summary> /// Finds all valid installed provisions /// </summary> public static void FindProvisions(string CFBundleIdentifier) { if (!Directory.Exists(Config.ProvisionDirectory)) { Program.Error("Could not find provision directory '{0}'.", Config.ProvisionDirectory); Program.ReturnCode = (int)ErrorCodes.Error_ProvisionNotFound; return; } // cache the provision library string SelectedProvision = ""; string SelectedCert = ""; string SelectedFile = ""; int FoundName = -1; Dictionary <string, MobileProvision> ProvisionLibrary = new Dictionary <string, MobileProvision>(); foreach (string ProvisionFile in Directory.EnumerateFiles(Config.ProvisionDirectory, "*.mobileprovision")) { MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionFile); ProvisionLibrary.Add(ProvisionFile, Provision); } // first sort all profiles so we look at newer ones first. IEnumerable <string> ProfileKeys = ProvisionLibrary.Select(KV => KV.Key) .OrderByDescending(K => ProvisionLibrary[K].CreationDate) .ToArray(); // note - all of this is a near duplicate of code in MobileProvisionUtilities, which other functions // in this class call to do similar work! // @todo - unify all of this. foreach (string ProvisionFile in ProfileKeys) { MobileProvision p = ProvisionLibrary[ProvisionFile]; DateTime EffectiveDate = p.CreationDate; DateTime ExpirationDate = p.ExpirationDate; DateTime Now = DateTime.UtcNow; bool bCertTimeIsValid = (EffectiveDate < Now) && (ExpirationDate > Now); bool bValid = false; X509Certificate2 Cert = FindCertificate(p); if (Cert != null) { bValid = (Cert.NotBefore.ToUniversalTime() < Now) && (Cert.NotAfter.ToUniversalTime() > Now); } bool bPassesNameCheck = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1) == CFBundleIdentifier; bool bPassesCompanyCheck = false; bool bPassesWildCardCheck = false; if (p.ApplicationIdentifier.Contains("*")) { string CompanyName = p.ApplicationIdentifier.Substring(p.ApplicationIdentifierPrefix.Length + 1); if (CompanyName != "*") { CompanyName = CompanyName.Substring(0, CompanyName.LastIndexOf(".")); bPassesCompanyCheck = CFBundleIdentifier.StartsWith(CompanyName); } else { bPassesWildCardCheck = true; } } bool bIsManaged = false; if (p.ProvisionName == "iOS Team Provisioning Profile: " + CFBundleIdentifier) { bIsManaged = true; } bool bDistribution = ((p.ProvisionedDeviceIDs.Count == 0) && !p.bDebug); string Validity = "VALID"; if (!bCertTimeIsValid) { Validity = "EXPIRED"; } else if (!bValid) { Validity = "NO_CERT"; } else if (!bPassesNameCheck && !bPassesWildCardCheck && !bPassesCompanyCheck) { Validity = "NO_MATCH"; } if (bIsManaged) { Validity = "MANAGED"; } if ((string.IsNullOrWhiteSpace(SelectedProvision) || FoundName < 2) && Validity == "VALID" && !bDistribution) { int Prev = FoundName; if (bPassesNameCheck) { FoundName = 2; } else if (bPassesCompanyCheck && FoundName < 1) { FoundName = 1; } else if (bPassesWildCardCheck && FoundName == -1) { FoundName = 0; } if (FoundName != Prev) { SelectedProvision = p.ProvisionName; SelectedFile = Path.GetFileName(ProvisionFile); SelectedCert = CryptoAdapter.GetFriendlyNameFromCert(Cert); } } Program.LogVerbose("PROVISION-File:{0},Name:{1},Validity:{2},StartDate:{3},EndDate:{4},Type:{5}", Path.GetFileName(ProvisionFile), p.ProvisionName, Validity, EffectiveDate.ToString(), ExpirationDate.ToString(), bDistribution ? "DISTRIBUTION" : "DEVELOPMENT"); } Program.LogVerbose("MATCHED-Provision:{0},File:{1},Cert:{2}", SelectedProvision, SelectedFile, SelectedCert); }