{"record":{"id":"137016a5755ed609","repo":"beemdevelopment/Aegis","slug":"certificates-cannot-be-null-or-empty","errorCode":null,"errorMessage":"Certificates cannot be null or empty!","messagePattern":"Certificates cannot be null or empty!","errorType":"exception","errorClass":"CertificateException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/info/guardianproject/trustedintents/TrustedIntents.java","lineNumber":231,"sourceCode":"\n    public void checkTrustedSigner(String packageName)\n            throws NameNotFoundException, CertificateException {\n        PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);\n        checkTrustedSigner(packageInfo.signatures);\n    }\n\n    public void checkTrustedSigner(PackageInfo packageInfo)\n            throws NameNotFoundException, CertificateException {\n        checkTrustedSigner(packageInfo.signatures);\n    }\n\n    public void checkTrustedSigner(Signature[] signatures)\n            throws NameNotFoundException, CertificateException {\n        if (signatures == null || signatures.length == 0)\n            throw new CertificateException(\"signatures cannot be null or empty!\");\n        for (int i = 0; i < signatures.length; i++)\n            if (signatures[i] == null || signatures[i].toByteArray().length == 0)\n                throw new CertificateException(\"Certificates cannot be null or empty!\");\n\n        // check whether the APK signer is trusted for all apps\n        for (ApkSignaturePin pin : pinList)\n            if (areSignaturesEqual(signatures, pin.getSignatures()))\n                return; // found a matching trusted APK signer\n\n        throw new CertificateException(\"APK signatures did not match!\");\n    }\n\n    public boolean areSignaturesEqual(Signature[] sigs0, Signature[] sigs1) {\n        // TODO where is Android's implementation of this that I can just call?\n        if (sigs0 == null || sigs1 == null)\n            return false;\n        if (sigs0.length == 0 || sigs1.length == 0)\n            return false;\n        if (sigs0.length != sigs1.length)\n            return false;\n        for (int i = 0; i < sigs0.length; i++)","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/beemdevelopment/Aegis/blob/d6f4e5925a97e4e91593f1542085eae03432a759/app/src/main/java/info/guardianproject/trustedintents/TrustedIntents.java#L213-L249","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before\nSignature sig = new Signature(certHex.getBytes()); // wrong: encodes hex text, may be empty\ntrustedIntents.checkTrustedSigner(new Signature[]{ sig });\n// after\nbyte[] certBytes = Hex.decodeHex(certHex.toCharArray()); // actually decode the hex\nif (certBytes == null || certBytes.length == 0) {\n    throw new IllegalArgumentException(\"cert data empty\");\n}\ntrustedIntents.checkTrustedSigner(new Signature[]{ new Signature(certBytes) });","handlingStrategy":"type-guard","validationCode":"boolean allValid = signatures != null;\nif (allValid) {\n    for (Signature s : signatures) {\n        if (s == null || s.toByteArray().length == 0) { allValid = false; break; }\n    }\n}\nif (!allValid) { reFetchSignaturesFromPackageManager(); }","typeGuard":"Signature[] validSignaturesOnly(Signature[] sigs) {\n    if (sigs == null) return new Signature[0];\n    return java.util.Arrays.stream(sigs)\n        .filter(s -> s != null && s.toByteArray().length > 0)\n        .toArray(Signature[]::new);\n}","tryCatchPattern":"try {\n    trustedIntents.checkTrustedSigner(signatures);\n} catch (CertificateException e) {\n    if (e.getMessage().contains(\"Certificates cannot be null\")) {\n        Log.e(TAG, \"malformed signature element; re-fetch from PackageManager\");\n    }\n}","preventionTips":["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"],"tags":["android","signatures","security","certificates"],"backgroundTag":"empty-required-field","analyzedSha":"d6f4e5925a97e4e91593f1542085eae03432a759","analyzedAt":"2026-09-08T00:46:31.111Z","contentChangedAt":"2026-09-08T00:46:31.111Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}