Merge pull request #11126 from vector-im/dbkr/windows_signing

Sign main Windows executable
This commit is contained in:
David Baker 2019-10-18 12:02:04 +01:00 committed by GitHub
commit 8e81685a9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 127 additions and 37 deletions

View File

@ -0,0 +1 @@
export OSSLSIGNCODE_SIGNARGS='-pkcs11module /Library/Frameworks/eToken.framework/Versions/Current/libeToken.dylib -pkcs11engine /usr/local/lib/engines/engine_pkcs11.so -certs electron_app/riot.im/New_Vector_Ltd.pem -key 0a3271cbc1ec0fd8afb37f6bbe0cd65ba08d3b4d -t http://timestamp.comodoca.com -h sha256 -verbose'

View File

@ -146,6 +146,7 @@
"postcss-simple-vars": "^4.1.0", "postcss-simple-vars": "^4.1.0",
"postcss-strip-inline-comments": "^0.1.5", "postcss-strip-inline-comments": "^0.1.5",
"rimraf": "^2.4.3", "rimraf": "^2.4.3",
"shell-escape": "^0.2.0",
"source-map-loader": "^0.2.4", "source-map-loader": "^0.2.4",
"webpack": "^4.23.1", "webpack": "^4.23.1",
"webpack-cli": "^3.1.2", "webpack-cli": "^3.1.2",

View File

