windowsCodeSign.js 11.4 KB
"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getSignVendorPath = getSignVendorPath;
exports.sign = sign;
exports.getCertInfo = getCertInfo;
exports.getCertificateFromStoreInfo = getCertificateFromStoreInfo;
exports.doSign = doSign;
exports.isOldWin6 = isOldWin6;

function _util() {
  const data = require("builder-util/out/util");

  _util = function () {
    return data;
  };

  return data;
}

function _binDownload() {
  const data = require("../binDownload");

  _binDownload = function () {
    return data;
  };

  return data;
}

function _appBuilder() {
  const data = require("../util/appBuilder");

  _appBuilder = function () {
    return data;
  };

  return data;
}

function _bundledTool() {
  const data = require("../util/bundledTool");

  _bundledTool = function () {
    return data;
  };

  return data;
}

function _fsExtra() {
  const data = require("fs-extra");

  _fsExtra = function () {
    return data;
  };

  return data;
}

function os() {
  const data = _interopRequireWildcard(require("os"));

  os = function () {
    return data;
  };

  return data;
}

var path = _interopRequireWildcard(require("path"));

function _platformPackager() {
  const data = require("../platformPackager");

  _platformPackager = function () {
    return data;
  };

  return data;
}

function _flags() {
  const data = require("../util/flags");

  _flags = function () {
    return data;
  };

  return data;
}

function _vm() {
  const data = require("../vm/vm");

  _vm = function () {
    return data;
  };

  return data;
}

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

function getSignVendorPath() {
  return (0, _binDownload().getBin)("winCodeSign");
}

async function sign(options, packager) {
  let hashes = options.options.signingHashAlgorithms; // msi does not support dual-signing

  if (options.path.endsWith(".msi")) {
    hashes = [hashes != null && !hashes.includes("sha1") ? "sha256" : "sha1"];
  } else if (options.path.endsWith(".appx")) {
    hashes = ["sha256"];
  } else if (hashes == null) {
    hashes = ["sha1", "sha256"];
  } else {
    hashes = Array.isArray(hashes) ? hashes : [hashes];
  }

  const executor = (0, _platformPackager().resolveFunction)(options.options.sign, "sign") || doSign;
  let isNest = false;

  for (const hash of hashes) {
    const taskConfiguration = { ...options,
      hash,
      isNest
    };
    await executor({ ...taskConfiguration,
      computeSignToolArgs: isWin => computeSignToolArgs(taskConfiguration, isWin)
    }, packager);
    isNest = true;

    if (taskConfiguration.resultOutputPath != null) {
      await (0, _fsExtra().rename)(taskConfiguration.resultOutputPath, options.path);
    }
  }
}

async function getCertInfo(file, password) {
  let result = null;
  const errorMessagePrefix = "Cannot extract publisher name from code signing certificate. As workaround, set win.publisherName. Error: ";

  try {
    result = await (0, _appBuilder().executeAppBuilderAsJson)(["certificate-info", "--input", file, "--password", password]);
  } catch (e) {
    throw new Error(`${errorMessagePrefix}${e.stack || e}`);
  }

  if (result.error != null) {
    // noinspection ExceptionCaughtLocallyJS
    throw new (_util().InvalidConfigurationError)(`${errorMessagePrefix}${result.error}`);
  }

  return result;
}

async function getCertificateFromStoreInfo(options, vm) {
  const certificateSubjectName = options.certificateSubjectName;
  const certificateSha1 = options.certificateSha1 ? options.certificateSha1.toUpperCase() : options.certificateSha1; // ExcludeProperty doesn't work, so, we cannot exclude RawData, it is ok
  // powershell can return object if the only item

  const rawResult = await vm.exec("powershell.exe", ["Get-ChildItem -Recurse Cert: -CodeSigningCert | Select-Object -Property Subject,PSParentPath,Thumbprint | ConvertTo-Json -Compress"]);
  const certList = rawResult.length === 0 ? [] : (0, _util().asArray)(JSON.parse(rawResult));

  for (const certInfo of certList) {
    if (certificateSubjectName != null && !certInfo.Subject.includes(certificateSubjectName) || certificateSha1 != null && certInfo.Thumbprint.toUpperCase() !== certificateSha1) {
      continue;
    }

    const parentPath = certInfo.PSParentPath;
    const store = parentPath.substring(parentPath.lastIndexOf("\\") + 1);

    _util().log.debug({
      store,
      PSParentPath: parentPath
    }, "auto-detect certificate store"); // https://github.com/electron-userland/electron-builder/issues/1717


    const isLocalMachineStore = parentPath.includes("Certificate::LocalMachine");

    _util().log.debug(null, "auto-detect using of LocalMachine store");

    return {
      thumbprint: certInfo.Thumbprint,
      subject: certInfo.Subject,
      store,
      isLocalMachineStore
    };
  }

  throw new Error(`Cannot find certificate ${certificateSubjectName || certificateSha1}, all certs: ${rawResult}`);
}

