JBR-5600 Sign macOS binaries using jet-sign

(cherry picked from commit 95ef69df13)
This commit is contained in:
Vladislav Rassokhin
2023-05-01 13:47:09 +02:00
committed by Vitaly Provodin
parent 2983cc9534
commit 70b07f6e4b
5 changed files with 198 additions and 19 deletions

View File

@@ -0,0 +1,47 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
source "$SCRIPT_DIR/jetsign-common.sh" || exit 1
function isMacOsBinary() {
file "$1" | grep -q 'Mach-O'
}
function isSigned() {
codesign --verify "$1" && ! grep -q Signature=adhoc < <(codesign --display --verbose "$1" 2>&1)
}
# last argument is a path to be signed
pathToBeSigned="$(pwd)/${*: -1}"
jetSignArgs=("${@:1:$#-1}")
if [[ ! -f "$pathToBeSigned" ]]; then
echo "$pathToBeSigned is missing or not a file"
exit 1
elif isSigned "$pathToBeSigned" && ! isForced "${jetSignArgs[@]}" ; then
echo "Already signed: $pathToBeSigned"
elif [[ "$JETSIGN_CLIENT" == "null" ]]; then
echo "JetSign client is missing, cannot proceed with signing"
exit 1
elif ! isMacOsBinary "$pathToBeSigned" && [[ "$pathToBeSigned" != *.sit ]] && [[ "$pathToBeSigned" != *.tar.gz ]]; then
echo "$pathToBeSigned won't be signed, assumed not to be a macOS executable"
else
if isMacOsBinary "$pathToBeSigned" && ! isSigned "$pathToBeSigned" ; then
echo "Unsigned macOS binary: $pathToBeSigned"
fi
workDir=$(dirname "$pathToBeSigned")
pathSigned="$workDir/signed/${pathToBeSigned##*/}"
jetSignExtensions=$(jetSignExtensions "${jetSignArgs[@]}")
contentType=$(jetSignContentType "$pathToBeSigned")
(
cd "$workDir" || exit 1
"$JETSIGN_CLIENT" -log-format text -denoted-content-type "$contentType" -extensions "$jetSignExtensions" "$pathToBeSigned"
# SRE-1223 (Codesign removes execute bits in executable files) workaround
chmod "$(stat -f %A "$pathToBeSigned")" "$pathSigned"
if isMacOsBinary "$pathSigned"; then
isSigned "$pathSigned"
fi
rm "$pathToBeSigned"
mv "$pathSigned" "$pathToBeSigned"
rm -rf "$workDir/signed"
)
fi

View File

@@ -0,0 +1,63 @@
#!/bin/bash
set -euo pipefail
function isForced() {
for arg in "$@"; do
if [[ "$arg" == --force ]]; then
return 0
fi
done
return 1
}
function jetSignExtensions() {
args=("$@")
((lastElementIndex=${#args[@]}-1))
for index in "${!args[@]}"; do
arg=${args[$index]}
case "$arg" in
--sign | -s)
echo -n 'mac_codesign_identity='
continue
;;
--entitlements)
echo -n 'mac_codesign_entitlements='
continue
;;
--options=runtime)
echo -n 'mac_codesign_options=runtime'
;;
--force)
echo -n 'mac_codesign_force=true'
;;
--timestamp | --verbose | -v)
continue
;;
*)
echo -n "$arg"
;;
esac
if [[ $index != "$lastElementIndex" ]]; then
echo -n ","
fi
done
}
# See jetbrains.sign.util.FileUtil.contentType
function jetSignContentType() {
case "${1##*/}" in
*.sit)
echo -n 'application/x-mac-app-zip'
;;
*.tar.gz)
echo -n 'application/x-mac-app-targz'
;;
*.pkg)
echo -n 'application/x-mac-pkg'
;;
*)
echo -n 'application/x-mac-app-bin'
;;
esac
}

View File

