MuntashirAkon/AppManager · error · IOException

Stream does not contain an unencrypted private key.

Error message

Stream does not contain an unencrypted private key.

What it means

generatePrivateKey parses a PEM/DER private key stream, accumulating base64 content between BEGIN/END markers. If no base64 key material is found (empty accumulation), it throws IOException "Stream does not contain an unencrypted private key." — i.e. the input wasn't an unencrypted PEM private key.

Solutions

  1. Supply an unencrypted PEM private key (-----BEGIN PRIVATE KEY----- or -----BEGIN RSA PRIVATE KEY-----).
  2. Decrypt an encrypted key first: openssl pkcs8 -topk8 -nocrypt -in encrypted.key -out unencrypted.key (enter passphrase, choose empty).
  3. Verify the correct file is selected — a private key file, not the certificate.
  4. Convert non-PEM formats (e.g. puttygen export to OpenSSH/PEM).

Example fix

// before
InputStream is = cr.openInputStream(encryptedKeyUri);
PrivateKey pk = KeyStoreUtils.generatePrivateKey(is);
// after
// decrypt on the command line first:
// openssl pkcs8 -topk8 -nocrypt -in encrypted.key -out plain.key
InputStream is = cr.openInputStream(plainKeyUri);
PrivateKey pk = KeyStoreUtils.generatePrivateKey(is);
Defensive patterns

Strategy: validation

Validate before calling

// validate the stream is an unencrypted PEM private key before parsing
String pem = readAll(is);
if (!(pem.contains("-----BEGIN PRIVATE KEY-----")
        || pem.contains("-----BEGIN RSA PRIVATE KEY-----")
        || pem.contains("-----BEGIN DSA PRIVATE KEY-----"))) {
    throw new IllegalArgumentException("Not an unencrypted PEM private key (encrypted/cert/other format?)");
}

Type guard

boolean isUnencryptedPemPrivateKey(InputStream is) throws IOException {
    String s = readAll(is);
    return s.contains("-----BEGIN PRIVATE KEY-----")
        || s.contains("-----BEGIN RSA PRIVATE KEY-----")
        || s.contains("-----BEGIN DSA PRIVATE KEY-----");
}

Try / catch

// try
try {
    PrivateKey pk = KeyStoreUtils.generatePrivateKey(keyIs);
} catch (IOException e) {
    if (e.getMessage().contains("unencrypted private key")) {
        // wrong or encrypted input: ask user for a decrypted PEM key file
        showKeyFormatHelpDialog();
    }
}

Prevention

When it happens

Trigger: Calling generatePrivateKey with a stream that is not an unencrypted PKCS#8/PKCS#1 PEM key: encrypted PEM ("ENCRYPTED PRIVATE KEY", or password prompt lines), certificates, wrong file, or binary key where headers don't match expected patterns.

Common situations: User selected the certificate (.crt/.pem) instead of the key; key is openssl-encrypted and requires a passphrase; file exported in a format (e.g. PuTTY .ppk or XML) the parser doesn't recognize.

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

Appendix: source

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

                } else {
                    switch (line) {
                        case RSA_BEGIN_HEADER:
                            readingKey = true;
                            rsaFormat = true;
                            break;
                        case DSA_BEGIN_HEADER:
                            readingKey = true;
                            dsaFormat = true;
                            break;
                        case PKCS8_BEGIN_HEADER:
                            readingKey = true;
                            pkcs8Format = true;
                            break;
                    }
                }
            }
            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.");
                }

View on GitHub (pinned to 0152f468fc)