@ -1,26 +1,30 @@
#!/bin/bash #!/bin/bash
set -e
usage() { usage() {
echo "Usage: $0 -v <version> -c <config file> [-n]" echo "Usage: $0 -v <version> -d <config directory> [-n]"
echo echo
echo "version: commit-ish to check out and build" echo "version: commit-ish to check out and build"
echo "config file: a path to a json config file to" echo "config directory: a path to a directory containing"
echo "ship with the build. In addition, update_base_url:" echo "config.json, a json config file to ship with the build"
echo "from this file is used to set up auto-update." echo "and env.sh, a file to source environment variables"
echo "from."
echo "-n: build with no config file." echo "-n: build with no config file."
echo echo
echo "Values may also be passed as environment variables" echo "The update_base_url value from config.json is used to set up auto-update."
echo
echo "Environment variables:"
echo " OSSLSIGNCODE_SIGNARGS: Arguments to pass to osslsigncode when signing"
echo " NOTARIZE_APPLE_ID: Apple ID to use for notarisation. The password for"
echo " this account must be set in NOTARIZE_CREDS in the keychain."
} }
conffile= confdir=
version= version=
skipcfg=0 skipcfg=0
while getopts "c:v:n" opt; do while getopts "d:v:n" opt; do
case $opt in case $opt in
c) d)
conffile=$OPTARG confdir=$OPTARG
;; ;;
v) v)
version=$OPTARG version=$OPTARG
@ -42,6 +46,8 @@ if [ -z "$version" ]; then
exit exit
fi fi
conffile="$confdir/config.json"
if [ -z "$conffile" ] && [ "$skipcfg" = 0 ]; then if [ -z "$conffile" ] && [ "$skipcfg" = 0 ]; then
echo "No config file given. Use -c to supply a config file or" echo "No config file given. Use -c to supply a config file or"
echo "-n to build with no config file (and no auto update)." echo "-n to build with no config file (and no auto update)."
@ -67,14 +73,31 @@ if [ ! -f package.json ]; then
exit exit
fi fi
[ -f "$confdir/env.sh" ] && . "$confdir/env.sh"
if [ -z "$NOTARIZE_APPLE_ID" ]; then if [ -z "$NOTARIZE_APPLE_ID" ]; then
echo "NOTARIZE_APPLE_ID is not set" echo "NOTARIZE_APPLE_ID is not set"
exit exit
fi fi
osslsigncode -h 2> /dev/null
if [ $? -ne 255 ]; then # osslsigncode exits with 255 after printing usage...
echo "osslsigncode not found"
exit
fi
# Test that altool can get its credentials for notarising the mac app # Test that altool can get its credentials for notarising the mac app
xcrun altool -u "$NOTARIZE_APPLE_ID" -p '@keychain:NOTARIZE_CREDS' --list-apps || exit xcrun altool -u "$NOTARIZE_APPLE_ID" -p '@keychain:NOTARIZE_CREDS' --list-apps || exit
# Get the token password: we'll need it later, but get it now so we fail early if it's not there
token_password=`security find-generic-password -s riot_signing_token -w`
if [ $? -ne 0 ]; then
echo "riot_signing_token not found in keychain"
exit
fi
set -e
echo "Building $version using Update base URL $update_base_url" echo "Building $version using Update base URL $update_base_url"
projdir=`pwd` projdir=`pwd`
@ -115,14 +138,12 @@ mkdir -p "$projdir/electron_app/dist/unsigned/"
mkdir -p "$pubdir/install/macos" mkdir -p "$pubdir/install/macos"
cp $distdir/*.dmg "$pubdir/install/macos/" cp $distdir/*.dmg "$pubdir/install/macos/"
# Windows installers go to the dist dir because they need signing # Windows installers need signing, this comes later
mkdir -p "$pubdir/install/win32/ia32/" mkdir -p "$pubdir/install/win32/ia32/"
mkdir -p "$projdir/electron_app/dist/unsigned/ia32/" mkdir -p "$projdir/electron_app/dist/unsigned/ia32/"
cp $distdir/squirrel-windows-ia32/*.exe "$projdir/electron_app/dist/unsigned/ia32/"
mkdir -p "$pubdir/install/win32/x64/" mkdir -p "$pubdir/install/win32/x64/"
mkdir -p "$projdir/electron_app/dist/unsigned/x64/" mkdir -p "$projdir/electron_app/dist/unsigned/x64/"
cp $distdir/squirrel-windows/*.exe "$projdir/electron_app/dist/unsigned/x64/"
# Packages for auto-update # Packages for auto-update
mkdir -p "$pubdir/update/macos" mkdir -p "$pubdir/update/macos"
@ -144,9 +165,21 @@ cp $distdir/squirrel-windows/RELEASES "$pubdir/update/win32/x64/"
# longer appears to work). # longer appears to work).
cp $distdir/*_amd64.deb "$projdir/electron_app/dist/" cp $distdir/*_amd64.deb "$projdir/electron_app/dist/"
# Now we sign the windows installer executables (as opposed to the main binary which
# is signed in the electron afteSign hook)
echo "Signing Windows installers..."
exe32=( "$distdir"/squirrel-windows-ia32/*.exe )
basename32=`basename "$exe32"`
osslsigncode sign $OSSLSIGNCODE_SIGNARGS -pass "$token_password" -in "$exe32" -out "$projdir/electron_app/pub/install/win32/ia32/$basename32"
exe64=( "$distdir"/squirrel-windows/*.exe )
basename64=`basename "$exe64"`
osslsigncode sign $OSSLSIGNCODE_SIGNARGS -pass "$token_password" -in "$exe64" -out "$projdir/electron_app/pub/install/win32/x64/$basename64"
echo "Installers signed"
rm -rf "$builddir" rm -rf "$builddir"
echo "Unsigned Windows installers have been placed in electron_app/dist/unsigned/ - sign them," echo "$pubdir can now be hosted on your web server."
echo "or just copy them to "$pubdir/install/win32/\<arch\>/""
echo "Once you've done this, $pubdir can be hosted on your web server."
echo "deb archives are in electron_app/dist/ - these should be added into your debian repository" echo "deb archives are in electron_app/dist/ - these should be added into your debian repository"

View File

@ -1,26 +1,76 @@
const { notarize } = require('electron-notarize'); const { notarize } = require('electron-notarize');
const { exec, execFile } = require('child_process');
const fs = require('fs');
const shellescape = require('shell-escape');
exports.default = async function(context) { exports.default = async function(context) {
const { electronPlatformName, appOutDir } = context; const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== 'darwin') {
return; if (electronPlatformName === 'darwin') {
const appName = context.packager.appInfo.productFilename;
// We get the password from keychain. The keychain stores
// user IDs too, but apparently altool can't get the user ID
// from the keychain, so we need to get it from the environment.
const userId = process.env.NOTARIZE_APPLE_ID;
if (userId === undefined) {
throw new Exception("User ID not found. Set NOTARIZE_APPLE_ID.");
}
console.log("Notarising macOS app. This may be some time.");
return await notarize({
appBundleId: 'im.riot.app',
appPath: `${appOutDir}/${appName}.app`,
appleId: userId,
appleIdPassword: '@keychain:NOTARIZE_CREDS',
});
} else if (electronPlatformName === 'win32') {
// This signs the actual Riot executable
const appName = context.packager.appInfo.productFilename;
// get the token passphrase from the keychain
const tokenPassphrase = await new Promise((resolve, reject) => {
execFile(
'security',
['find-generic-password', '-s', 'riot_signing_token', '-w'],
{},
(err, stdout) => {
if (err) {
reject(err);
} else {
resolve(stdout.trim());
}
},
);
});
return new Promise((resolve, reject) => {
let cmdLine = 'osslsigncode sign ';
if (process.env.OSSLSIGNCODE_SIGNARGS) {
cmdLine += process.env.OSSLSIGNCODE_SIGNARGS + ' ';
}
const tmpFile = 'tmp_' + Math.random().toString(36).substring(2, 15) + '.exe';
cmdLine += shellescape([
'-pass', tokenPassphrase,
'-in', `${appOutDir}/${appName}.exe`,
'-out', `${appOutDir}/${tmpFile}`,
]);
const signproc = exec(cmdLine, {}, (error, stdout) => {
console.log(stdout);
});
signproc.on('exit', (code) => {
if (code !== 0) {
reject("osslsigncode failed with code " + code);
return;
}
fs.rename(`${appOutDir}/${tmpFile}`, `${appOutDir}/${appName}.exe`, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
} }
// We get the password from keychain. The keychain stores
// user IDs too, but apparently altool can't get the user ID
// from the keychain, so we need to get it from the environment.
const userId = process.env.NOTARIZE_APPLE_ID;
if (userId === undefined) {
throw new Exception("User ID not found. Set NOTARIZE_APPLE_ID.");
}
const appName = context.packager.appInfo.productFilename;
console.log("Notarising macOS app. This may be some time.");
return await notarize({
appBundleId: 'im.riot.app',
appPath: `${appOutDir}/${appName}.app`,
appleId: userId,
appleIdPassword: '@keychain:NOTARIZE_CREDS',
});
}; };

View File

@ -8239,6 +8239,11 @@ shebang-regex@^1.0.0:
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
shell-escape@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/shell-escape/-/shell-escape-0.2.0.tgz#68fd025eb0490b4f567a027f0bf22480b5f84133"
integrity sha1-aP0CXrBJC09WegJ/C/IkgLX4QTM=
shell-quote@^1.6.1: shell-quote@^1.6.1:
version "1.7.2" version "1.7.2"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2" resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.2.tgz#67a7d02c76c9da24f99d20808fcaded0e0e04be2"