MuntashirAkon/AppManager · error · KeyException

Unsupported ASN.1 tag {tag} encountered. Is this a valid RS

Error message

Unsupported ASN.1 tag {tag} encountered.  Is this a valid RSA key?

What it means

ASN1Parse is a hand-rolled minimal DER/BER walker that only understands two ASN.1 tags: SEQUENCE (0x30) and INTEGER (0x02), which is all a plain RSA key structure should contain. If it meets any other tag — e.g. a BIT STRING, OCTET STRING, or an encoded key with algorithm parameters — it cannot map it to an RSA integer list and throws KeyException. This guards against passing a non-RSA or improperly encoded key to RSA key generation/parsing.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/crypto/ks/KeyStoreUtils.java:341

            int length = b[pos++];
            if ((length & 0x80) != 0) {
                int extLen = 0;
                for (int i = 0; i < (length & 0x7F); i++) {
                    extLen = (extLen << 8) | (b[pos++] & 0xFF);
                }
                length = extLen;
            }
            byte[] contents = new byte[length];
            System.arraycopy(b, pos, contents, 0, length);
            pos += length;

            if (tag == 0x30) {  // sequence
                ASN1Parse(contents, integers);
            } else if (tag == 0x02) {  // Integer
                BigInteger i = new BigInteger(contents);
                integers.add(i);
            } else {
                throw new KeyException("Unsupported ASN.1 tag " + tag + " encountered.  Is this a " + "valid RSA key?");
            }
        }
    }

    private static X509Certificate generateECDSACert(@NonNull PrivateKey privateKey, @NonNull PublicKey publicKey,
                                                     @NonNull String formattedSubject, long expiryDate)
            throws OperatorCreationException, CertificateException {
        Date notBefore = new Date();
        Date notAfter = new Date(expiryDate);
        org.bouncycastle.asn1.x500.X500Name x500Name = new org.bouncycastle.asn1.x500.X500Name(formattedSubject);
        JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder("SHA512withECDSA");
        signerBuilder.setProvider(new BouncyCastleProvider());
        SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded());
        X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(x500Name,
                BigInteger.valueOf(new SecureRandom().nextInt() & Integer.MAX_VALUE), notBefore, notAfter, x500Name, spki);
        return new JcaX509CertificateConverter().getCertificate(v3CertGen.build(signerBuilder.build(privateKey)));
    }

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Verify the key file is actually an RSA key (e.g. `openssl rsa -in key.pem -check`) and convert EC/other types if RSA was intended
  2. Strip PEM armor and decode Base64 correctly before passing raw DER to the parser
  3. Re-export the key from its source in PKCS#1 DER form so the payload only contains SEQUENCE and INTEGER nodes
  4. Inspect the DER structure (e.g. `openssl asn1parse -inform DER -in key.der`) to find the unexpected tag and fix the input
  5. If the source truly cannot be an RSA key, use a key parser that handles the actual key type instead of KeyStoreUtils' RSA-only walker

Example fix

// before
KeyStoreUtils.generatePrivateKey(misreadBytes); // KeyException: Unsupported ASN.1 tag 3
// after
byte[] der = Base64.decode(pem.replaceAll("-----(BEGIN|END) RSA PRIVATE KEY-----", "").trim(), Base64.DEFAULT);
if (isRsaKey(der)) { // check leading OID for rsaEncryption
    KeyStoreUtils.generatePrivateKey(der);
} else {
    throw new IllegalArgumentException("Only RSA private keys are supported here");
}
Defensive patterns

Strategy: validation

Validate before calling

// Verify DER contains only SEQUENCE/INTEGER nodes before parsing
static boolean isPlainRsaDer(byte[] der) {
    if (der == null || der.length == 0 || der[0] != 0x30) return false;
    for (int i = 1; i < der.length; ) {
        int tag = der[i++] & 0xFF;
        if (tag != 0x30 && tag != 0x02) return false;
        int len = der[i++] & 0xFF; // short-form lengths only for this check
        i += len;
    }
    return true;
}

Type guard

static boolean looksLikeRsaPrivateKey(byte[] der) {
    return der != null && der.length > 2 && der[0] == 0x30; // top-level SEQUENCE
}

Try / catch

try {
    KeyStoreUtils.generatePrivateKey(der);
} catch (KeyException e) {
    Log.e(TAG, "Not a parsable RSA key", e);
    throw new IllegalArgumentException("Provide a PKCS#1 RSA DER key", e);
}

Prevention

When it happens

Trigger: Calling generatePrivateKey (or ASN1Parse directly) with a key blob whose DER payload contains a tag other than 0x30 or 0x02 — e.g. an EC key, a PKCS#8/PKCS#1 wrapper with extra fields, a truncated/corrupted file where the parser misreads a length and lands mid-field, or a key with OID/parameter blocks.

Common situations: Developer passes an EC or Ed25519 key where an RSA key is required; key file was Base64-decoded incorrectly (headers not stripped); key file corrupted in transfer; legacy DER produced by a non-standard tool.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/3d46006301409449. Report an issue: GitHub.