Maximilian Fornacon
Maximilian Fornacon

Reputation: 35

“xxx.app” cannot be opened because the developer cannot be verified. even though notarization was successfull

I'm building a MacOS application with Flutter. I'm new to MacOS-Development in general and might need some assistance with all the steps that are necessary to complete before I can successfully publish the application.

The main problem currently is that I get this "“xxx.app” cannot be opened because the developer cannot be verified." dialog, when I want to execute the application.

I want to deliver the app via dmg-File. The "installation" works fine I guess, but the dialog appears when I'm launching the app e.g. from launchpad.

I'm also using sparkle-project for updating the application later on.

My research has resulted in it being an issue with notarization.

I'll try to describe my whole build process as detailed as possible.

First of all I'm using GitHub Actions. For creating the build I'm using the flutter_distributor package. It simplifies the process of packaging and creating the dmg-file.

- name: Build with flutter_distributor
  run: |
    dart pub global activate flutter_distributor
    flutter_distributor release --name prod --jobs macos-dmg

This is the configuration for flutter_distributor:

output: dist/
releases:
  - name: prod
    jobs:
      - name: macos-dmg
        package:
          platform: macos
          target: dmg
          build_args:
            dart-define:
              APP_ENV: dev

Packaging went all good and it resulted in a dmg-image which could be executed. It uses node-appdmg for creation of the dmg-image.

The next step is notarization and stapling:

      - name: "Notarize and Staple Release Build"
        uses: GuillaumeFalourd/notary-tools@v1
        with:
          product_path: "dist/${{ steps.version.outputs.VERSION }}/xxx.dmg"
          apple_id: ${{ secrets.NOTARIZATION_USERNAME }}
          password: ${{ secrets.NOTARIZATION_PASSWORD }}
          team_id: ${{ secrets.NOTARIZATION_TEAMID }}
          staple: true
          xcode_path: '/Applications/Xcode.app'

Notarization failed at this point.

I read in the logs of node-appdmg that it skipped the signing part when creating the dmg-image. Therefore I added the code-sign configuration:

"code-sign": {
  "signing-identity": "Developer ID Application: xxx (***)"
}

After this it signed the dmg but notarization still failed. So I looked into the notarization logs:

{
      "severity": "error",
      "code": null,
      "path": "xxx.dmg/xxx.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate",
      "message": "The binary is not signed with a valid Developer ID certificate.",
      "docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087721",
      "architecture": "x86_64"
    },
    {
      "severity": "error",
      "code": null,
      "path": "xxx.dmg/xxx.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate",
      "message": "The signature does not include a secure timestamp.",
      "docUrl": "https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/resolving_common_notarization_issues#3087733",
      "architecture": "x86_64"
    },

There were more like this that appear to be related to sparkle. So my solution to this was adding --timestamp and --deep to Other Code Signing Flags in XCode.

After this notarization and stapling was successful, but the dialog still appeared when I try to start the application.

What am I missing? Do I need to notarize the app before adding it to the dmg-image aswell?

Upvotes: 2

Views: 10740

Answers (1)

Hadi
Hadi

Reputation: 1373

If you are exporting your app from Xcode you won't see this issue, but since you want to automate this process, you must also sign the Sparkle framework manually with your Developer ID Application Certificate.

Here is a ruby function that does that:

def self.sign_sparkle_framework(exported_app_path, application_cert_name)
    sparkle_auto_update_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/AutoUpdate"
    codesign('Signing Sparkle AutoUpdate...', application_cert_name, sparkle_auto_update_path)

    sparkle_updater_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/Updater.app"
    codesign('Signing Sparkle Updater...', application_cert_name, sparkle_updater_path)

    sparkle_installer_xpc_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/XPCServices/Installer.xpc/Contents/MacOS/Installer"
    codesign('Signing Sparkle Installer XPC Service...', application_cert_name, sparkle_installer_xpc_path)

    sparkle_downloader_xpc_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework/XPCServices/Downloader.xpc/Contents/MacOS/Downloader"
    codesign('Signing Sparkle Downloader XPC Service...', application_cert_name, sparkle_downloader_xpc_path)

    sparkle_framework_path = "#{exported_app_path}/Contents/Frameworks/Sparkle.framework"
    codesign('Signing Sparkle framework...', application_cert_name, sparkle_framework_path)
end

def self.codesign(title, application_cert_name, path)
    puts_if_verbose title
    execute_command("codesign -f -o runtime --timestamp -s \"#{application_cert_name}\" \"#{path}\"")
end

I have implemented an open-source command line tool that automates the whole process of archiving, exporting, notarizing, packaging, and uploading a macOS application outside the App Store using the Sparkle framework. It can be helpful for your case, check it out here.

Upvotes: 0

Related Questions