async Task SubmitInternal(HashMode hashMode, string name, string description, string descriptionUrl, IList <string> files) { logger.LogInformation("Signing OpenVsixSignTool job {0} with {1} files", name, files.Count()); // Dual isn't supported, use sha256 var alg = hashMode == HashMode.Sha1 ? HashAlgorithmName.SHA1 : HashAlgorithmName.SHA256; var config = new RsaSignConfigurationSet { FileDigestAlgorithm = alg, PkcsDigestAlgorithm = alg, SigningCertificate = await keyVaultService.GetCertificateAsync(), Rsa = await keyVaultService.ToRSA() }; try { var tasks = files.Select(file => { telemetryLogger.OnSignFile(file, signToolName); return(Sign(file, config, keyVaultService.CertificateInfo.TimestampUrl, alg)); }); await Task.WhenAll(tasks); } finally { config.Rsa?.Dispose(); } }
void SubmitInternal(HashMode hashMode, string name, string description, string descriptionUrl, IList <string> files) { logger.LogInformation("Signing SignTool job {0} with {1} files", name, files.Count()); var certificate = keyVaultService.GetCertificateAsync().Result; using var rsa = keyVaultService.ToRSA().Result; using var signer = new AuthenticodeKeyVaultSigner(rsa, certificate, HashAlgorithmName.SHA256, new TimeStampConfiguration(keyVaultService.CertificateInfo.TimestampUrl, HashAlgorithmName.SHA256, TimeStampType.RFC3161)); // loop through all of the files here, looking for appx/eappx // mark each as being signed and strip appx Parallel.ForEach(files, (file, state) => { telemetryLogger.OnSignFile(file, signToolName); if (!Sign(signer, file, description, descriptionUrl)) { throw new Exception($"Could not append sign {file}"); } }); }
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(); } }); } }