A pure-Rust library and CLI tool for parsing and signing Android App Bundles (.aab) and Android Packages (.apk).
This repository is a refactored and modernized fork of google/pack, decomposed into a Cargo workspace consisting of two crates:
android-signer-cli: A standalone, statically-compiled command-line tool.android-signer-lib: A reusable Rust library crate for integrating Android signing capabilities into your own applications.
You can download pre-compiled binaries from the Releases page for Windows, macOS, and Linux.
The CLI takes an input archive, a keystore file (.p12 or .pem), and the keystore password, then outputs a signed archive containing the appropriate signatures (v1 for .aab, v2/v3 for .apk).
android-signer-cli --input app-release-unsigned.aab \
--output app-release-signed.aab \
--keystore my-release-key.p12 \
--password my-super-secret-passwordBy default it will look for keystore.p12 and use password as the password.
x86_64-unknown-linux-muslaarch64-unknown-linux-muslx86_64-apple-darwinaarch64-apple-darwinx86_64-pc-windows-msvc
Add android-signer-lib to your Cargo.toml.
[dependencies]
android-signer-lib = "0.1"Then you can use it in your code as follows:
use android_signer_lib::{crypto_keys::Keys, sign_apk_buffer, sign_aab_buffer};
use std::fs;
fn sign_my_app() -> Result<(), Box<dyn std::error::Error>> {
// 1. Load Keys
let p12_bytes = fs::read("my_keystore.p12")?;
let keys = Keys::from_p12(&p12_bytes, "my_password")?;
// 2. Read App Buffer
let mut apk_buf = fs::read("unsigned.apk")?;
let aab_buf = fs::read("unsigned.aab")?;
// 3. Sign Archive (APK uses v2/v3, AAB uses v1)
let signed_apk = sign_apk_buffer(&mut apk_buf, &keys)?;
let signed_aab = sign_aab_buffer(&aab_buf, &keys)?;
// 4. Save
fs::write("signed.apk", signed_apk)?;
fs::write("signed.aab", signed_aab)?;
Ok(())
}Below is an example of how to use the android-signer-cli to sign an .aab file using a PEM-encoded keystore stored in GitHub Secrets.
- name: Download android-signer-cli
run: |
wget [https://github.com/caffeine-soft/android-signer/releases/download/v0.1.1/android-signer-cli-x86_64-unknown-linux-musl](https://github.com/caffeine-soft/android-signer/releases/download/v0.1.1/android-signer-cli-x86_64-unknown-linux-musl) -O android-signer-cli
- name: Grant execute permission for android-signer-cli
run: chmod +x android-signer-cli
- name: Prepare PEM Keystore
run: printf "%s" "${{ secrets.KEYSTORE_PEM_BASE64 }}" | base64 -di > keystore.pem
- name: Sign AAB
run: |
./android-signer-cli \
--input unsigned-aab-${{ needs.prepare.outputs.new_tag }}.aab \
--output gmsl-release-${{ needs.prepare.outputs.new_tag }}.aab \
--keystore keystore.pem
- name: Clean up Keystore
if: always()
run: rm -f keystore.pemThe android-signer-cli expects a "combined" PEM file containing both your certificate and private key.
If you have a .p12 file, convert it using OpenSSL:
# Extract private key
openssl pkcs12 -in cert.p12 -nocerts -nodes -out key.pem
# Extract certificate
openssl pkcs12 -in cert.p12 -clcerts -nokeys -out cert.pem
# Combine
cat cert.pem key.pem > combined.pemEncode the combined.pem file to a string to store it in GitHub Secrets:
macOS/Linux:
base64 -i combined.pem | pbcopyWindows (PowerShell):
[Convert]::ToBase64String([IO.File]::ReadAllBytes("combined.pem")) | clipNavigate to Settings > Secrets and variables > Actions in your repository and add a new secret named KEYSTORE_PEM_BASE64 with the copied string as the value.
- Zero dependencies on the Android SDK, Java, or
apksigner. - Computes APK Signature Scheme v2 and v3 blocks directly.
- Handles both
.apkand.aabfile structures natively.
Apache License 2.0