Example #1
0
        public static DistinguishedName TryParse(string name, bool isForceValidation = true)
        {
            if (name.IsNullOrWhiteSpace())
            {
                return(null);
            }

            var nameParts = DistinguishedNameParser.GetNameParts(name);

            return(isForceValidation && nameParts.None()
                ? null
                : nameParts.None() ? new DistinguishedName(name) : new DistinguishedName(nameParts));
        }
        void SubmitInternal(HashMode hashMode, string name, string description, string descriptionUrl, IList <string> files, string filter)
        {
            logger.LogInformation("Signing Mage job {0} with {1} files", name, files.Count());

            var args = "-a sha256RSA";

            if (!string.IsNullOrWhiteSpace(name))
            {
                args += $@" -n ""{name}""";
            }

            var certificate  = keyVaultService.GetCertificateAsync().Result;
            var timeStampUrl = keyVaultService.CertificateInfo.TimestampUrl;

            using (var rsaPrivateKey = keyVaultService.ToRSA()
                                       .Result)
            {
                // This outer loop is for a .clickonce file
                Parallel.ForEach(files, options, (file, state) =>
                {
                    // We need to be explicit about the order these files are signed in. The data files must be signed first
                    // Then the .manifest file
                    // Then the nested clickonce/vsto file
                    // finally the top-level clickonce/vsto file

                    using (var zip = new TemporaryZipFile(file, filter, logger))
                    {
                        // Look for the data files first - these are .deploy files
                        // we need to rename them, sign, then restore the name

                        var deployFilesToSign = zip.FilteredFilesInDirectory.Where(f => ".deploy".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase))
                                                .ToList();

                        var contentFiles = new List <string>();
                        foreach (var dfile in deployFilesToSign)
                        {
                            // Rename to file without extension
                            var dest = dfile.Replace(".deploy", "");
                            File.Move(dfile, dest);
                            contentFiles.Add(dest);
                        }

                        var filesToSign = contentFiles.ToList(); // copy it since we may add setup.exe

                        var setupExe = zip.FilteredFilesInDirectory.Where(f => ".exe".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase));
                        filesToSign.AddRange(setupExe);

                        // Safe to call Wait here because we're in a Parallel.ForEach()
                        // sign the inner files
                        signToolAggregate.Value.Submit(hashMode, name, description, descriptionUrl, filesToSign, filter).Wait();

                        // rename the rest of the deploy files since signing the manifest will need them
                        var deployFiles = zip.FilesExceptFiltered.Where(f => ".deploy".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase))
                                          .ToList();

                        foreach (var dfile in deployFiles)
                        {
                            // Rename to file without extension
                            var dest = dfile.Replace(".deploy", "");
                            File.Move(dfile, dest);
                            contentFiles.Add(dest);
                        }

                        // at this point contentFiles has all deploy files renamed

                        // Inner files are now signed
                        // now look for the manifest file and sign that

                        var manifestFile = zip.FilteredFilesInDirectory.Single(f => ".manifest".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase));

                        var fileArgs = $@"-update ""{manifestFile}"" {args}";

                        telemetryLogger.OnSignFile(manifestFile, signToolName);
                        if (!Sign(fileArgs, manifestFile, hashMode, rsaPrivateKey, certificate, timeStampUrl))
                        {
                            throw new Exception($"Could not sign {manifestFile}");
                        }

                        // Read the publisher name from the manifest for use below
                        var manifestDoc  = XDocument.Load(manifestFile);
                        var ns           = manifestDoc.Root.GetDefaultNamespace();
                        var publisherEle = manifestDoc.Root.Element(ns + "publisherIdentity");
                        var pubName      = publisherEle.Attribute("name").Value;

                        var publisherParam = "";

                        var dict = DistinguishedNameParser.Parse(pubName);
                        if (dict.TryGetValue("CN", out var cns))
                        {
                            // get the CN. it may be quoted
                            publisherParam = $@"-pub ""{string.Join("+", cns.Select(s => s.Replace("\"", "")))}"" ";
                        }

                        // Now sign the inner vsto/clickonce file
                        // Order by desending length to put the inner one first
                        var clickOnceFilesToSign = zip.FilteredFilesInDirectory
                                                   .Where(f => ".vsto".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase) ||
                                                          ".application".Equals(Path.GetExtension(f), StringComparison.OrdinalIgnoreCase))
                                                   .Select(f => new { file = f, f.Length })
                                                   .OrderByDescending(f => f.Length)
                                                   .Select(f => f.file)
                                                   .ToList();

                        foreach (var f in clickOnceFilesToSign)
                        {
                            fileArgs = $@"-update ""{f}"" {args} -appm ""{manifestFile}"" {publisherParam}";
                            if (!string.IsNullOrWhiteSpace(descriptionUrl))
                            {
                                fileArgs += $@" -SupportURL {descriptionUrl}";
                            }

                            telemetryLogger.OnSignFile(f, signToolName);
                            if (!Sign(fileArgs, f, hashMode, rsaPrivateKey, certificate, timeStampUrl))
                            {
                                throw new Exception($"Could not sign {f}");
                            }
                        }

                        // restore the deploy files
                        foreach (var dfile in contentFiles)
                        {
                            File.Move(dfile, $"{dfile}.deploy");
                        }

                        zip.Save();
                    }
                });
            }
        }