Azure Trusted Signing — EV Code Signing Setup Guide

Purpose: Eliminate the "Windows protected your PC" SmartScreen warning that appears when university students install the Examini desktop app.

Outcome: After completing this guide, the installer will show "Endolit LLC" as the verified publisher and Windows SmartScreen will allow installation without any warning.

Estimated time: 30 minutes of active work + 1–3 business days waiting for Microsoft to verify the organization.

Cost: $9.99 / month (billed by Microsoft via your Azure subscription).


Table of Contents

  1. Prerequisites
  2. Create an Azure Account
  3. Create the Trusted Signing Resource
  4. Submit Organization Verification
  5. Create a Certificate Profile
  6. Create an App Registration for Automated Signing
  7. Assign the Signing Role
  8. Install Required Tools on the Build Machine
  9. Integrate into the Examini Build
  10. Run a Signed Build and Verify
  11. Troubleshooting

1. Prerequisites

Before starting, confirm the following:

Requirement Details
Azure account A Microsoft account (personal or work). Create one free at portal.azure.com if you don't have one.
Credit card Required to activate an Azure subscription. You will only be charged $9.99/month after setup.
Company documents You will need to provide legal business name and registered address for org verification.
Build machine access The developer who runs npm run build:prod needs the credentials from this guide.
Windows build machine Azure Trusted Signing client runs on Windows. The Examini build already requires Windows.

2. Create an Azure Account

Skip this section if you already have an Azure account with an active subscription.

  1. Open a browser and go to: https://portal.azure.com

  2. Click "Start for free" or "Sign in" if you already have a Microsoft account.

  3. Complete the sign-up form:

    • Use your company email address (e.g. yourname@endolit.com)
    • Enter your phone number for identity verification
    • Enter a credit card (required by Microsoft — you will NOT be charged until you use paid services)
  4. After sign-up, you will land on the Azure Portal dashboard.

  5. In the top search bar, type "Subscriptions" and click it.

    • You should see at least one subscription listed (e.g. "Free Trial" or "Pay-As-You-Go").
    • Note the Subscription ID — you will need it later.

Important: If your subscription shows "Disabled" or "Expired", you must activate or upgrade it before proceeding. Click the subscription → "Reactivate".


3. Create the Trusted Signing Resource

This creates the Azure service that will sign your application files.

  1. In the Azure Portal top search bar, type: Trusted Signing

  2. Click "Trusted Signing Accounts" in the results.

  3. Click the "+ Create" button (top left).

  4. Fill in the form:

    Field Value to enter
    Subscription Select your subscription
    Resource group Click "Create new" → enter examini-codesigning → click OK
    Name endolit-signing (or your preferred name, must be globally unique)
    Region East US (recommended — most features available here)
    Pricing tier Basic ($9.99/month)
  5. Click "Review + Create" at the bottom.

  6. Review the details, then click "Create".

  7. Wait for the deployment to complete (usually 30–60 seconds).

    • You will see "Your deployment is complete" when done.
  8. Click "Go to resource" to open your new Trusted Signing account.

Save for later: Copy the resource name (e.g. endolit-signing) — you will need it in Step 9.


4. Submit Organization Verification

Microsoft must verify that Endolit LLC is a real legal entity before issuing a certificate.

  1. Inside your Trusted Signing account, look at the left sidebar menu.

  2. Click "Identity validation" (under the Settings section).

  3. Click "+ Add".

  4. Select validation type: "Organization" and click Next.

  5. Fill in your company details exactly as they appear on your official business registration:

    Field What to enter
    Legal business name Endolit LLC (must match business registration exactly)
    Business registration number Your EIN or company registration number
    Address (line 1) Registered street address
    City Registered city
    State/Province Registered state
    Postal code Registered ZIP code
    Country United States
    Primary email Company email address
    Primary phone Company phone number
  6. Click "Submit".

  7. You will receive an email from Microsoft at the address you provided.

    • Follow the instructions in the email — this usually involves a callback phone verification or uploading business documents.

Wait time: Microsoft typically completes verification within 1–3 business days. You will receive an email confirmation when approved. You cannot proceed to Step 5 until this is complete.


5. Create a Certificate Profile

Once Microsoft confirms your organization is verified, come back and complete this step.

  1. In your Trusted Signing account (Azure Portal → Trusted Signing Accounts → your account).

  2. In the left sidebar, click "Certificate profiles".

  3. Click "+ Add".

  4. Fill in:

    Field Value
    Name examini-cert
    Profile type Public Trust
    Common name Endolit LLC
    Include street address Optional — leave unchecked unless required
  5. Click "Create".

  6. The profile will appear in the list with status "Active".

Save for later: Copy the profile name (e.g. examini-cert) — you will need it in Step 9.


6. Create an App Registration for Automated Signing

This creates a "service account" identity that your build machine will use to sign files automatically without requiring manual login every time.

  1. In the Azure Portal top search bar, type: App registrations and click it.

  2. Click "+ New registration".

  3. Fill in:

    Field Value
    Name examini-build-signing
    Supported account types "Accounts in this organizational directory only"
    Redirect URI Leave blank
  4. Click "Register".

  5. On the app registration overview page, copy and save these two values:

    Application (client) ID:  xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    Directory (tenant) ID:    xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  6. In the left sidebar, click "Certificates & secrets".

  7. Click "+ New client secret".

  8. Fill in:

    • Description: examini-build-key
    • Expires: 24 months (or the longest option available)
  9. Click "Add".

  10. Immediately copy the secret VALUE (the long string in the "Value" column).

    Warning: This value is only shown once. If you navigate away without copying it, you must delete it and create a new one.

    Client Secret Value:  <paste the value here and keep it safe>

Keep these three values safe — they are the credentials your build machine will use:


7. Assign the Signing Role

Now grant your app registration permission to use the certificate profile.

  1. In the Azure Portal, go back to Trusted Signing Accounts → your account (endolit-signing).

  2. In the left sidebar, click "Access control (IAM)".

  3. Click "+ Add""Add role assignment".

  4. In the Role tab, search for: Trusted Signing Certificate Profile Signer

    • Select it from the list.
    • Click "Next".
  5. In the Members tab:

    • Select "User, group, or service principal"
    • Click "+ Select members"
    • Search for: examini-build-signing (the app registration you just created)
    • Click on it to select it
    • Click "Select"
  6. Click "Review + assign""Review + assign" again to confirm.

  7. The role assignment is now active. Your build service account can sign files.


8. Install Required Tools on the Build Machine

These commands are run on the Windows machine where npm run build:prod is executed.

8a. Install the Azure Trusted Signing Client

Open PowerShell as Administrator and run:

winget install Microsoft.TrustedSigningClient

If winget is not available, download the installer from: https://aka.ms/trustedsigning/install

Verify installation:

trusted-signing-client --version

You should see a version number printed (e.g. 1.0.x).

8b. Install the Azure CLI (optional but useful for testing)

winget install Microsoft.AzureCLI

9. Integrate into the Examini Build

These changes are made to the Examini project source code by the developer.

For the developer: All files referenced below are inside the Examini/ folder of the project.

9a. Create the signing script

Create a new file: Examini/sign.js

/**
 * sign.js — Custom signing script for electron-builder.
 * Called automatically by electron-builder for every EXE it produces.
 * Uses Azure Trusted Signing via the trusted-signing-client CLI tool.
 *
 * Required environment variables (set in .env.build):
 *   AZURE_ENDPOINT        — e.g. https://eus.codesigning.azure.net
 *   AZURE_ACCOUNT_NAME    — your Trusted Signing account name
 *   AZURE_PROFILE_NAME    — your certificate profile name
 *   AZURE_CLIENT_ID       — App Registration client ID
 *   AZURE_CLIENT_SECRET   — App Registration client secret
 *   AZURE_TENANT_ID       — Azure Directory tenant ID
 */

const { execSync } = require('child_process');

exports.default = async function sign(config) {
    const filePath = config.path;

    const endpoint     = process.env.AZURE_ENDPOINT;
    const accountName  = process.env.AZURE_ACCOUNT_NAME;
    const profileName  = process.env.AZURE_PROFILE_NAME;
    const clientId     = process.env.AZURE_CLIENT_ID;
    const clientSecret = process.env.AZURE_CLIENT_SECRET;
    const tenantId     = process.env.AZURE_TENANT_ID;

    if (!endpoint || !accountName || !profileName || !clientId || !clientSecret || !tenantId) {
        console.warn('[sign] One or more Azure signing env vars are missing — skipping signing.');
        console.warn('[sign] Set AZURE_ENDPOINT, AZURE_ACCOUNT_NAME, AZURE_PROFILE_NAME,');
        console.warn('[sign] AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID in .env.build');
        return;
    }

    // Pass credentials to the Azure SDK used by trusted-signing-client
    process.env.AZURE_CLIENT_ID     = clientId;
    process.env.AZURE_CLIENT_SECRET = clientSecret;
    process.env.AZURE_TENANT_ID     = tenantId;

    console.log(`[sign] Signing: ${filePath}`);

    execSync(
        `trusted-signing-client sign` +
        ` --endpoint "${endpoint}"` +
        ` --account "${accountName}"` +
        ` --profile "${profileName}"` +
        ` --timestamp-rfc3161 "http://timestamp.acs.microsoft.com"` +
        ` --timestamp-digest sha256` +
        ` --file-digest sha256` +
        ` "${filePath}"`,
        { stdio: 'inherit' }
    );

    console.log('[sign] Successfully signed:', filePath);
};

