# zsign: Cross-Platform iOS Re-signing on Linux and Windows

> zsign is a C++11 command-line tool for re-signing iOS packages and modifying app bundles without Xcode. It is intended for developers and CI operators who already control the app, signing credentials, and provisioning profiles.

**zhlynn/zsign** — Fast Cross-Platform iOS Code Signing Tool

- Repository: https://github.com/zhlynn/zsign
- Stars: 1,925 · Forks: 513
- Language: C++
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/zhlynn-zsign

## Why zsign Exists Outside the Xcode Workflow

zsign addresses a specific gap: an iOS artifact has to be signed, or signed again, but the build host is not macOS with Xcode installed. The README describes zsign as a cross-platform `codesign` alternative for iOS 12 and later. It accepts `.ipa` packages, Mach-O binaries, and `.app` bundles, with documented support for macOS, Linux, Windows, Android, and FreeBSD.

The intended users are iOS developers, CI/CD operators, and teams distributing internally controlled IPA packages from Linux or Windows servers. zsign can apply a custom private key, certificate, and provisioning profile, or create an ad-hoc signature without a developer certificate. It can also change bundle metadata, inject or remove dylibs, and handle app extensions. These functions target packaging workflows, not general mobile application development.

There is an important boundary around that positioning. The repository does not provide Apple developer accounts, signing identities, or provisioning profiles. Those remain external inputs. zsign is suitable when the organization is already authorized to use the relevant certificate and profile. The README does not describe an entitlement-grant service or a way to create valid App Store distribution credentials.

Its Linux and Windows support is the main reason to consider it over a workflow tied to Apple command-line tools. A Linux CI runner can invoke one command for signing an IPA, while a Windows operator can build the Visual Studio solution. That is especially relevant for jobs that package an already prepared app rather than compile its Objective-C or Swift sources.

## zsign's IPA and Mach-O Signing Flow

The documented interface shows that zsign treats signing as a command-line packaging operation. The input can be an IPA, an application bundle, or another accepted filesystem target. `-k` supplies a private key or P12 file, `-c` supplies a certificate, and `-m` supplies a provisioning profile. Repeating `-m` assigns profiles to app extensions, while repeating `-l` injects multiple dylibs.

The output path is set with `-o`, and the archive compression level is selected with `-z`. Bundle changes occur through flags such as `-b` for the bundle identifier, `-n` for the display name, `-r` for the version, `-e` for an entitlements file, and `-M` for `MinimumOSVersion`. This indicates a workflow that combines metadata replacement, binary signing, and IPA output rather than a certificate-only command.

Mach-O load commands are a specific part of the implementation surface. zsign can inject dylibs as `LC_LOAD_DYLIB` or, with `-w`, as `LC_LOAD_WEAK_DYLIB` in the terms shown by the option documentation. It can also remove named dylibs with `-D`. When `-P` is used, injection is extended to app extensions under `PlugIns` and `Extensions`; the README explains that extension processes do not inherit the main app's injected dylibs.

Caching affects repeat builds. The README states that a persistent `.zsign_cache` lets subsequent signing operations skip unchanged Mach-O files. The `-f` option disables that behavior by forcing signing without the cache. This makes `-f` relevant when diagnosing a signing problem, although the README does not document a separate command for inspecting or deleting the cache.

## Building zsign on Linux, macOS, and Windows

zsign is source-built rather than documented as a package installed from a package registry. On Ubuntu or Debian, the README requires Git, a C++ compiler, `pkg-config`, and OpenSSL development files. The following commands clone the repository and build it from `build/linux`:

```bash
sudo apt-get install -y git g++ pkg-config libssl-dev
git clone https://github.com/zhlynn/zsign.git
cd zsign/build/linux
make clean && make
```

After a successful build, the command examples invoke the resulting `zsign` executable. The README does not specify its output directory, so it does not establish whether it is placed at the repository root or requires installation through a separate step.

macOS uses Homebrew to install the build prerequisites and a platform-specific Makefile:

```bash
brew install pkg-config openssl
git clone https://github.com/zhlynn/zsign.git
cd zsign/build/macos
make clean && make
```

Windows support is presented as a Visual Studio workflow. Open `build/windows/vs2022/zsign.sln` in Visual Studio 2022 and build the solution. No Visual Studio workload or runtime installation list is documented.

The default Linux build uses vendored zlib and minizip sources in `src/third-party/`. Builds can instead link system libraries with `SYSTEM_MINIZIP=1` for minizip or `SYSTEM_MINIZIP=ng` for minizip-ng:

```bash
make clean && make SYSTEM_MINIZIP=1
make clean && make SYSTEM_MINIZIP=ng
```

These alternatives require the corresponding system package and `pkg-config` metadata. The build depends on `openssl-devel` or `libssl-dev` as specified for the distribution, so replacing OpenSSL with another cryptographic library would require code changes that the README does not document.

## A First zsign Run with an Ad-Hoc Signature

The simplest first use does not require a private key, certificate, or provisioning profile. The `-a` option requests an ad-hoc signature, and `-o` selects the output IPA:

```bash
zsign -a -o output.ipa demo.ipa
```

The expected result is `output.ipa`, as named in the example. This exercises archive input, signing, and output generation without introducing credential files. It is the clearest way to confirm that the executable runs on the host, although the README does not state what app-specific changes, if any, occur to metadata during this operation.

After that baseline works, a normal signing command can add external credentials:

```bash
zsign -k privkey.pem -m dev.prov -o output.ipa -z 9 demo.ipa
```

Here, `privkey.pem` is the private key and `dev.prov` is the provisioning profile. The `-z 9` option selects the maximum documented compression level, from 0 through 9. For a P12 identity, the README also accepts a password through `-p`:

```bash
zsign -k dev.p12 -p 123 -m dev.prov -o output.ipa demo.app
```

The example password is visible because it is part of the documented command. A real workflow should not place a production password directly in shell history or a CI log, but the README does not describe an environment-variable or secret-file option, so it does not establish a supported credential injection mechanism beyond `-p`.

The `-q` option suppresses normal operation output, while `-v` prints the version. Neither option changes signing inputs. Use `-d` when a `.zsign_debug` folder is needed. The README lists debug generation but does not document the contents of that folder.

## Bundle Editing and Extension Boundaries

zsign includes several options that change an app beyond its signature. A single example changes both the bundle identifier and display name:

```bash
zsign -k dev.p12 -p 123 -m dev.prov -b 'com.new.bundle.id' -n 'NewName' -o output.ipa demo.ipa
```

That command edits `Info.plist`-associated identity before producing the IPA. Separate options can replace entitlements, version, minimum OS version, or the primary PNG icon. `-x` extracts metadata and the app icon to a directory, with the feature description naming `metadata.json` and a PNG as outputs.

zsign can also remove parts of the bundle. `-E` removes app extensions from `PlugIns/Extensions`, `-W` removes a watch app, and `-U` removes `UISupportedDevices` from `Info.plist`. These are destructive transformations, not reversible editing commands. The README does not say whether the original IPA is modified in place or whether every mode copies all content into the output archive, so `-o` should be used deliberately.

`-S` is a separate, narrower change. It enables both `UISupportsDocumentBrowser` and `UIFileSharingEnabled`. This is not equivalent to rewriting arbitrary `Info.plist` values, and the README does not document a general property editor.

Extension handling deserves particular attention. Multiple `-m` options provide separate provisioning profiles for extensions, while `-P` injects selected dylibs into both the app and its extensions. The latter changes load paths so that each extension can reference the shared dylib relative to its own executable. Removing extensions with `-E` may simplify packaging, but it also removes executable components and their associated provisioning requirements. That choice belongs only in workflows where removing those features is intentional.

## Certificate Inspection and Wrong-Tool Cases

zsign has a separate certificate-checking path, selected with `-C`. The README says this checks certificate validity and OCSP status for material including IPA files, P12 files, `.mobileprovision`, `.cer`, `.pem`, and Mach-O files. This is useful before signing when an operator needs to inspect the signing identity, but it is not a replacement for deciding whether the identity is appropriate for the target distribution channel.

The README defines `-C` as a check option and describes supported certificate inputs, but it does not provide a dedicated check example or define the output format for validity and OCSP results. Consequently, the exact report fields cannot be promised from the available documentation.

zsign is the wrong tool when the task is to compile an Xcode project, generate a provisioning profile, diagnose an Apple entitlement not accepted by the platform, or manage device installation policy. The `-i` option can invoke `ideviceinstaller` after signing, but that is an integration point rather than a full device-management workflow.

