最近使用 Tauri 开发了一个电脑端的应用,但是 MacOS 因为系统安全性原因,如果不进行代码签名,安装时会报“应用程序” 已损坏,无法打开。您应该将它移到废纸篓。。如果不进行软件公证,安装时会报macOS无法验证此App不包含恶意软件

当然前一个问题可以通过执行命令sudo spctl --master-disablesudo xattr -r -d com.apple.quarantine /Applications/xxx.app来解决。第二个问题可以通过在设置里面允许打开“任何来源”的方式来解决。

但是这样的方式对于普通用户,想要通过上述的方式打开软件,还是有一些门槛和解释成本的。所以就想尝试完成软件的代码签名和公证。

前置条件

对 MacOS 软件进行代码签名和公证,需要您先申请加入苹果开发者计划,每年需要 688 元人民币。

开发者可以通过Apple Developer App以及网页注册,注册成为个人/组织均可。

注册完毕,等待审核通过即可。

信息准备

创建签名证书请求

要创建一个新的签名证书,您必须在您的 Mac 电脑上通过钥匙串访问程序生成一个证书签名请求(CSR)文件。

  1. 启动位于 /Applications/Utilities 中的“钥匙串访问”。
  2. 选取“钥匙串访问”>“证书助理”>“从证书颁发机构请求证书”。
  3. 在“证书助理”对话框中,在“用户电子邮件地址”栏位中输入电子邮件地址。
  4. 在“常用名称”栏位中,输入密钥的名称 (例如,Gita Kumar Dev Key)。
  5. 将“CA 电子邮件地址”栏位留空。
  6. 选取“存储到磁盘”,然后点按“继续”。

最终会生成一个扩展名为.certSigningRequest的文件。

创建并下载证书

  1. 在您的苹果开发者账户上,导航到Certificates, Identifiers & Profiles页面。
  2. 点击+号创建一个新的证书。
  3. 选择合适的证书类型(Apple Distribution 用于将应用提交到 App Store, Developer ID Application 用于将应用打包成 dmg 发布到 App Store 之外的地方)。
  4. 上传你的 CSR 文件(即上一步的扩展名为.certSigningRequest的文件),证书就会被创建出来
  5. Certificates, Identifiers & Profiles页面,点击你想要使用的证书,然后点击 Download 按钮。 它会保存一个 .cer 文件,打开后将证书安装到 钥匙串访问 上。

将证书转换为 base64 格式

  1. 打开 钥匙串访问 应用程序,点击 登录类型 中的 我的证书 标签,找到你的证书条目。
  2. 展开条目,右键点击,并选择 导出。
  3. 选择保存证书的 .p12 文件的路径,并为导出的证书设置一个密码。
  4. 在终端运行以下脚本将 .p12 文件转换为 base64:
openssl base64 -in /path/to/certificate.p12 -out certificate-base64.txt
  1. certificate-base64.txt 文件的内容设置为 APPLE_CERTIFICATE 环境变量的值。
  2. 将证书密码设置为 APPLE_CERTIFICATE_PASSWORD 环境变量。

获取并配置 Apple ID 的信息

  1. account.apple.com 上,登录你的 Apple 账户。
  2. 在“登录和安全”部分中,选择“App 专用密码”。
  3. 选择“生成 App 专用密码”,然后按照屏幕上的步骤操作。
  4. 将您的Apple ID 的邮箱设置为APPLE_ID环境变量。
  5. 将您的第 3 步生成的专用密码设置为APPLE_ID_PASSWORD环境变量。
  6. 进入Developer 账户页面,获取您的团队 ID,并将其设置为APPLE_TEAM_ID环境变量。

获取并配置 App Store Connect 信息

  1. 打开App Store Connect 用户和访问页面,并选择“集成”选项卡。
  2. 点击+号添加密钥信息,名字可以自定义,访问权限选择“开发者”,点击“生成”按钮。
  3. 集成页面可以看到“Issuer ID”信息,复制,并将其设置为APPLE_API_ISSUER环境变量。
  4. 在有效的密钥列表中,有一列“密钥ID”数据,复制对应的密钥 ID 信息,并将其设置为APPLE_API_KEY环境变量。
  5. 下载密钥,它只能下载一次,并且只有在页面重新加载后才可见(该按钮显示在新创建密钥的表格行上),下载完毕后打开文件,获取密钥文件内容,并将其设置为APPLE_API_KEY_CONTENT环境变量。
  6. ~/.appstoreconnect/private_keys/AuthKey_xxx.p8设置为APPLE_API_KEY_PATH环境变量(其中xxx你需要自行替换)。

创建Github Action文件

.github/workflows目录下,创建publish.yml文件,内容如下:

name: "publish"

on:
  workflow_dispatch:
  push:
    branches:
      - release

jobs:
  publish-tauri:
    permissions:
      contents: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - platform: "macos-latest" # for Arm based macs (M1 and above).
            args: "--target aarch64-apple-darwin"
          - platform: "macos-latest" # for Intel based macs.
            args: "--target x86_64-apple-darwin"
          - platform: "ubuntu-22.04"
            args: ""
          - platform: "windows-latest"
            args: ""

    runs-on: ${{ matrix.platform }}
    env:
      APPLE_ID: ${{ secrets.APPLE_ID }}
      APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
    steps:
      - uses: actions/checkout@v4

      - name: install dependencies (ubuntu only)
        if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above.
        run: |
          sudo apt-get update
          sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf          

      - name: setup node
        uses: actions/setup-node@v4
        with:
          node-version: lts/*
          cache: "yarn" # Set this to npm, yarn or pnpm.

      - name: install Rust stable
        uses: dtolnay/rust-toolchain@stable # Set this to dtolnay/rust-toolchain@nightly
        with:
          # Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
          targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}

      - name: install sqlx-cli
        run: cargo install sqlx-cli --no-default-features --features sqlite,rustls

      - name: init db
        run: cd src-tauri && sqlx database create && sqlx migrate run && cd ../

      - name: Rust cache
        uses: swatinem/rust-cache@v2
        with:
          workspaces: "./src-tauri -> target"

      - name: install frontend dependencies
        # If you don't have `beforeBuildCommand` configured you may want to build your frontend here too.
        run: yarn install # change this to npm or pnpm depending on which one you use.
      
     - name: Import Apple Developer Certificate
        if: matrix.platform == 'macos-latest'
        env:
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
          security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
          security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
          security find-identity -v -p codesigning build.keychain          
      - name: Create .p8 file
        if: matrix.platform == 'macos-latest'
        run: |
          mkdir -p ~/.appstoreconnect/private_keys
          echo "${{ secrets.APPLE_API_KEY_CONTENT }}" > ~/.appstoreconnect/private_keys/AuthKey_${{ secrets.APPLE_API_KEY }}.p8          
      - name: Verify Certificate
        if: matrix.platform == 'macos-latest'
        run: |
          CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep "Developer ID Application")
          CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
          echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
          echo "Certificate imported."          

      - uses: tauri-apps/tauri-action@v0
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
          APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
          APPLE_API_KEY_PATH: ${{ secrets.APPLE_API_KEY_PATH }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
        with:
          tagName: app-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version.
          releaseName: "App v__VERSION__"
          releaseBody: "See the assets to download this version and install."
          releaseDraft: true
          prerelease: false
          args: ${{ matrix.args }}