1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.isOldWin6 = exports.doSign = exports.getCertificateFromStoreInfo = exports.getCertInfo = exports.sign = exports.getSignVendorPath = void 0;
|
4 | const util_1 = require("builder-util/out/util");
|
5 | const binDownload_1 = require("../binDownload");
|
6 | const appBuilder_1 = require("../util/appBuilder");
|
7 | const bundledTool_1 = require("../util/bundledTool");
|
8 | const fs_extra_1 = require("fs-extra");
|
9 | const os = require("os");
|
10 | const path = require("path");
|
11 | const platformPackager_1 = require("../platformPackager");
|
12 | const flags_1 = require("../util/flags");
|
13 | const vm_1 = require("../vm/vm");
|
14 | function getSignVendorPath() {
|
15 | return (0, binDownload_1.getBin)("winCodeSign");
|
16 | }
|
17 | exports.getSignVendorPath = getSignVendorPath;
|
18 | async function sign(options, packager) {
|
19 | let hashes = options.options.signingHashAlgorithms;
|
20 |
|
21 | if (options.path.endsWith(".msi")) {
|
22 | hashes = [hashes != null && !hashes.includes("sha1") ? "sha256" : "sha1"];
|
23 | }
|
24 | else if (options.path.endsWith(".appx")) {
|
25 | hashes = ["sha256"];
|
26 | }
|
27 | else if (hashes == null) {
|
28 | hashes = ["sha1", "sha256"];
|
29 | }
|
30 | else {
|
31 | hashes = Array.isArray(hashes) ? hashes : [hashes];
|
32 | }
|
33 | const executor = (await (0, platformPackager_1.resolveFunction)(packager.appInfo.type, options.options.sign, "sign")) || doSign;
|
34 | let isNest = false;
|
35 | for (const hash of hashes) {
|
36 | const taskConfiguration = { ...options, hash, isNest };
|
37 | await Promise.resolve(executor({
|
38 | ...taskConfiguration,
|
39 | computeSignToolArgs: isWin => computeSignToolArgs(taskConfiguration, isWin),
|
40 | }, packager));
|
41 | isNest = true;
|
42 | if (taskConfiguration.resultOutputPath != null) {
|
43 | await (0, fs_extra_1.rename)(taskConfiguration.resultOutputPath, options.path);
|
44 | }
|
45 | }
|
46 | return true;
|
47 | }
|
48 | exports.sign = sign;
|
49 | async function getCertInfo(file, password) {
|
50 | let result = null;
|
51 | const errorMessagePrefix = "Cannot extract publisher name from code signing certificate. As workaround, set win.publisherName. Error: ";
|
52 | try {
|
53 | result = await (0, appBuilder_1.executeAppBuilderAsJson)(["certificate-info", "--input", file, "--password", password]);
|
54 | }
|
55 | catch (e) {
|
56 | throw new Error(`${errorMessagePrefix}${e.stack || e}`);
|
57 | }
|
58 | if (result.error != null) {
|
59 |
|
60 | throw new util_1.InvalidConfigurationError(`${errorMessagePrefix}${result.error}`);
|
61 | }
|
62 | return result;
|
63 | }
|
64 | exports.getCertInfo = getCertInfo;
|
65 | async function getCertificateFromStoreInfo(options, vm) {
|
66 | const certificateSubjectName = options.certificateSubjectName;
|
67 | const certificateSha1 = options.certificateSha1 ? options.certificateSha1.toUpperCase() : options.certificateSha1;
|
68 |
|
69 |
|
70 | const rawResult = await vm.exec("powershell.exe", [
|
71 | "-NoProfile",
|
72 | "-NonInteractive",
|
73 | "-Command",
|
74 | "Get-ChildItem -Recurse Cert: -CodeSigningCert | Select-Object -Property Subject,PSParentPath,Thumbprint | ConvertTo-Json -Compress",
|
75 | ]);
|
76 | const certList = rawResult.length === 0 ? [] : (0, util_1.asArray)(JSON.parse(rawResult));
|
77 | for (const certInfo of certList) {
|
78 | if ((certificateSubjectName != null && !certInfo.Subject.includes(certificateSubjectName)) ||
|
79 | (certificateSha1 != null && certInfo.Thumbprint.toUpperCase() !== certificateSha1)) {
|
80 | continue;
|
81 | }
|
82 | const parentPath = certInfo.PSParentPath;
|
83 | const store = parentPath.substring(parentPath.lastIndexOf("\\") + 1);
|
84 | util_1.log.debug({ store, PSParentPath: parentPath }, "auto-detect certificate store");
|
85 |
|
86 | const isLocalMachineStore = parentPath.includes("Certificate::LocalMachine");
|
87 | util_1.log.debug(null, "auto-detect using of LocalMachine store");
|
88 | return {
|
89 | thumbprint: certInfo.Thumbprint,
|
90 | subject: certInfo.Subject,
|
91 | store,
|
92 | isLocalMachineStore,
|
93 | };
|
94 | }
|
95 | throw new Error(`Cannot find certificate ${certificateSubjectName || certificateSha1}, all certs: ${rawResult}`);
|
96 | }
|
97 | exports.getCertificateFromStoreInfo = getCertificateFromStoreInfo;
|
98 | async function doSign(configuration, packager) {
|
99 |
|
100 | const timeout = parseInt(process.env.SIGNTOOL_TIMEOUT, 10) || 10 * 60 * 1000;
|
101 |
|
102 | let args;
|
103 | let env = process.env;
|
104 | let vm;
|
105 | const vmRequired = configuration.path.endsWith(".appx") || !("file" in configuration.cscInfo);
|
106 | const isWin = process.platform === "win32" || vmRequired;
|
107 | const toolInfo = await getToolPath(isWin);
|
108 | const tool = toolInfo.path;
|
109 | if (vmRequired) {
|
110 | vm = await packager.vm.value;
|
111 | args = computeSignToolArgs(configuration, isWin, vm);
|
112 | }
|
113 | else {
|
114 | vm = new vm_1.VmManager();
|
115 | args = configuration.computeSignToolArgs(isWin);
|
116 | if (toolInfo.env != null) {
|
117 | env = toolInfo.env;
|
118 | }
|
119 | }
|
120 | try {
|
121 | await vm.exec(tool, args, { timeout, env });
|
122 | }
|
123 | catch (e) {
|
124 | if (e.message.includes("The file is being used by another process") || e.message.includes("The specified timestamp server either could not be reached")) {
|
125 | util_1.log.warn(`First attempt to code sign failed, another attempt will be made in 15 seconds: ${e.message}`);
|
126 | await new Promise((resolve, reject) => {
|
127 | setTimeout(() => {
|
128 | vm.exec(tool, args, { timeout, env }).then(resolve).catch(reject);
|
129 | }, 15000);
|
130 | });
|
131 | }
|
132 | throw e;
|
133 | }
|
134 | }
|
135 | exports.doSign = doSign;
|
136 |
|
137 | function computeSignToolArgs(options, isWin, vm = new vm_1.VmManager()) {
|
138 | const inputFile = vm.toVmFile(options.path);
|
139 | const outputPath = isWin ? inputFile : getOutputPath(inputFile, options.hash);
|
140 | if (!isWin) {
|
141 | options.resultOutputPath = outputPath;
|
142 | }
|
143 | const args = isWin ? ["sign"] : ["-in", inputFile, "-out", outputPath];
|
144 | if (process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
|
145 | const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";
|
146 | if (isWin) {
|
147 | args.push(options.isNest || options.hash === "sha256" ? "/tr" : "/t", options.isNest || options.hash === "sha256" ? options.options.rfc3161TimeStampServer || "http://timestamp.digicert.com" : timestampingServiceUrl);
|
148 | }
|
149 | else {
|
150 | args.push("-t", timestampingServiceUrl);
|
151 | }
|
152 | }
|
153 | const certificateFile = options.cscInfo.file;
|
154 | if (certificateFile == null) {
|
155 | const cscInfo = options.cscInfo;
|
156 | const subjectName = cscInfo.thumbprint;
|
157 | if (!isWin) {
|
158 | throw new Error(`${subjectName == null ? "certificateSha1" : "certificateSubjectName"} supported only on Windows`);
|
159 | }
|
160 | args.push("/sha1", cscInfo.thumbprint);
|
161 | args.push("/s", cscInfo.store);
|
162 | if (cscInfo.isLocalMachineStore) {
|
163 | args.push("/sm");
|
164 | }
|
165 | }
|
166 | else {
|
167 | const certExtension = path.extname(certificateFile);
|
168 | if (certExtension === ".p12" || certExtension === ".pfx") {
|
169 | args.push(isWin ? "/f" : "-pkcs12", vm.toVmFile(certificateFile));
|
170 | }
|
171 | else {
|
172 | throw new Error(`Please specify pkcs12 (.p12/.pfx) file, ${certificateFile} is not correct`);
|
173 | }
|
174 | }
|
175 | if (!isWin || options.hash !== "sha1") {
|
176 | args.push(isWin ? "/fd" : "-h", options.hash);
|
177 | if (isWin && process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
|
178 | args.push("/td", "sha256");
|
179 | }
|
180 | }
|
181 | if (options.name) {
|
182 | args.push(isWin ? "/d" : "-n", options.name);
|
183 | }
|
184 | if (options.site) {
|
185 | args.push(isWin ? "/du" : "-i", options.site);
|
186 | }
|
187 |
|
188 | if (options.isNest) {
|
189 | args.push(isWin ? "/as" : "-nest");
|
190 | }
|
191 | const password = options.cscInfo == null ? null : options.cscInfo.password;
|
192 | if (password) {
|
193 | args.push(isWin ? "/p" : "-pass", password);
|
194 | }
|
195 | if (options.options.additionalCertificateFile) {
|
196 | args.push(isWin ? "/ac" : "-ac", vm.toVmFile(options.options.additionalCertificateFile));
|
197 | }
|
198 | const httpsProxyFromEnv = process.env.HTTPS_PROXY;
|
199 | if (!isWin && httpsProxyFromEnv != null && httpsProxyFromEnv.length) {
|
200 | args.push("-p", httpsProxyFromEnv);
|
201 | }
|
202 | if (isWin) {
|
203 |
|
204 | args.push("/debug");
|
205 |
|
206 | args.push(inputFile);
|
207 | }
|
208 | return args;
|
209 | }
|
210 | function getOutputPath(inputPath, hash) {
|
211 | const extension = path.extname(inputPath);
|
212 | return path.join(path.dirname(inputPath), `${path.basename(inputPath, extension)}-signed-${hash}${extension}`);
|
213 | }
|
214 |
|
215 | function isOldWin6() {
|
216 | const winVersion = os.release();
|
217 | return winVersion.startsWith("6.") && !winVersion.startsWith("6.3");
|
218 | }
|
219 | exports.isOldWin6 = isOldWin6;
|
220 | function getWinSignTool(vendorPath) {
|
221 |
|
222 | if (isOldWin6()) {
|
223 | return path.join(vendorPath, "windows-6", "signtool.exe");
|
224 | }
|
225 | else {
|
226 | return path.join(vendorPath, "windows-10", process.arch, "signtool.exe");
|
227 | }
|
228 | }
|
229 | async function getToolPath(isWin = process.platform === "win32") {
|
230 | if ((0, flags_1.isUseSystemSigncode)()) {
|
231 | return { path: "osslsigncode" };
|
232 | }
|
233 | const result = process.env.SIGNTOOL_PATH;
|
234 | if (result) {
|
235 | return { path: result };
|
236 | }
|
237 | const vendorPath = await getSignVendorPath();
|
238 | if (isWin) {
|
239 |
|
240 | return { path: getWinSignTool(vendorPath) };
|
241 | }
|
242 | else if (process.platform === "darwin") {
|
243 | const toolDirPath = path.join(vendorPath, process.platform, "10.12");
|
244 | return {
|
245 | path: path.join(toolDirPath, "osslsigncode"),
|
246 | env: (0, bundledTool_1.computeToolEnv)([path.join(toolDirPath, "lib")]),
|
247 | };
|
248 | }
|
249 | else {
|
250 | return { path: path.join(vendorPath, process.platform, "osslsigncode") };
|
251 | }
|
252 | }
|
253 |
|
\ | No newline at end of file |