Skip to content

Support aes256-gcm@openssh.com and aes128-gcm@openssh.com for key dec…#479

Merged
kruton merged 2 commits intoconnectbot:mainfrom
nindanaoto:fix_ed25519aes256gcm
Mar 22, 2026
Merged

Support aes256-gcm@openssh.com and aes128-gcm@openssh.com for key dec…#479
kruton merged 2 commits intoconnectbot:mainfrom
nindanaoto:fix_ed25519aes256gcm

Conversation

@nindanaoto
Copy link
Contributor

@nindanaoto nindanaoto commented Mar 22, 2026

Fixes connectbot/connectbot#1984

Issue: Ed25519 keys encrypted with aes256-gcm@openssh.com couldn't be imported — the CommonDecoder didn't recognize GCM ciphers, throwing "Cannot decrypt, unknown cipher".

Root cause: Two problems:

  1. CommonDecoder.decryptData() had no GCM cipher handling — it only supported CBC and CTR modes
  2. OpenSSHKeyDecoder.decode() didn't read the 16-byte GCM authentication tag, which OpenSSH stores after the private section blob (not inside it)

Fix (3 files in sshlib, branch fix_ed25519aes256gcm based on main):

  • CommonDecoder.java: Added decryptDataGcm() method using JCE's AES/GCM/NoPadding with bcrypt-derived key (32 or 16 bytes) + 12-byte nonce
  • OpenSSHKeyDecoder.java: After reading the private section blob, read any remaining bytes (the AEAD auth tag) and append them to the data before decryption
  • OpenSSHKeyDecoderTest.java: Added tests for both aes256-gcm@openssh.com and aes128-gcm@openssh.com encrypted Ed25519 keys, plus a wrong-password test

…ryption

OpenSSH keys encrypted with AES-GCM ciphers (e.g., via ssh-keygen -Z
aes256-gcm@openssh.com) could not be imported, failing with "Cannot
decrypt, unknown cipher". This adds GCM support to CommonDecoder using
JCE's AES/GCM/NoPadding, and fixes OpenSSHKeyDecoder to read the AEAD
authentication tag stored after the private section blob.

Fixes connectbot/connectbot#1984

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
// For AEAD ciphers (e.g., aes256-gcm@openssh.com), the authentication
// tag is stored after the private section blob in the key file.
// Read it and append to dataBytes so the cipher can verify it.
if (tr.remain() > 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changes the behavior of the program for non-AEAD ciphers. It should check the cipherName here to avoid problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I beleieve that it is fixed by a new commit.

Guard the trailing-bytes read with an isAeadCipher() check so non-AEAD
ciphers (CBC, CTR) are not affected. Add a regression test that appends
garbage bytes to a non-AEAD key to verify they are ignored.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@kruton kruton merged commit b9dc743 into connectbot:main Mar 22, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failed to decrypt key. Cannot decrypt, unknown cipher aes256-gcm@openssh.com on Android 10

2 participants