- name
- winapp-signing
- description
- Sign an existing .msix or .exe and manage code-signing certificates: winapp sign, cert generate/install/info, Azure Trusted Signing. Use when signing with a .pfx, creating or trusting a dev certificate, verifying a signature, or fixing a publisher mismatch. Not for building the MSIX.
**If you can't run `winapp` yourself** (no shell, or the command is denied), give the user the exact `winapp` command(s) for their project instead of only describing the steps.
## When to use
Use this skill when:
- **Generating a development certificate** for local MSIX signing and testing
- **Installing (trusting) a certificate** on a machine so MSIX packages can be installed
- **Signing an MSIX package or executable** for distribution
- **Signing with Azure Trusted Signing** (cloud-managed signing identity) via `winapp az-sign`
## Prerequisites
- winapp CLI installed
- **Administrator access** required for `cert install` (trusting certificates on the machine)
## Key concepts
**Publisher matching:** The publisher in your certificate must exactly match the `Publisher` attribute in `Package.appxmanifest`. Most X.500 distinguished names are supported (e.g., `CN=MyCompany` or `OU=Team, O=Corp, C=US`), but components must be single-valued and comma-separated — multi-valued RDNs (`CN=Foo+OU=Bar`) and backslashes aren't supported because the MSIX manifest publisher cannot represent them. Use `--manifest` when generating to auto-match.
**Dev vs. production certs:** `winapp cert generate` creates self-signed certificates for **local testing only**. For production distribution (Microsoft Store or enterprise), obtain a certificate from a trusted Certificate Authority.
**Default password:** Generated certificates use `password` as the default PFX password. Override with `--password`. That default is publicly known, so a certificate left with it can be used by anyone who obtains the `.pfx` — keep it to builds that stay on your own machines. With `--json`, check `defaultPasswordIsPublic` (always present when a certificate is generated) and the `warnings` array before handing the certificate to anything else. With `--if-exists skip`, an existing file is left untouched and the JSON is only `{"certificatePath": "...", "skipped": true}`.
## Usage
### Generate a development certificate
```powershell
# Auto-infer publisher from Package.appxmanifest in the current directory
winapp cert generate
# Explicitly point to a manifest
winapp cert generate --manifest ./path/to/Package.appxmanifest
# Set publisher manually (when no manifest exists yet)
winapp cert generate --publisher "CN=Contoso, O=Contoso Ltd, C=US"
# Custom output path and password
winapp cert generate --output ./certs/myapp.pfx --password MySecurePassword
# Custom validity period
winapp cert generate --valid-days 730
# Overwrite existing certificate
winapp cert generate --if-exists overwrite
```
Output: `devcert.pfx` (or custom path via `--output`).
### Install (trust) a certificate
```powershell
# Trust the certificate on this machine (requires admin/elevated terminal)
winapp cert install ./devcert.pfx
# Force reinstall even if already trusted
winapp cert install ./devcert.pfx --force
```
This adds the certificate to the local machine's **Trusted Root Certification Authorities** store. Required before double-clicking MSIX packages or running `Add-AppxPackage`.
### Sign a file
```powershell
# Sign an MSIX package
winapp sign ./myapp.msix ./devcert.pfx
# Sign with custom password
winapp sign ./myapp.msix ./devcert.pfx --password MySecurePassword
# Sign with timestamp for production (signature remains valid after cert expires)
winapp sign ./myapp.msix ./production.pfx --timestamp http://timestamp.digicert.com
# Verify a signature (runs the Windows SDK signtool; the signing certificate must be trusted)
winapp tool signtool.exe verify /pa ./myapp.msix
```
### Bundle signing
When packaging multiple architectures into an `.msixbundle`, only the bundle needs to be signed — the signature covers all packages inside. The individual `.msix` slices do not need separate signatures.
Note: The `package` command can sign automatically when you pass `--cert`, so you often don't need `sign` separately.
### Sign with Azure Trusted Signing (cloud signing)
For production-grade signing without managing a PFX file, use `winapp az-sign` to sign with [Azure Trusted Signing](https://learn.microsoft.com/azure/trusted-signing/). The signing identity (certificate) is managed in Azure, so no private key ever lives on the machine.
```powershell
# Interactive: discover subscription, account, and profile (prompts for any not provided)
winapp az-sign ./app.msix
# Fully specified — no prompting (ideal for CI/CD)
winapp az-sign ./app.msix --subscription <sub-id> --resource-group <rg> --account <account> --profile <profile>
# Reuse an existing metadata.json (skips resource discovery and identity selection; authentication may still be interactive)
winapp az-sign ./app.msix --metadata-file ./metadata.json
```
**Authentication:** `az-sign` uses Azure's standard credential chain (`DefaultAzureCredential`). In CI/CD, set `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`, and `AZURE_CLIENT_SECRET` (or GitHub Actions OIDC / managed identity). An existing Azure CLI session (`az login`, including the `azure/login` GitHub Action) is also honored in any environment. Only when no credentials are found *and* the session is interactive will `az-sign` launch `az login` for you.
**Prerequisites:** An Azure Code Signing account and certificate profile (created in the Azure portal after identity validation), plus a role assignment granting your identity the **Code Signing Certificate Profile Signer** role. Signing also requires two machine-wide x64 runtimes that winapp does **not** install for you (it downloads the raw NuGet signing package, not the official client-tools installer): the **x64 .NET 8+ runtime** (the signing library is a managed assembly loaded by `signtool.exe`; winapp's self-contained runtime does not satisfy it) and the **x64 Visual C++ Redistributable** (https://aka.ms/vs/17/release/vc_redist.x64.exe). Also requires **SignTool 10.0.22621.755 or later**. If signing fails while *loading* the dlib (e.g. `0xc000007b` or a missing-DLL error) rather than during authentication, install the missing runtime — most often the VC++ Redistributable.
> **Least-privilege CI:** Auto-discovery (listing subscriptions, resource groups, accounts, and profiles) needs read access at a parent scope. To avoid *every* collection-listing call, pass all four of `--subscription`, `--resource-group`, `--account`, and `--profile`: `az-sign` then validates the account and profile with direct resource reads (a GET on each named resource) instead of enumerating the parent collection, so a principal scoped to just that account and profile is sufficient. Omitting any one of them re-introduces a listing call — for example, leaving out `--subscription` makes `az-sign` list the subscriptions your identity can access — which a narrowly-scoped principal may not be permitted to do. A principal scoped only to a single certificate profile can skip validation entirely by passing a pre-generated `--metadata-file` (which specifies the account endpoint and profile directly).
## Recommended workflow
1. **Generate cert** — `winapp cert generate` (auto-infers publisher from manifest)
2. **Trust cert** (one-time) — `winapp cert install ./devcert.pfx` (run as admin)
3. **Package + sign** — `winapp package ./dist --cert ./devcert.pfx`
4. **Distribute** — share the `.msix`; recipients must also trust the cert, or use a trusted CA cert
## Tips
- Always use `--manifest` (or have `Package.appxmanifest` in the working directory) when generating certs to ensure the publisher matches automatically
- For CI/CD, store the PFX as a secret and pass the password via `--password` rather than using the default
- `winapp cert install` modifies the machine certificate store — it persists across reboots and user sessions
- Use `--timestamp` when signing production builds so the signature survives certificate expiration
- You can also use the shorthand: `winapp package ./dist --generate-cert --install-cert` to do everything in one command
## Related skills
- Need to create a manifest first? See `winapp-manifest` to generate `Package.appxmanifest` with correct publisher info
- Ready to package? See `winapp-package` to create and sign an MSIX in one step
- Having issues? See `winapp-troubleshoot` for common error solutions
## Troubleshooting
| Error | Cause | Solution |
|-------|-------|----------|
| "Publisher mismatch" | Cert publisher ≠ manifest publisher | `winapp cert generate --manifest ./Package.appxmanifest` to re-generate with correct publisher |
| "Access denied" / "elevation required" | `cert install` needs admin | Run your terminal as Administrator |
| "Certificate not trusted" | Cert not installed on machine | `winapp cert install ./devcert.pfx` (admin) |
| "Certificate file already exists" | `devcert.pfx` already present | Use `--if-exists overwrite` or `--if-exists skip` |
| Signature invalid after time passes | No timestamp used during signing | Re-sign with `--timestamp http://timestamp.digicert.com` |
| `az-sign` fails with "No credentials found" | No Azure auth in environment | Run `az login`, or set `AZURE_TENANT_ID`/`AZURE_CLIENT_ID`/`AZURE_CLIENT_SECRET` for CI/CD |
| `az-sign` "No Trusted Signing accounts found" | No account in the subscription/resource group | Create a Trusted Signing account and certificate profile in the Azure portal |
## CLI reference
Run `winapp <command> --help` for current command options, or `winapp --cli-schema` for the complete machine-readable command schema.
Auf GitHub ansehen