Conveyor 19: Azure Trusted Signing

Conveyor 19 adds support for Microsoft’s new certificate authority, and with it comes some nice reductions in cloud code signing costs. We also simplified the macOS update user experience.

Azure Trusted Signing

Azure Trusted Signing (ATS) is a service from Microsoft that simplifies code signing in the cloud. It’s conceptually similar to other cloud signing services: you pay the service provider to generate a private key for you in their hardware security modules, they verify your identity and then issue a certificate for that key. To sign a file you upload a hash of it to them and they return the signature.

ATS can verify not only companies, but also individuals. This makes it a (relatively) cheap way to ship software without needing to set up a company.

Normally using ATS requires Windows, but in Conveyor 19 we’ve added support for using it from any OS. It’s integrated into the usual build experience and easy to set up. All you have to do is log in using the az command line tool and then add the relevant information to your config. You an also pass an API token directly if you prefer.

Getting to the point where you have a signed certificate ready to use is unfortunately a little more involved. Microsoft has documented the steps here. Unfortunately they recently restricted availability to companies in the US and Canada that are at least three years old. That constraint is intended to be temporary, but if you can obtain a certificate from them:

  1. Sign up for Azure. You will need an Entra ID and then an Azure subscription (this doesn’t imply any monthly fees by itself).
  2. Go to Subscription > Resource Providers and enter Microsoft.CodeSigning. Click the ... and then click Register
  3. Search for Trusted Signing Accounts and create an account (this is distinct from your Azure account).
  4. Make sure your regular Azure account has been assigned the Trusted Signing Identity Verifier role.
  5. Create a new identity verification object in the Trusted Signing section. Use “Public” verification. Go through the ID verification process.
  6. You now have the right names to put in the Conveyor config so the signing key can be found.

Make sure you preserve the Conveyor cache between builds if using CI, because like all cloud signing providers Azure charges per signature. Conveyor caches signatures to save you money.

If you want to migrate from another CA to ATS you can do that using Conveyor’s escape hatch mechanism, but it will cause the user to see a brief uninstall/reinstall progress prompt when they next start the app. Unfortunately Windows certificate authorities don’t agree on how to name organizations or people and thus from the operating system’s perspective the change of identity makes it a different application.

Simplified macOS update experience

In Conveyor 19 we customized Sparkle to enable smoother updates when triggered by the Conveyor Control API (i.e. by your app). If an update is available the user will briefly see a download progress window as the patch is downloaded and applied, then the app will immediately update. There is no longer any need for the user to confirm that they want to restart the app or apply the update. If the patch has already been downloaded and applied in the background by the time the API is invoked, the app will restart immediately. This eliminates some redundant button clicks for the case when the user has already indicated a desire to update by interacting with your app in some way.

Note that update prompts generated by Sparkle asynchronously (because e.g. the user focused your app after it was in the background for a while) will still ask the user to update, as it may not be desirable to restart the app at that exact moment.

Ubuntu/Debian targeting

The Linux desktop APIs are less stable than on other platforms. However, sometimes they break an API or ABI in ways that don’t affect your app, in which case it can be convenient to optionally depend on both versions of the library. Conveyor automatically discovers package dependencies based on the shared libraries your app needs, and now it lets you specify more than one Ubuntu or Debian distribution to target. This is especially convenient for navigating the 64-bit time_t transition that Debian and Ubuntu are currently going through if you’re using the JVM. By default, your packages will target Ubuntu 24. To also target Ubuntu 22 add the following to your config file:

app.linux.debian.distributions += {
    name = "jammy"
    mirrors = ["http://archive.ubuntu.com/ubuntu/", "http://ports.ubuntu.com/ubuntu-ports/"]
}

Make sure your app works on this platform, and that’s all it takes to be compatible with both!

.