/// <summary> /// Create a signed package /// </summary> public AppletPackage CreateSignedSolution(AppletSolution sln) { try { X509Certificate2 signCert = this.GetSigningCert(); if (!signCert.HasPrivateKey) { throw new InvalidOperationException($"You do not have the private key for certificiate {signCert.Subject}"); } // Combine all the manifests sln.Meta.Hash = SHA256.Create().ComputeHash(sln.Include.SelectMany(o => o.Manifest).ToArray()); sln.Meta.PublicKeyToken = signCert.Thumbprint; if (this.m_parms.EmbedCertificate) { sln.PublicKey = signCert.Export(X509ContentType.Cert); } if (!signCert.HasPrivateKey) { throw new SecurityException($"Provided key {this.m_parms.SignKeyFile} has no private key"); } RSACryptoServiceProvider rsa = signCert.PrivateKey as RSACryptoServiceProvider; sln.Meta.Signature = rsa.SignData(sln.Include.SelectMany(o => o.Manifest).ToArray(), CryptoConfig.MapNameToOID("SHA1")); return(sln); } catch (Exception e) { Emit.Message("ERROR", "Error signing package: {0}", e); return(null); } }
/// <summary> /// Install the specified applet solution /// </summary> /// <param name="solution"></param> /// <param name="isUpgrade"></param> /// <returns></returns> public bool Install(AppletSolution solution, bool isUpgrade = false) { this.m_tracer.TraceInfo("Installing solution {0}", solution.Meta); // TODO: Verify package hash / signature if (!this.VerifyPackage(solution)) { throw new SecurityException("Applet failed validation"); } this.m_appletCollection.Add(solution.Meta.Id, new AppletCollection()); this.m_readonlyAppletCollection.Add(solution.Meta.Id, this.m_appletCollection[solution.Meta.Id].AsReadonly()); this.m_readonlyAppletCollection[solution.Meta.Id].CollectionChanged += (o, e) => this.Changed?.Invoke(o, e); // Save the applet var appletDir = this.m_configuration.AppletDirectory; if (!Path.IsPathRooted(appletDir)) { appletDir = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), this.m_configuration.AppletDirectory); } if (!Directory.Exists(appletDir)) { Directory.CreateDirectory(appletDir); } // Install var pakFile = Path.Combine(appletDir, solution.Meta.Id + ".pak"); if (this.m_solutions.Any(o => o.Meta.Id == solution.Meta.Id) && File.Exists(pakFile) && !isUpgrade) { throw new InvalidOperationException($"Cannot replace {solution.Meta} unless upgrade is specifically specified"); } // Unpack items from the solution package and install if needed foreach (var itm in solution.Include.Where(o => o.Manifest != null)) { var installedApplet = this.GetApplet(solution.Meta.Id, itm.Meta.Id); if (installedApplet == null || new Version(installedApplet.Info.Version) < new Version(itm.Meta.Version)) // Installed version is there but is older or is not installed, so we install it { this.m_tracer.TraceInfo("Installing Solution applet {0} v{1}...", itm.Meta.Id, itm.Meta.Version); this.Install(itm, true, solution); } } // Register the pakfile lock (this.m_fileDictionary) if (!this.m_fileDictionary.ContainsKey(solution.Meta.Id + ".sln")) { this.m_fileDictionary.Add(solution.Meta.Id + ".sln", pakFile); } this.m_solutions.Add(solution); return(true); }
/// <summary> /// Package the assets /// </summary> /// <returns></returns> public int Package() { using (FileStream fs = File.OpenRead(this.m_parameters.Output)) this.m_package = AppletSolution.Load(fs); // Package the android APK project if (this.m_parameters.DcdrAssets.Contains("android")) { this.PackageApk(); } // Package the DCG project if (this.m_parameters.DcdrAssets.Contains("gateway")) { this.PackageDcg(); } return(1); }
/// <summary> /// Compose multiple PAK files into a solution /// </summary> public int Compose() { try { AppletManifest mfst = null; using (FileStream fs = File.OpenRead(this.m_parms.Source)) mfst = AppletManifest.Load(fs); var slnPak = mfst.CreatePackage(); AppletSolution sln = new AppletSolution(); sln.Meta = slnPak.Meta; sln.PublicKey = slnPak.PublicKey; sln.Manifest = slnPak.Manifest; if (sln.Meta.Uuid == Guid.Empty) { Emit.Message("WARN", "The package does not carry a UUID! You should add a UUID to your solution manifest"); } sln.Include = new List <AppletPackage>(); foreach (var pfile in sln.Meta.Dependencies.ToArray()) { AppletPackage pkg = null; if (!String.IsNullOrEmpty(pfile.Version)) // specific version { pkg = PackageRepositoryUtil.GetFromAny(pfile.Id, new Version(pfile.Version)); } else if (!String.IsNullOrEmpty(m_parms.Version)) { pkg = PackageRepositoryUtil.GetFromAny(pfile.Id, new Version(m_parms.Version)) ?? PackageRepositoryUtil.GetFromAny(pfile.Id, null); } else { pkg = PackageRepositoryUtil.GetFromAny(pfile.Id, null); } if (pkg == null) { throw new KeyNotFoundException($"Package {pfile.Id} ({pfile.Version ?? m_parms.Version ?? "latest"}) not found"); } else { Emit.Message("INFO", "Including {0} version {1}..", pkg.Meta.Id, pkg.Meta.Version); sln.Meta.Dependencies.RemoveAll(o => o.Id == pkg.Meta.Id); if (this.m_parms.Sign && pkg.Meta.Signature == null) { Emit.Message("WARN", "Package {0} is not signed, but you're signing your package. We'll sign it using your key", pkg.Meta.Id); pkg = new Signer(this.m_parms).CreateSignedPackage(pkg.Unpack()); } sln.Include.Add(pkg); } } // Emit i18n file? if (!String.IsNullOrEmpty(this.m_parms.InternationalizationFile)) { Emit.Message("INFO", $"Writing string manifest to {this.m_parms.InternationalizationFile}"); using (var fs = File.Create(this.m_parms.InternationalizationFile)) using (var tw = new StreamWriter(fs, System.Text.Encoding.UTF8)) { // tx translations var mfsts = sln.Include.Select(o => o.Unpack()).ToList(); var appletStrings = mfsts.SelectMany(o => o.Strings).ToArray(); var stringKeys = appletStrings.SelectMany(o => o.String).Select(o => o.Key).Distinct(); var langs = appletStrings.Select(o => o.Language).Distinct().ToArray(); tw.Write("key,"); tw.WriteLine(String.Join(",", langs)); foreach (var str in stringKeys) { tw.Write($"{str},"); foreach (var lang in langs) { tw.Write($"\"{appletStrings.Where(o => o.Language == lang).SelectMany(s => s.String).FirstOrDefault(o => o.Key == str)?.Value}\","); } tw.WriteLine(); } } } sln.Meta.Hash = SHA256.Create().ComputeHash(sln.Include.SelectMany(o => o.Manifest).ToArray()); // Sign the signature package if (this.m_parms.Sign) { new Signer(this.m_parms).CreateSignedSolution(sln); } // Now save using (FileStream fs = File.Create(this.m_parms.Output ?? Path.ChangeExtension(sln.Meta.Id, ".sln.pak"))) sln.Save(fs); return(0); } catch (System.Exception e) { Emit.Message("ERROR", e.Message); //Console.Error.WriteLine("Cannot compose solution {0}: {1}", this.m_parms.Source, e); return(-1); } }
/// <summary> /// Install a applet solution /// </summary> public bool Install(AppletSolution solution, bool isUpgrade = false) { throw new NotSupportedException(); }
/// <summary> /// Create applet solution /// </summary> public AppletSolutionInfo CreateAppletSolution(AppletSolution solution) { return(this.Client.Post <AppletSolution, AppletSolutionInfo>("AppletSolution", solution)); }
/// <summary> /// Initializes a new instance of the <see cref="AppletSolutionInfo"/> class /// with a specific applet manifest instance. /// </summary> public AppletSolutionInfo(AppletSolution soln, X509Certificate2Info publisher) : base(soln.Meta, publisher) { this.Include = soln.Include.Select(s => new AppletManifestInfo(s.Meta, null)).ToList(); }
/// <summary> /// Performs an installation /// </summary> public bool Install(AppletPackage package, bool isUpgrade, AppletSolution owner) { this.m_tracer.TraceInfo("Installing {0}", package.Meta); var appletScope = owner?.Meta.Id ?? String.Empty; // TODO: Verify package hash / signature if (!this.VerifyPackage(package)) { throw new SecurityException("Applet failed validation"); } else if (!this.m_appletCollection[appletScope].VerifyDependencies(package.Meta)) { this.m_tracer.TraceWarning($"Applet {package.Meta} depends on : [{String.Join(", ", package.Meta.Dependencies.Select(o => o.ToString()))}] which are missing or incompatible"); } // Save the applet var appletDir = this.m_configuration.AppletDirectory; if (!Path.IsPathRooted(appletDir)) { appletDir = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), this.m_configuration.AppletDirectory); } if (owner != null) { appletDir = Path.Combine(appletDir, owner.Meta.Id); } if (!Directory.Exists(appletDir)) { Directory.CreateDirectory(appletDir); } // Install var pakFile = Path.Combine(appletDir, package.Meta.Id + ".pak"); if (this.m_appletCollection[appletScope].Any(o => o.Info.Id == package.Meta.Id) && File.Exists(pakFile) && !isUpgrade) { throw new InvalidOperationException($"Cannot replace {package.Meta} unless upgrade is specifically specified"); } using (var fs = File.Create(pakFile)) { package.Save(fs); } lock (this.m_fileDictionary) if (!this.m_fileDictionary.ContainsKey($"{appletScope}{package.Meta.Id}")) { this.m_fileDictionary.Add($"{appletScope}{package.Meta.Id}", pakFile); } var pkg = package.Unpack(); // remove the package from the collection if this is an upgrade if (isUpgrade) { this.m_appletCollection[appletScope].Remove(pkg); } this.m_appletCollection[appletScope].Add(pkg); // We want to install the templates & protocols into the DB this.m_tracer.TraceInfo("Installing templates..."); // Install templates var idp = ApplicationServiceContext.Current.GetService <ITemplateDefinitionRepositoryService>(); if (idp != null) { foreach (var itm in pkg.Templates) { if (idp.GetTemplateDefinition(itm.Mnemonic) == null) { this.m_tracer.TraceInfo("Installing {0}...", itm.Mnemonic); idp.Insert(new TemplateDefinition() { Oid = itm.Oid, Mnemonic = itm.Mnemonic, Description = itm.Description, Name = itm.Mnemonic }); } } } AppletCollection.ClearCaches(); return(true); }