Skip to content

Repository files navigation

PEM Key Decryptor (Android)

A small, fully-offline Android app that decrypts a password-protected PEM private key and gives you back both:

  • a ready-to-use java.security.PrivateKey object, and
  • the key re-serialized as an unencrypted PKCS#8 PEM (-----BEGIN PRIVATE KEY-----).

Everything happens on-device — the app requests no INTERNET permission.

Supported key formats

Input Header
PKCS#8 encrypted (PBES2 / AES, etc.) -----BEGIN ENCRYPTED PRIVATE KEY-----
Traditional OpenSSL PKCS#1 (RSA/EC) -----BEGIN RSA PRIVATE KEY----- + Proc-Type / DEK-Info
OpenSSH (openssh-key-v1, bcrypt) -----BEGIN OPENSSH PRIVATE KEY-----
Already-unencrypted keys BEGIN PRIVATE KEY / BEGIN RSA PRIVATE KEY / BEGIN OPENSSH … (passed through)

RSA, EC/ECDSA, and Ed25519 keys are covered. The PKCS#8 / PKCS#1 paths are backed by BouncyCastle (bcprov + bcpkix); OpenSSH keys are handled by SSHJ.

Project layout

app/src/main/java/com/example/pemdecryptor/
├── crypto/PemKeyDecryptor.kt   # reusable, Android-free crypto core
├── MainActivity.kt             # Jetpack Compose UI
├── DecryptViewModel.kt         # UI state; runs decryption off the main thread
└── ui/theme/Theme.kt           # Material 3 theme (dynamic color on Android 12+)
app/src/test/java/.../PemKeyDecryptorTest.kt   # pure-JVM unit tests

The reusable core

If you only want the logic, drop crypto/PemKeyDecryptor.kt into your own project (it has no Android dependencies, only BouncyCastle) and call:

val decrypted = PemKeyDecryptor.decrypt(pemText, password.toCharArray())
decrypted.privateKey       // java.security.PrivateKey
decrypted.unencryptedPem   // unencrypted PKCS#8 PEM String
decrypted.algorithm        // "RSA", "EC", …

It throws WrongPasswordException for a bad passphrase and UnsupportedKeyException for input that isn't a parseable private key.

Android provider note: the PKCS#8 / PKCS#1 paths create their own BouncyCastleProvider() instance and pass it explicitly to every builder. For OpenSSH keys, SSHJ resolves EC keys via the JCE provider registered as "BC"; since Android ships a stripped-down "BC", the core replaces it once with the full BouncyCastle implementation (see ensureFullBouncyCastle). RSA and Ed25519 OpenSSH keys work even without that step.

Build & run

Requires the Android SDK (via Android Studio, or ANDROID_HOME + command-line tools). Open the project in Android Studio (Ladybug or newer) and Run, or:

# Unit tests for the crypto core — no device/emulator needed:
./gradlew :app:testDebugUnitTest

# Build the debug APK:
./gradlew :app:assembleDebug

Toolchain used: AGP 8.7.3, Kotlin 2.0.21, Compose BOM 2024.10.01, minSdk 24, targetSdk 35, JDK 17.

Try it

# Generate an encrypted test key:
openssl genrsa -aes256 -out enc.pem 2048          # asks for a passphrase

# In the app: Load .pem file → enter the passphrase → Decrypt.
# Copy or Save the result, then verify it:
openssl rsa -in exported.pem -check -noout

Security notes

  • No network permission; nothing is uploaded.
  • The passphrase is handled as a CharArray and zero-filled after use.
  • Key material is never written to logs.
  • A decrypted, unencrypted key is sensitive — treat any exported PEM accordingly.

About

PEM Key Decryptor (Android)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages