beemdevelopment/Aegis · error · CertificateException
Certificates cannot be null or empty!
Error message
Certificates cannot be null or empty!
What it means
checkTrustedSigner also validates each individual Signature element: any null entry or a signature whose byte array is empty triggers CertificateException('Certificates cannot be null or empty!'). This is a per-element guard distinct from the array-level null check, catching malformed signature data coming from PackageManager or a hand-built Signature[] array.
Solutions
- Filter out null/empty Signature entries before calling checkTrustedSigner, or fail fast with your own diagnostic
- Verify how the Signature was built — decode the hex/base64 certificate correctly (Signature(javaCertHex) expects the raw certificate bytes)
- Re-fetch signatures via PackageManager with GET_SIGNATURES / SigningInfo instead of caching stale values
- Compare against the expected certificate bytes and re-pin if the app's signing key changed
Example fix
// before
Signature sig = new Signature(certHex.getBytes()); // wrong: encodes hex text, may be empty
trustedIntents.checkTrustedSigner(new Signature[]{ sig });
// after
byte[] certBytes = Hex.decodeHex(certHex.toCharArray()); // actually decode the hex
if (certBytes == null || certBytes.length == 0) {
throw new IllegalArgumentException("cert data empty");
}
trustedIntents.checkTrustedSigner(new Signature[]{ new Signature(certBytes) }); Defensive patterns
Strategy: type-guard
Validate before calling
boolean allValid = signatures != null;
if (allValid) {
for (Signature s : signatures) {
if (s == null || s.toByteArray().length == 0) { allValid = false; break; }
}
}
if (!allValid) { reFetchSignaturesFromPackageManager(); } Type guard
Signature[] validSignaturesOnly(Signature[] sigs) {
if (sigs == null) return new Signature[0];
return java.util.Arrays.stream(sigs)
.filter(s -> s != null && s.toByteArray().length > 0)
.toArray(Signature[]::new);
} Try / catch
try {
trustedIntents.checkTrustedSigner(signatures);
} catch (CertificateException e) {
if (e.getMessage().contains("Certificates cannot be null")) {
Log.e(TAG, "malformed signature element; re-fetch from PackageManager");
}
} Prevention
- Build Signature objects from properly decoded raw certificate bytes, never encoded hex text
- Re-fetch signatures from PackageManager instead of caching across updates
- Unit-test pin construction against the real certificate dump
When it happens
Trigger: The Signature array contains a null element or a Signature constructed from an empty/invalid byte[] (e.g. Signature(new byte[0]), parsing an empty/corrupt hex string, or a provider returning a placeholder empty signature).
Common situations: Manual Signature construction from wrongly-decoded certificate data (hex/base64 decode failure or truncation); corrupted PackageManager results on some OEM ROMs; tests passing dummy empty signatures.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- signatures cannot be null or empty!
- APK signatures did not match!
- Unable to decode stream to bitmap
- Unable to find pack.json in the root of the ZIP file
- Attempted to write outside of the parent directory
AI-assisted analysis of beemdevelopment/Aegis@d6f4e5925a (2026-09-08).
Data as JSON: /api/errors/137016a5755ed609.
Report an issue: GitHub.
Appendix: source
Thrown at app/src/main/java/info/guardianproject/trustedintents/TrustedIntents.java:231
public void checkTrustedSigner(String packageName)
throws NameNotFoundException, CertificateException {
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
checkTrustedSigner(packageInfo.signatures);
}
public void checkTrustedSigner(PackageInfo packageInfo)
throws NameNotFoundException, CertificateException {
checkTrustedSigner(packageInfo.signatures);
}
public void checkTrustedSigner(Signature[] signatures)
throws NameNotFoundException, CertificateException {
if (signatures == null || signatures.length == 0)
throw new CertificateException("signatures cannot be null or empty!");
for (int i = 0; i < signatures.length; i++)
if (signatures[i] == null || signatures[i].toByteArray().length == 0)
throw new CertificateException("Certificates cannot be null or empty!");
// check whether the APK signer is trusted for all apps
for (ApkSignaturePin pin : pinList)
if (areSignaturesEqual(signatures, pin.getSignatures()))
return; // found a matching trusted APK signer
throw new CertificateException("APK signatures did not match!");
}
public boolean areSignaturesEqual(Signature[] sigs0, Signature[] sigs1) {
// TODO where is Android's implementation of this that I can just call?
if (sigs0 == null || sigs1 == null)
return false;
if (sigs0.length == 0 || sigs1.length == 0)
return false;
if (sigs0.length != sigs1.length)
return false;
for (int i = 0; i < sigs0.length; i++)View on GitHub (pinned to d6f4e5925a)