@@ -0,0 +1,41 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
source "$SCRIPT_DIR/jetsign-common.sh" || exit 1
function isSigned() {
pkgutil --check-signature "$1" && grep -q "signed by a developer certificate" < <(pkgutil --check-signature "$1" 2>&1)
}
# second last argument is a path to be signed
pathToBeSigned="$(pwd)/${*:(-2):1}"
# last argument is a path to signed file
pathOut="$(pwd)/${*:(-1)}"
jetSignArgs=("${@:1:$#-2}")
if [[ ! -f "$pathToBeSigned" ]]; then
echo "$pathToBeSigned is missing or not a file"
exit 1
elif isSigned "$pathToBeSigned" && ! isForced "${jetSignArgs[@]}" ; then
echo "Already signed: $pathToBeSigned"
elif [[ "$JETSIGN_CLIENT" == "null" ]]; then
echo "JetSign client is missing, cannot proceed with signing"
exit 1
elif [[ "$pathToBeSigned" != *.pkg ]]; then
echo "$pathToBeSigned won't be signed, assumed not to be a macOS package"
else
if ! isSigned "$pathToBeSigned" ; then
echo "Unsigned macOS package: $pathToBeSigned"
fi
workDir=$(dirname "$pathToBeSigned")
pathSigned="$workDir/signed/${pathToBeSigned##*/}"
jetSignExtensions=$(jetSignExtensions "${jetSignArgs[@]}")
contentType=$(jetSignContentType "$pathToBeSigned")
(
cd "$workDir" || exit 1
"$JETSIGN_CLIENT" -log-format text -denoted-content-type "$contentType" -extensions "$jetSignExtensions" "$pathToBeSigned"
isSigned "$pathSigned"
rm -f "$pathOut"
mv "$pathSigned" "$pathOut"
rm -rf "$workDir/signed"
)
fi

View File

@@ -9,6 +9,19 @@ BUNDLE_ID=$3
JB_DEVELOPER_CERT=$4
JB_INSTALLER_CERT=$5
SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
# Use JetBrains sign utility if it's available
if [[ "${JETSIGN_CLIENT:=}" == "null" ]] || [[ "$JETSIGN_CLIENT" == "" ]]; then
JB_SIGN=false
SIGN_UTILITY="codesign"
PRODUCTSIGN_UTILITY="productsign"
else
JB_SIGN=true
SIGN_UTILITY="$SCRIPT_DIR/codesign.sh"
PRODUCTSIGN_UTILITY="$SCRIPT_DIR/productsign.sh"
fi
if [[ -z "$APPLICATION_PATH" ]] || [[ -z "$JB_DEVELOPER_CERT" ]]; then
echo "Usage: $0 AppDirectory CertificateID"
exit 1
@@ -37,13 +50,13 @@ for f in \
if [ -d "$APPLICATION_PATH/$f" ]; then
find "$APPLICATION_PATH/$f" \
-type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "*.tbd" -o -name "*.node" -o -perm +111 \) \
-exec codesign --timestamp \
-exec "$SIGN_UTILITY" --timestamp \
-v -s "$JB_DEVELOPER_CERT" --options=runtime --force \
--entitlements entitlements.xml {} \;
--entitlements "$SCRIPT_DIR/entitlements.xml" {} \;
fi
done
log "Signing libraries in jars in $PWD"
log "Signing libraries in jars in $APPLICATION_PATH"
# todo: add set -euo pipefail; into the inner sh -c
# `-e` prevents `grep -q && printf` loginc
@@ -61,10 +74,10 @@ find "$APPLICATION_PATH" -name '*.jar' \
find jarfolder \
-type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "*.tbd" -o -name "jattach" \) \
-exec codesign --timestamp \
-exec "$SIGN_UTILITY" --timestamp \
--force \
-v -s "$JB_DEVELOPER_CERT" --options=runtime \
--entitlements entitlements.xml {} \;
--entitlements "$SCRIPT_DIR/entitlements.xml" {} \;
(cd jarfolder; zip -q -r -o -0 ../jar.jar .)
mv jar.jar "$file"
@@ -73,14 +86,15 @@ find "$APPLICATION_PATH" -name '*.jar' \
rm -rf jarfolder jar.jar
log "Signing other files..."
# shellcheck disable=SC2043
for f in \
"Contents/Home/bin"; do
if [ -d "$APPLICATION_PATH/$f" ]; then
find "$APPLICATION_PATH/$f" \
-type f \( -name "*.jnilib" -o -name "*.dylib" -o -name "*.so" -o -name "*.tbd" -o -perm +111 \) \
-exec codesign --timestamp \
-exec "$SIGN_UTILITY" --timestamp \
-v -s "$JB_DEVELOPER_CERT" --options=runtime --force \
--entitlements entitlements.xml {} \;
--entitlements "$SCRIPT_DIR/entitlements.xml" {} \;
fi
done
@@ -91,12 +105,23 @@ done
# --entitlements entitlements.xml "$APPLICATION_PATH/Contents/MacOS/idea"
log "Signing whole app..."
codesign --timestamp \
-v -s "$JB_DEVELOPER_CERT" --options=runtime \
--force \
--entitlements entitlements.xml "$APPLICATION_PATH"
if [ "$JB_SIGN" = true ]; then
tar -pczvf tmp-to-sign.tar.gz --exclude='man' -C "$(dirname "$APPLICATION_PATH")" "$(basename "$APPLICATION_PATH")"
"$SIGN_UTILITY" --timestamp \
-v -s "$JB_DEVELOPER_CERT" --options=runtime \
--force \
--entitlements "$SCRIPT_DIR/entitlements.xml" tmp-to-sign.tar.gz
rm -rf "$APPLICATION_PATH"
tar -xzvf tmp-to-sign.tar.gz --directory "$(dirname "$APPLICATION_PATH")"
rm -f tmp-to-sign.tar.gz
else
"$SIGN_UTILITY" --timestamp \
-v -s "$JB_DEVELOPER_CERT" --options=runtime \
--force \
--entitlements "$SCRIPT_DIR/entitlements.xml" "$APPLICATION_PATH"
fi
BUILD_NAME=$(echo $APPLICATION_PATH | awk -F"/" '{ print $2 }')
BUILD_NAME="$(basename "$APPLICATION_PATH")"
log "Creating $APP_NAME.pkg..."
rm -rf "$APP_NAME.pkg"
@@ -104,7 +129,8 @@ rm -rf "$APP_NAME.pkg"
mkdir -p unsigned
pkgbuild --identifier $BUNDLE_ID --root $APPLICATION_PATH \
--install-location /Library/Java/JavaVirtualMachines/${BUILD_NAME} unsigned/${APP_NAME}.pkg
productsign --timestamp --sign "$JB_INSTALLER_CERT" unsigned/${APP_NAME}.pkg ${APP_NAME}.pkg
log "Signing $APP_NAME.pkg..."
"$PRODUCTSIGN_UTILITY" --timestamp --sign "$JB_INSTALLER_CERT" unsigned/${APP_NAME}.pkg ${APP_NAME}.pkg
#log "Signing whole app..."
#codesign --timestamp \