9b. Update package.json

Open Examini/package.json. Find the "win" section inside "build" and add the "sign" line:

Before:

"win": {
  "target": [
    { "target": "nsis", "arch": ["x64", "ia32"] },
    { "target": "appx", "arch": ["x64", "ia32"] }
  ],
  "icon": "icons/icon.ico"
}

After:

"win": {
  "sign": "./sign.js",
  "signingHashAlgorithms": ["sha256"],
  "target": [
    { "target": "nsis", "arch": ["x64", "ia32"] },
    { "target": "appx", "arch": ["x64", "ia32"] }
  ],
  "icon": "icons/icon.ico"
}

9c. Create / update the .env.build file

Open or create Examini/.env.build and add these lines:

# Azure Trusted Signing credentials
# Get these values from Steps 3, 5, and 6 of the setup guide.

AZURE_ENDPOINT=https://eus.codesigning.azure.net
AZURE_ACCOUNT_NAME=endolit-signing
AZURE_PROFILE_NAME=examini-cert
AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
AZURE_CLIENT_SECRET=your-client-secret-value-here
AZURE_TENANT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

Replace each xxxx placeholder with the actual values collected in Steps 3, 5, and 6.

Security note: .env.build is already listed in .gitignore — it will never be committed to Git. Keep it only on the build machine.

9d. Load .env.build before signing

Open Examini/sign.js and add the following two lines at the very top (before everything else):

const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env.build') });

The final top of sign.js should look like:

const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env.build') });

const { execSync } = require('child_process');

exports.default = async function sign(config) {
    // ... rest of the file unchanged

10. Run a Signed Build and Verify

10a. Build the app

Open PowerShell in the Examini/ folder and run:

npm run build:prod

Watch the output — you should see lines like:

[sign] Signing: dist\win-unpacked\Examini.exe
[sign] Successfully signed: dist\win-unpacked\Examini.exe
[sign] Signing: dist\Examini Setup 1.0.9.exe
[sign] Successfully signed: dist\Examini Setup 1.0.9.exe

10b. Verify the signature

In PowerShell:

Get-AuthenticodeSignature "dist\Examini Setup 1.0.9.exe" | Format-List

Expected output:

SignerCertificate : [Subject: CN=Endolit LLC, ...]
TimeStamperCertificate : [...]
Status             : Valid
StatusMessage      : Signature verified.
Path               : dist\Examini Setup 1.0.9.exe

Alternatively: right-click Examini Setup 1.0.9.exePropertiesDigital Signatures tab. You should see "Endolit LLC" listed as the signer.

10c. Test the installer

Double-click dist\Examini Setup 1.0.9.exe.

Before signing: Blue SmartScreen warning — "Windows protected your PC", Publisher: Unknown.

After signing: ✅ No SmartScreen warning at all, or a standard UAC prompt showing "Endolit LLC".


11. Troubleshooting

"trusted-signing-client is not recognized"

The CLI was not installed or is not in the system PATH.

"AADSTS700016: Application not found"

The AZURE_CLIENT_ID or AZURE_TENANT_ID value is incorrect.

"403 Forbidden" or "Insufficient privileges"

The app registration does not have the signing role.

"Identity validation not approved"

Organization verification is still pending.

Signature shows but SmartScreen still appears

This can happen for the very first few installs after getting a new certificate, while Microsoft's SmartScreen system builds reputation data. It typically clears within a few days and a few hundred installs. It is much less common with Azure Trusted Signing (since Microsoft trusts its own service) but can still occasionally occur on the first release.

Build succeeds but no signing lines in output

The .env.build file is missing or the dotenv lines were not added to sign.js.


Summary Checklist

Use this to track progress:


Document prepared for Endolit LLC — Examini Desktop App v1.0.9 Azure Trusted Signing — $9.99/month via Microsoft Azure portal