It is also a poor fit for operators who need a supported Apple toolchain with Apple's own diagnostics and signing policy. The README's comparison is functional: zsign avoids Xcode and supports non-macOS hosts. It does not claim binary, policy, or output equivalence with Apple's tools. That distinction should shape adoption decisions.

## zsign Compared with an Xcode-Based Signing Step

The practical alternative is an Xcode-based or macOS-native signing process. Both approaches can sign an existing iOS artifact, but their operational models differ. An Xcode-centered process keeps signing inside Apple's toolchain and requires the host environment to support Xcode and its command-line tools. zsign instead compiles as a C++11 program and accepts signing inputs directly from the command line on Linux, Windows, and the other documented platforms.

That difference matters most for CI runners that are not macOS. zsign can sit after an IPA has been assembled, while an Xcode-based route requires a compatible Apple build environment or a remote execution arrangement. The repository does not claim that zsign compiles app source code, so it complements packaging and signing jobs rather than replacing the complete Xcode build pipeline.

Feature coverage also differs in emphasis. zsign adds a persistent cache, metadata editing, dylib injection and removal, extension-specific profiles, cleanup options, and ad-hoc signing. Those operations make it useful for controlled repackaging. An Apple-native workflow may be preferable when consistency with Xcode's signing commands, platform diagnostics, and distribution setup matters more than non-macOS execution.

The MIT license covers the repository under its `LICENSE` file. That is the project-level licensing information to preserve in source distributions and derivatives. The license does not grant Apple signing authority, and the README does not claim that generated IPAs become distributable merely because zsign is MIT licensed.

## Release Activity and Upgrade Boundaries

The repository is not archived. Its last push was on 2026-08-21, and the release list includes `v1.1.2` on 2026-08-21, `v1.1.1` on 2026-07-16, and `v1.1.0` on 2026-07-15. Those dates show recent project activity in the available release record. They do not establish compatibility guarantees for future iOS releases, certificate changes, or platform toolchains.

Upgrade cost depends on where zsign participates in the pipeline. A simple `zsign -a -o output.ipa demo.ipa` invocation has a small command surface. A production job may also depend on `-k`, `-c`, `-m`, repeated `-m`, `-l`, repeated `-l`, `-P`, `-e`, bundle metadata flags, cache behavior, and quiet output. A version change should therefore be checked against the complete command line and the expected output archive.

The build workflow badge is present in the README, but the repository files do not state the supported compiler matrix, packaged artifacts, upgrade migration notes, or whether release binaries are produced for every advertised platform. The project is source-buildable on the documented systems, which gives operators control over dependencies, but it also means the host must supply a compatible compiler, OpenSSL, and platform build environment.

## Conclusion

Adopt zsign when a controlled Linux, Windows, or similar build environment must produce signed iOS artifacts without Xcode. Do not treat it as a substitute for Apple developer credentials, provisioning authority, or device policy review. Verify first that the selected certificate, profile, entitlements, bundle identifier, and target Mach-O formats match your IPA distribution workflow.

## FAQ

### Can zsign sign an IPA on Linux or Windows without Xcode?

Yes. The README describes Linux and Windows support and states that zsign works without Xcode. Linux builds use `build/linux`, while Windows users open `build/windows/vs2022/zsign.sln` in Visual Studio 2022.

### How do I make zsign re-sign an IPA with a P12 file?

Use `-k` for the P12 file, `-p` for its password, `-m` for a provisioning profile, and `-o` for the output IPA. The documented form is `zsign -k dev.p12 -p 123 -m dev.prov -o output.ipa demo.app`.

### Does zsign support ad-hoc signing and dylib injection?

Yes. `-a` performs an ad-hoc signature, while `-l` injects a dylib during signing. `-w` selects weak injection, `-D` removes a named dylib, and `-P` also injects the dylib into app extensions.

## Sources

- [Issues](https://github.com/zhlynn/zsign/issues)
- [License: MIT](https://github.com/zhlynn/zsign/blob/master/LICENSE)
- [README](https://github.com/zhlynn/zsign/blob/master/README.md)
- [Releases](https://github.com/zhlynn/zsign/releases)
- [zhlynn/zsign on GitHub](https://github.com/zhlynn/zsign)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/zhlynn-zsign
