public static void TryInstallingMobileProvision(string ProvisionFilename, bool ShowPrompt = true) { if (!String.IsNullOrEmpty(ProvisionFilename) || ShowOpenFileDialog(MobileProvisionFilter, "Choose a mobile provision to install", "mobileprovision", "", ref ChoosingFilesToInstallDirectory, out ProvisionFilename)) { try { // Determine if this is a development or distribution certificate bool bIsDistribution = false; MobileProvision Provision = MobileProvisionParser.ParseFile(ProvisionFilename); bIsDistribution = IsProfileForDistribution(Provision); // use the input filename if the GameName is empty string DestName = string.IsNullOrEmpty(Program.GameName) ? Path.GetFileNameWithoutExtension(ProvisionFilename) : Program.GameName; // Copy the file into the destination location string EffectivePrefix = bIsDistribution ? "Distro_" : Config.SigningPrefix; string DestinationFilename = Path.Combine(Config.ProvisionDirectory, EffectivePrefix + DestName + ".mobileprovision"); DestinationFilename = DestinationFilename.Replace("\\", "/"); if (File.Exists(DestinationFilename)) { MobileProvision OldProvision = MobileProvisionParser.ParseFile(DestinationFilename); string MessagePrompt = String.Format( "{0} already contains a {1} mobile provision file. Do you want to replace the provision '{2}' with '{3}'?", Config.BuildDirectory, bIsDistribution ? "distribution" : "development", OldProvision.ProvisionName, Provision.ProvisionName); if (ShowPrompt && System.Windows.Forms.MessageBox.Show(MessagePrompt, Config.AppDisplayName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } if (DestinationFilename != ProvisionFilename) { FileOperations.DeleteFile(DestinationFilename); } } if (DestinationFilename != ProvisionFilename) { FileOperations.CopyRequiredFile(ProvisionFilename, DestinationFilename); } } catch (Exception ex) { ShowError(String.Format("Encountered an error '{0} while trying to install a mobile provision", ex.Message)); } } }
/// <summary> /// Merges a certificate and private key into a single combined certificate /// </summary> public static X509Certificate2 CombineKeyAndCert(string CertificateFilename, string KeyFilename) { // Load the certificate string CertificatePassword = ""; X509Certificate2 Cert = new X509Certificate2(CertificateFilename, CertificatePassword, X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.Exportable | X509KeyStorageFlags.MachineKeySet); // Make sure we have a useful friendly name string FriendlyName = Cert.FriendlyName; if ((FriendlyName == "") || (FriendlyName == null)) { FriendlyName = GetCommonNameFromCert(Cert); } // Create a PKCS#12 store with both the certificate and the private key in it Pkcs12Store Store = new Pkcs12StoreBuilder().Build(); X509CertificateEntry[] CertChain = new X509CertificateEntry[1]; Org.BouncyCastle.X509.X509Certificate BouncyCert = DotNetUtilities.FromX509Certificate(Cert); CertChain[0] = new X509CertificateEntry(BouncyCert); AsymmetricCipherKeyPair KeyPair = LoadKeyPairFromDiskBouncy(KeyFilename); Store.SetKeyEntry(FriendlyName, new AsymmetricKeyEntry(KeyPair.Private), CertChain); // Verify the public key from the key pair matches the certificate's public key AsymmetricKeyParameter CertPublicKey = BouncyCert.GetPublicKey(); if (!(KeyPair.Public as RsaKeyParameters).Equals(CertPublicKey as RsaKeyParameters)) { throw new InvalidDataException("The key pair provided does not match the certificate. Make sure you provide the same key pair that was used to generate the original certificate signing request"); } // Export the merged cert as a .p12 string TempFileName = Path.GetTempFileName(); string ReexportedPassword = "******"; Stream OutStream = File.OpenWrite(TempFileName); Store.Save(OutStream, ReexportedPassword.ToCharArray(), new Org.BouncyCastle.Security.SecureRandom()); OutStream.Close(); // Load it back in and delete the temporary file X509Certificate2 Result = new X509Certificate2(TempFileName, ReexportedPassword, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.MachineKeySet); FileOperations.DeleteFile(TempFileName); return(Result); }
/// <summary> /// Creates a copy of a source IPA to a working path and opens it up as a Zip for further modifications /// </summary> static private ZipFile CreateWorkingIPA(string SourceIPAPath, string WorkIPAPath) { FileInfo ReferenceInfo = new FileInfo(SourceIPAPath); if (!ReferenceInfo.Exists) { Program.Error(String.Format("Failed to find stub IPA '{0}'", SourceIPAPath)); return(null); } else { Program.Log(String.Format("Loaded stub IPA from '{0}' ...", SourceIPAPath)); } if (Program.GameName == "UE4Game") { WorkIPAPath = Config.RemapIPAPath(".ipa"); } // Make sure there are no stale working copies around FileOperations.DeleteFile(WorkIPAPath); // Create a working copy of the IPA FileOperations.CopyRequiredFile(SourceIPAPath, WorkIPAPath); // Open up the zip file ZipFile Stub = ZipFile.Read(WorkIPAPath); // Do a few quick spot checks to catch problems that may have occurred earlier bool bHasCodeSignature = Stub[Config.AppDirectoryInZIP + "/_CodeSignature/CodeResources"] != null; bool bHasMobileProvision = Stub[Config.AppDirectoryInZIP + "/embedded.mobileprovision"] != null; if (!bHasCodeSignature || !bHasMobileProvision) { Program.Error("Stub IPA does not appear to be signed correctly (missing mobileprovision or CodeResources)"); Program.ReturnCode = (int)ErrorCodes.Error_StubNotSignedCorrectly; } // Set encoding to support unicode filenames Stub.AlternateEncodingUsage = ZipOption.Always; Stub.AlternateEncoding = Encoding.UTF8; return(Stub); }
private void ResignButton_Click(object sender, EventArgs e) { saveFileDialog1.DefaultExt = "ipa"; saveFileDialog1.FileName = ""; saveFileDialog1.Filter = ToolsHub.IpaFilter; saveFileDialog1.Title = "Choose a filename for the re-signed IPA"; string CWD = Directory.GetCurrentDirectory(); bool bDialogSucceeded = (saveFileDialog1.ShowDialog() == DialogResult.OK); Directory.SetCurrentDirectory(CWD); if (bDialogSucceeded) { bool bSavedVerbose = Config.bVerbose; Config.bVerbose = true; Program.ProgressDialog.OnBeginBackgroundWork = delegate { string SrcFilename = IPAFilenameEdit.Text; string DestFilename = saveFileDialog1.FileName; // Delete the target location and copy the source there FileOperations.DeleteFile(DestFilename); FileOperations.CopyRequiredFile(SrcFilename, DestFilename); // Open the file FileOperations.ZipFileSystem FileSystem = new FileOperations.ZipFileSystem(DestFilename); FileSystem.SetCompression(CBCompressModifiedFiles.Checked ? Ionic.Zlib.CompressionLevel.BestCompression : Ionic.Zlib.CompressionLevel.None); // Resign and save the file ResignIPA(FileSystem); FileSystem.Close(); }; Config.bVerbose = bSavedVerbose; Program.ProgressDialog.ShowDialog(); } }