|
29 | 29 | echo "Removing quarantine xattr" |
30 | 30 | /usr/bin/xattr -dr 'com.apple.quarantine' "$app_to_sign" 2>/dev/null |
31 | 31 |
|
32 | | -entitlements_path="$self_dir/OMCApplet/OMCApplet.entitlements" |
33 | | -entitlements_path_root="$self_dir/OMCApplet.entitlements" |
| 32 | +app_dir=$(/usr/bin/dirname "$app_to_sign") |
| 33 | + |
| 34 | +# Look for entitlements: |
| 35 | +# 1. OMCApplet.entitlements next to the applet being signed |
| 36 | +# 2. First *.entitlements file next to the applet |
| 37 | +# 3. Default fallback in directory next to this script |
| 38 | +entitlements_file="" |
| 39 | +if [ -f "$app_dir/OMCApplet.entitlements" ]; then |
| 40 | + entitlements_file="$app_dir/OMCApplet.entitlements" |
| 41 | +else |
| 42 | + first_ent=$(/bin/ls "$app_dir"/*.entitlements 2>/dev/null | /usr/bin/head -1) |
| 43 | + if [ -n "$first_ent" ] && [ -f "$first_ent" ]; then |
| 44 | + entitlements_file="$first_ent" |
| 45 | + elif [ -f "$self_dir/OMCApplet.entitlements" ]; then |
| 46 | + entitlements_file="$self_dir/OMCApplet.entitlements" |
| 47 | + fi |
| 48 | +fi |
34 | 49 |
|
35 | 50 | entitlements="" |
36 | 51 |
|
| 52 | +is_developer_id="no" |
| 53 | + |
37 | 54 | if test -z "$identity" || test "$identity" = "-"; then |
38 | 55 | identity="-" |
39 | 56 | timestamp="--timestamp=none" |
40 | 57 | sign_options="" |
41 | 58 | else |
42 | | - if [ -f "${entitlements_path}" ]; then |
43 | | - entitlements="--entitlements $entitlements_path" |
44 | | - elif [ -f "${entitlements_path_root}" ]; then |
45 | | - entitlements="--entitlements $entitlements_path_root" |
| 59 | + if [ -n "$entitlements_file" ]; then |
| 60 | + echo "Using entitlements: $entitlements_file" |
| 61 | + entitlements="--entitlements $entitlements_file" |
| 62 | + fi |
| 63 | + |
| 64 | + # Check if this is an Apple-issued Developer ID certificate |
| 65 | + # Developer ID certs have an anchor in Apple's CA chain |
| 66 | + if echo "$identity" | /usr/bin/grep -q "Developer ID"; then |
| 67 | + is_developer_id="yes" |
| 68 | + timestamp="--timestamp" |
| 69 | + sign_options="--options runtime" |
| 70 | + else |
| 71 | + # Self-signed or other non-Apple certs: |
| 72 | + # - No timestamp server (Apple's TSA won't service non-Apple certs) |
| 73 | + # - No hardened runtime (requires Gatekeeper trust) |
| 74 | + echo "" |
| 75 | + echo "NOTE: \"$identity\" does not appear to be an Apple Developer ID certificate." |
| 76 | + echo "The signed app will not pass Gatekeeper and may not launch without" |
| 77 | + echo "manual approval (right-click > Open, or System Settings > Privacy)." |
| 78 | + echo "" |
| 79 | + timestamp="--timestamp=none" |
| 80 | + sign_options="" |
46 | 81 | fi |
47 | | - timestamp="--timestamp" |
48 | | - sign_options="--options runtime" |
49 | 82 | fi |
50 | 83 |
|
51 | 84 | refresh_app() { |
@@ -113,6 +146,29 @@ if test -d "$app_to_sign/Contents/Support"; then |
113 | 146 | sign_executables_in_dir "$app_to_sign/Contents/Support" |
114 | 147 | fi |
115 | 148 |
|
| 149 | +# Sign frameworks in Contents/Frameworks |
| 150 | +if test -d "$app_to_sign/Contents/Frameworks"; then |
| 151 | + # Sign executables inside each framework first (e.g. Support tools) |
| 152 | + for fw in "$app_to_sign/Contents/Frameworks"/*.framework; do |
| 153 | + test -d "$fw" || continue |
| 154 | + sign_executables_in_dir "$fw/Versions/Current/Support" |
| 155 | + done |
| 156 | + |
| 157 | + echo "" |
| 158 | + echo "Signing frameworks in: $app_to_sign/Contents/Frameworks" |
| 159 | + echo "-----------------------------------" |
| 160 | + for fw in "$app_to_sign/Contents/Frameworks"/*.framework; do |
| 161 | + test -d "$fw" || continue |
| 162 | + fw_name=$(/usr/bin/basename "$fw") |
| 163 | + echo "Signing framework: $fw_name" |
| 164 | + /usr/bin/codesign --verbose --force $sign_options $timestamp --sign "$identity" "$fw" |
| 165 | + if test "$?" != "0"; then |
| 166 | + echo "warning: failed to sign $fw_name" |
| 167 | + fi |
| 168 | + done |
| 169 | + echo "-----------------------------------" |
| 170 | +fi |
| 171 | + |
116 | 172 | echo "" |
117 | 173 |
|
118 | 174 | # Finally sign the app bundle itself |
|
129 | 185 | refresh_app "$app_to_sign" |
130 | 186 |
|
131 | 187 | echo "" |
132 | | -echo "Verifying and validating codesigned app:" |
| 188 | +echo "Verifying codesigned app:" |
133 | 189 | echo "-----------------------------------------" |
134 | | -/usr/bin/codesign --verify --display --verbose=4 "$app_to_sign" |
| 190 | +/usr/bin/codesign --verify --display --verbose=4 "$app_to_sign" 2>&1 |
135 | 191 |
|
136 | 192 | if test "$?" = "0"; then |
137 | 193 | echo "-----------------------------------------" |
138 | | - echo "✓ App signature is valid" |
| 194 | + echo "✓ Code signature is valid (integrity check passed)" |
139 | 195 | else |
140 | 196 | echo "-----------------------------------------" |
141 | | - echo "✗ App signature validation failed" |
| 197 | + echo "✗ Code signature validation failed" |
142 | 198 | exit 1 |
143 | 199 | fi |
| 200 | + |
| 201 | +echo "" |
| 202 | +echo "Gatekeeper assessment:" |
| 203 | +echo "-----------------------------------------" |
| 204 | +spctl_output=$(/usr/sbin/spctl --assess --verbose=4 --type execute "$app_to_sign" 2>&1) |
| 205 | +spctl_status=$? |
| 206 | + |
| 207 | +echo "$spctl_output" |
| 208 | +echo "-----------------------------------------" |
| 209 | + |
| 210 | +if test "$spctl_status" = "0"; then |
| 211 | + echo "✓ App is accepted by Gatekeeper" |
| 212 | +elif test "$identity" = "-"; then |
| 213 | + echo "⚠ Ad-hoc signed apps are not accepted by Gatekeeper (expected)" |
| 214 | + echo " The app will run on this Mac" |
| 215 | +elif test "$is_developer_id" = "yes"; then |
| 216 | + # Check if it's just a notarization issue |
| 217 | + if echo "$spctl_output" | /usr/bin/grep -qi "unnotarized"; then |
| 218 | + echo "⚠ App is signed with Developer ID but not notarized" |
| 219 | + echo " The app will run on this Mac. For distribution, notarize with:" |
| 220 | + echo " xcrun notarytool submit <path> --apple-id <ID> --team-id <TEAM>" |
| 221 | + else |
| 222 | + echo "✗ App is rejected by Gatekeeper (unexpected for Developer ID)" |
| 223 | + echo " Check that the certificate is valid and not expired" |
| 224 | + exit 1 |
| 225 | + fi |
| 226 | +else |
| 227 | + echo "⚠ App is rejected by Gatekeeper (self-signed certificate)" |
| 228 | + echo " To launch: right-click the app > Open, or allow it in" |
| 229 | + echo " System Settings > Privacy & Security" |
| 230 | +fi |
0 commit comments