View File

@@ -17,7 +17,7 @@ JB_INSTALLER_CERT=$6
NOTARIZE=$7
BUNDLE_ID=$8
cd "$(dirname "$0")"
SCRIPT_DIR="$(cd "$(dirname "$0")" >/dev/null && pwd)"
function log() {
echo "$(date '+[%H:%M:%S]') $*"
@@ -44,7 +44,7 @@ fi
log "$INPUT_FILE extracted and removed"
APP_NAME=$(echo ${INPUT_FILE} | awk -F".tar" '{ print $1 }')
APP_NAME=$(basename "$INPUT_FILE" | awk -F".tar" '{ print $1 }')
APPLICATION_PATH=$EXPLODED/$(ls $EXPLODED)
find "$APPLICATION_PATH/Contents/Home/bin" \
@@ -73,16 +73,18 @@ if [[ $non_plist -gt 0 ]]; then
exit 1
fi
log "Unlocking keychain..."
# Make sure *.p12 is imported into local KeyChain
security unlock-keychain -p "$PASSWORD" "/Users/$USERNAME/Library/Keychains/login.keychain"
if [[ "${JETSIGN_CLIENT:=}" == "null" ]] || [[ "$JETSIGN_CLIENT" == "" ]]; then
log "Unlocking keychain..."
# Make sure *.p12 is imported into local KeyChain
security unlock-keychain -p "$PASSWORD" "/Users/$USERNAME/Library/Keychains/login.keychain"
fi
attempt=1
limit=3
set +e
while [[ $attempt -le $limit ]]; do
log "Signing (attempt $attempt) $APPLICATION_PATH ..."
./sign.sh "$APPLICATION_PATH" "$APP_NAME" "$BUNDLE_ID" "$CODESIGN_STRING" "$JB_INSTALLER_CERT"
"$SCRIPT_DIR/sign.sh" "$APPLICATION_PATH" "$APP_NAME" "$BUNDLE_ID" "$CODESIGN_STRING" "$JB_INSTALLER_CERT"
ec=$?
if [[ $ec -ne 0 ]]; then
((attempt += 1))