async function doSign(configuration, packager) {
  // https://github.com/electron-userland/electron-builder/pull/1944
  const timeout = parseInt(process.env.SIGNTOOL_TIMEOUT, 10) || 10 * 60 * 1000;
  let tool;
  let args;
  let env = process.env;
  let vm;

  if (configuration.path.endsWith(".appx") || !("file" in configuration.cscInfo)
  /* certificateSubjectName and other such options */
  ) {
      vm = await packager.vm.value;
      tool = getWinSignTool(await getSignVendorPath());
      args = computeSignToolArgs(configuration, true, vm);
    } else {
    vm = new (_vm().VmManager)();
    const toolInfo = await getToolPath();
    tool = toolInfo.path;
    args = configuration.computeSignToolArgs(process.platform === "win32");

    if (toolInfo.env != null) {
      env = toolInfo.env;
    }
  }

  try {
    await vm.exec(tool, args, {
      timeout,
      env
    });
  } catch (e) {
    if (e.message.includes("The file is being used by another process") || e.message.includes("The specified timestamp server either could not be reached")) {
      _util().log.warn(`First attempt to code sign failed, another attempt will be made in 15 seconds: ${e.message}`);

      await new Promise((resolve, reject) => {
        setTimeout(() => {
          vm.exec(tool, args, {
            timeout,
            env
          }).then(resolve).catch(reject);
        }, 15000);
      });
    }

    throw e;
  }
} // on windows be aware of http://stackoverflow.com/a/32640183/1910191


function computeSignToolArgs(options, isWin, vm = new (_vm().VmManager)()) {
  const inputFile = vm.toVmFile(options.path);
  const outputPath = isWin ? inputFile : getOutputPath(inputFile, options.hash);

  if (!isWin) {
    options.resultOutputPath = outputPath;
  }

  const args = isWin ? ["sign"] : ["-in", inputFile, "-out", outputPath];

  if (process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
    const timestampingServiceUrl = options.options.timeStampServer || "http://timestamp.digicert.com";

    if (isWin) {
      args.push(options.isNest || options.hash === "sha256" ? "/tr" : "/t", options.isNest || options.hash === "sha256" ? options.options.rfc3161TimeStampServer || "http://timestamp.digicert.com" : timestampingServiceUrl);
    } else {
      args.push("-t", timestampingServiceUrl);
    }
  }

  const certificateFile = options.cscInfo.file;

  if (certificateFile == null) {
    const cscInfo = options.cscInfo;
    const subjectName = cscInfo.thumbprint;

    if (!isWin) {
      throw new Error(`${subjectName == null ? "certificateSha1" : "certificateSubjectName"} supported only on Windows`);
    }

    args.push("/sha1", cscInfo.thumbprint);
    args.push("/s", cscInfo.store);

    if (cscInfo.isLocalMachineStore) {
      args.push("/sm");
    }
  } else {
    const certExtension = path.extname(certificateFile);

    if (certExtension === ".p12" || certExtension === ".pfx") {
      args.push(isWin ? "/f" : "-pkcs12", vm.toVmFile(certificateFile));
    } else {
      throw new Error(`Please specify pkcs12 (.p12/.pfx) file, ${certificateFile} is not correct`);
    }
  }

  if (!isWin || options.hash !== "sha1") {
    args.push(isWin ? "/fd" : "-h", options.hash);

    if (isWin && process.env.ELECTRON_BUILDER_OFFLINE !== "true") {
      args.push("/td", "sha256");
    }
  }

  if (options.name) {
    args.push(isWin ? "/d" : "-n", options.name);
  }

  if (options.site) {
    args.push(isWin ? "/du" : "-i", options.site);
  } // msi does not support dual-signing


  if (options.isNest) {
    args.push(isWin ? "/as" : "-nest");
  }

  const password = options.cscInfo == null ? null : options.cscInfo.password;

  if (password) {
    args.push(isWin ? "/p" : "-pass", password);
  }

  if (options.options.additionalCertificateFile) {
    args.push(isWin ? "/ac" : "-ac", vm.toVmFile(options.options.additionalCertificateFile));
  }

  const httpsProxyFromEnv = process.env.HTTPS_PROXY;

  if (!isWin && httpsProxyFromEnv != null && httpsProxyFromEnv.length) {
    args.push("-p", httpsProxyFromEnv);
  }

  if (isWin) {
    // https://github.com/electron-userland/electron-builder/issues/2875#issuecomment-387233610
    args.push("/debug"); // must be last argument

    args.push(inputFile);
  }

  return args;
}

function getOutputPath(inputPath, hash) {
  const extension = path.extname(inputPath);
  return path.join(path.dirname(inputPath), `${path.basename(inputPath, extension)}-signed-${hash}${extension}`);
}
/** @internal */


function isOldWin6() {
  const winVersion = os().release();
  return winVersion.startsWith("6.") && !winVersion.startsWith("6.3");
}

function getWinSignTool(vendorPath) {
  // use modern signtool on Windows Server 2012 R2 to be able to sign AppX
  if (isOldWin6()) {
    return path.join(vendorPath, "windows-6", "signtool.exe");
  } else {
    return path.join(vendorPath, "windows-10", process.arch, "signtool.exe");
  }
}

async function getToolPath() {
  if ((0, _flags().isUseSystemSigncode)()) {
    return {
      path: "osslsigncode"
    };
  }

  const result = process.env.SIGNTOOL_PATH;

  if (result) {
    return {
      path: result
    };
  }

  const vendorPath = await getSignVendorPath();

  if (process.platform === "win32") {
    // use modern signtool on Windows Server 2012 R2 to be able to sign AppX
    return {
      path: getWinSignTool(vendorPath)
    };
  } else if (process.platform === "darwin") {
    const toolDirPath = path.join(vendorPath, process.platform, "10.12");
    return {
      path: path.join(toolDirPath, "osslsigncode"),
      env: (0, _bundledTool().computeToolEnv)([path.join(toolDirPath, "lib")])
    };
  } else {
    return {
      path: path.join(vendorPath, process.platform, "osslsigncode")
    };
  }
} 
// __ts-babel@6.0.4
//# sourceMappingURL=windowsCodeSign.js.map