diff --git a/src/applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php b/src/applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php --- a/src/applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php +++ b/src/applications/passphrase/credentialtype/PassphraseSSHPrivateKeyTextCredentialType.php @@ -30,27 +30,38 @@ } public function requiresPassword(PhutilOpaqueEnvelope $secret) { - // According to the internet, this is the canonical test for an SSH private - // key with a password. - return preg_match('/ENCRYPTED/', $secret->openEnvelope()); + // Some older SSH keys have the text "ENCRYPTED" in the key content. As + // a coarse initial test, just look for this as an indicator. + if (preg_match('/ENCRYPTED/', $secret->openEnvelope())) { + return true; + } + + // Otherwise, try to change the password from the empty string to the + // empty string. If this works, the file has no passphrase. If it does + // not work, assume it has a passphrase. + + $this->requireSSHKeygen(); + $tmp = $this->newTemporaryPrivateKeyFile($secret); + + list($err, $stdout, $stderr) = exec_manual( + 'ssh-keygen -p -P %s -N %s -f %s', + '', + '', + (string)$tmp); + + if (!$err) { + return false; + } + + return true; } public function decryptSecret( PhutilOpaqueEnvelope $secret, PhutilOpaqueEnvelope $password) { - $tmp = new TempFile(); - Filesystem::writeFile($tmp, $secret->openEnvelope()); - - if (!Filesystem::binaryExists('ssh-keygen')) { - throw new Exception( - pht( - 'Decrypting SSH keys requires the `%s` binary, but it '. - 'is not available in %s. Either make it available or strip the '. - 'password from this SSH key manually before uploading it.', - 'ssh-keygen', - '$PATH')); - } + $this->requireSSHKeygen(); + $tmp = $this->newTemporaryPrivateKeyFile($secret); list($err, $stdout, $stderr) = exec_manual( 'ssh-keygen -p -P %P -N %s -f %s', @@ -65,4 +76,25 @@ } } + private function requireSSHKeygen() { + if (Filesystem::binaryExists('ssh-keygen')) { + return; + } + + throw new Exception( + pht( + 'Decrypting SSH keys requires the "ssh-keygen" binary, but it '. + 'is not available in "%s". Either make it available or strip the '. + 'password from this SSH key manually before uploading it.', + '$PATH')); + } + + private function newTemporaryPrivateKeyFile(PhutilOpaqueEnvelope $secret) { + $tmp = new TempFile(); + + Filesystem::writeFile($tmp, $secret->openEnvelope()); + + return $tmp; + } + }