MuntashirAkon/AppManager · error · InvalidKeyException

Stream does not appear to be a properly formatted RSA key.

Error message

Stream does not appear to be a properly formatted RSA key.

What it means

generatePrivateKey recognized PKCS#1 "RSA PRIVATE KEY" format and ASN.1-parsed the bytes, but fewer than 8 integers were recovered (an RSA private key needs version, modulus, public/private exponents, primes, exponents, coefficient), so it throws InvalidKeyException "Stream does not appear to be a properly formatted RSA key."

Source

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

            if (base64EncodedKey.length() == 0) {
                throw new IOException("Stream does not contain an unencrypted private key.");
            }

            BASE64Decoder decoder = new BASE64Decoder();
            byte[] bytes = decoder.decodeBuffer(base64EncodedKey.toString());

            KeyFactory kf;
            KeySpec spec;
            if (pkcs8Format) {
                kf = KeyFactory.getInstance("RSA");
                spec = new PKCS8EncodedKeySpec(bytes);
            } else if (rsaFormat) {
                // PKCS#1 format
                kf = KeyFactory.getInstance("RSA");
                List<BigInteger> rsaIntegers = new ArrayList<>();
                ASN1Parse(bytes, rsaIntegers);
                if (rsaIntegers.size() < 8) {
                    throw new InvalidKeyException("Stream does not appear to be a properly formatted RSA key.");
                }
                BigInteger publicExponent = rsaIntegers.get(2);
                BigInteger privateExponent = rsaIntegers.get(3);
                BigInteger modulus = rsaIntegers.get(1);
                BigInteger primeP = rsaIntegers.get(4);
                BigInteger primeQ = rsaIntegers.get(5);
                BigInteger primeExponentP = rsaIntegers.get(6);
                BigInteger primeExponentQ = rsaIntegers.get(7);
                BigInteger crtCoefficient = rsaIntegers.get(8);
                //spec = new RSAPrivateKeySpec(modulus, privateExponent);
                spec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent,
                        primeP, primeQ, primeExponentP, primeExponentQ, crtCoefficient);
            } else if (dsaFormat) {
                kf = KeyFactory.getInstance("DSA");
                List<BigInteger> dsaIntegers = new ArrayList<>();
                ASN1Parse(bytes, dsaIntegers);
                if (dsaIntegers.size() < 5) {
                    throw new InvalidKeyException("Stream does not appear to be a properly formatted DSA key");

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Validate the key: openssl rsa -in key.pem -check -noout; regenerate/export the key if corrupt.
  2. Convert the key to proper PKCS#8/PKCS#1 PEM: openssl pkcs8 -topk8 -nocrypt -in key.pem -out key_pkcs8.pem.
  3. Ensure the file is complete and unmodified — re-copy without altering whitespace/line breaks.
  4. If the key is OpenSSH format, convert it: ssh-keygen -p -m PEM -f key.

Example fix

// before
PrivateKey pk = KeyStoreUtils.generatePrivateKey(cr.openInputStream(id_rsa_openssh_uri));
// after
// convert first: ssh-keygen -p -m PEM -f id_rsa
PrivateKey pk = KeyStoreUtils.generatePrivateKey(cr.openInputStream(id_rsa_pem_uri));
Defensive patterns

Strategy: validation

Validate before calling

// verify the RSA key parses correctly (e.g. via openssl in the export pipeline) and file is complete
String pem = readAll(is);
if (!pem.contains("-----BEGIN RSA PRIVATE KEY-----")) {
    throw new IllegalArgumentException("Expected PKCS#1 RSA PEM input");
}
if (pem.replace("\n", "").length() < 800) { // rough sanity size for 2048-bit RSA
    throw new IllegalArgumentException("Key file looks truncated");
}

Try / catch

// try
try {
    PrivateKey pk = KeyStoreUtils.generatePrivateKey(rsaKeyIs);
} catch (InvalidKeyException e) {
    if (e.getMessage().contains("properly formatted RSA key")) {
        // corrupt or wrong-format key: re-export via openssl and retry once
        retryWithReExportedKey();
    }
}

Prevention

When it happens

Trigger: Calling generatePrivateKey with data whose RSA PKCS#1 ASN.1 structure is truncated, malformed, or not actually an RSA key — e.g. an EC or DSA key mislabeled as RSA, or corrupted base64 decoding.

Common situations: Truncated key file from a bad copy/paste; key file with wrong extension fed as RSA; OpenSSH-format keys (openssh-key-v1) instead of PKCS#1 PEM; line-wrapping corruption stripping base64 characters.

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/9d15913b7111271c. Report an issue: GitHub.