From eb3dcd25f0772d47cf09f8ae26c13b26817975e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 May 2024 08:47:41 +0000 Subject: [PATCH 1/7] build(deps): bump org.connectbot:sshlib from 2.2.9 to 2.2.23 Bumps [org.connectbot:sshlib](https://github.com/connectbot/sshlib) from 2.2.9 to 2.2.23. - [Commits](https://github.com/connectbot/sshlib/compare/2.2.9...2.2.23) --- updated-dependencies: - dependency-name: org.connectbot:sshlib dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 9ea9e9ff8..ce8adb943 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,7 +93,7 @@ dependencies { implementation 'com.android.support:multidex:1.0.3' implementation 'me.toptas.fancyshowcase:fancyshowcaseview:1.3.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.connectbot:sshlib:2.2.9' + implementation 'org.connectbot:sshlib:2.2.23' def lifecycle_version = "2.8.0" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" From 791e7d422e72c4934172b1e9b85379e9e574f8f8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:44:18 +0000 Subject: [PATCH 2/7] build(deps): bump org.connectbot:sshlib from 2.2.9 to 2.2.23 Bumps [org.connectbot:sshlib](https://github.com/connectbot/sshlib) from 2.2.9 to 2.2.23. - [Commits](https://github.com/connectbot/sshlib/compare/2.2.9...2.2.23) --- updated-dependencies: - dependency-name: org.connectbot:sshlib dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index ae2bed1b4..13877aa5d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -93,7 +93,7 @@ dependencies { implementation 'com.android.support:multidex:1.0.3' implementation 'me.toptas.fancyshowcase:fancyshowcaseview:1.3.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.connectbot:sshlib:2.2.9' + implementation 'org.connectbot:sshlib:2.2.23' implementation "androidx.core:core-splashscreen:1.0.1" def lifecycle_version = "2.8.7" From 96706121604a8fb3f51ad9a269f694bd36102c35 Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Fri, 13 Dec 2024 19:05:23 +0300 Subject: [PATCH 3/7] update ssh implementation --- app/build.gradle | 3 +- app/local.properties | 8 + .../io/treehouses/remote/bases/BaseSSH.kt | 273 +----------------- .../io/treehouses/remote/ssh/PubKeyUtils.kt | 253 ++-------------- build.gradle | 5 +- 5 files changed, 45 insertions(+), 497 deletions(-) create mode 100644 app/local.properties diff --git a/app/build.gradle b/app/build.gradle index 13877aa5d..85554781e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -94,6 +94,7 @@ dependencies { implementation 'me.toptas.fancyshowcase:fancyshowcaseview:1.3.3' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'org.connectbot:sshlib:2.2.23' + implementation 'net.i2p.crypto:eddsa:0.3.0' implementation "androidx.core:core-splashscreen:1.0.1" def lifecycle_version = "2.8.7" @@ -109,6 +110,6 @@ repositories { maven { url "https://maven.aliyun.com/repository/jcenter" } } -android.sourceSets.all { +android.sourceSets.configureEach { java.srcDir("src/$name/kotlin") } diff --git a/app/local.properties b/app/local.properties new file mode 100644 index 000000000..3120d00ee --- /dev/null +++ b/app/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Fri Dec 13 17:45:08 EAT 2024 +sdk.dir=/Users/gideonokuro/Library/Android/sdk diff --git a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt index 10b63f8aa..c22ea2549 100644 --- a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt +++ b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt @@ -1,19 +1,3 @@ -/* - * ConnectBot: simple, powerful, open-source SSH client for Android - * Copyright 2007 Kenny Root, Jeffrey Sharkey - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package io.treehouses.remote.bases import com.trilead.ssh2.* @@ -23,10 +7,10 @@ import com.trilead.ssh2.signature.Ed25519Verify import com.trilead.ssh2.signature.RSASHA1Verify import io.treehouses.remote.R import io.treehouses.remote.ssh.Ed25519Provider.Companion.insertIfNeeded -import io.treehouses.remote.ssh.terminal.TerminalBridge -import io.treehouses.remote.ssh.terminal.TerminalManager import io.treehouses.remote.ssh.beans.HostBean import io.treehouses.remote.ssh.beans.PubKeyBean +import io.treehouses.remote.ssh.terminal.TerminalBridge +import io.treehouses.remote.ssh.terminal.TerminalManager import io.treehouses.remote.utils.KeyUtils import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey @@ -38,9 +22,6 @@ import java.security.interfaces.* import java.util.* import java.util.regex.Pattern -/** - * @author Kenny Root - */ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { var host: HostBean? = null var bridge: TerminalBridge? = null @@ -49,10 +30,7 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { var emulation: String? = null companion object { - //const val protocolName = "ssh" const val TAG = "CB.SSH" - - //const val defaultPort = 22 const val AUTH_PUBLICKEY = "publickey" const val AUTH_PASSWORD = "password" const val AUTH_KEYBOARDINTERACTIVE = "keyboard-interactive" @@ -64,38 +42,7 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { or ChannelCondition.CLOSED or ChannelCondition.EOF) -// fun getUri(input: String?): Uri? { -// val matcher = hostmask.matcher(input) -// if (!matcher.matches()) return null -// val sb = StringBuilder() -// sb.append(protocolName) -// .append("://") -// .append(Uri.encode(matcher.group(1))) -// .append('@') -// .append(Uri.encode(matcher.group(2))) -// val portString = matcher.group(6) -// var port = defaultPort -// if (portString != null) { -// try { -// port = portString.toInt() -// if (port < 1 || port > 65535) { -// port = defaultPort -// } -// } catch (nfe: NumberFormatException) { -// // Keep the default port -// } -// } -// if (port != defaultPort) { -// sb.append(':') -// .append(port) -// } -// sb.append("/#") -// .append(Uri.encode(input)) -// return Uri.parse(sb.toString()) -// } - init { - // Since this class deals with EdDSA keys, we need to make sure this is available. insertIfNeeded() } } @@ -117,8 +64,6 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { protected var stdin: OutputStream? = null protected var stdout: InputStream? = null protected var stderr: InputStream? = null - - // private List portForwards = new ArrayList<>(); protected var columns = 0 protected var rows = 0 private val width = 0 @@ -129,8 +74,6 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { inner class HostKeyVerifier : ExtendedServerHostKeyVerifier() { @Throws(IOException::class) override fun verifyServerHostKey(hostname: String, port: Int, serverHostKeyAlgorithm: String, serverHostKey: ByteArray): Boolean { - - // read in all known hosts from hostdb val hosts = KeyUtils.getAllKnownHosts(manager!!.applicationContext) val matchName = String.format(Locale.US, "%s:%d", hostname, port) val print = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey) @@ -145,10 +88,6 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { val hostWarn = manager!!.res!!.getString(id2, hostname) bridge!!.outputLine(hostWarn) bridge!!.outputLine(hostPrint) - // if (result) { -// // save this key in known database -// manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); -// } promptKeys(hostname, port, serverHostKeyAlgorithm, serverHostKey) } KnownHosts.HOSTKEY_HAS_CHANGED -> { @@ -172,8 +111,7 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { } private fun onHostKeyChanged(algorithmName: String, fingerprint: String) { - val header = String.format("@ %s @", - manager!!.res!!.getString(R.string.host_verification_failure_warning_header)) + val header = String.format("@ %s @", manager!!.res!!.getString(R.string.host_verification_failure_warning_header)) val atsigns = CharArray(header.length) Arrays.fill(atsigns, '@') val border = String(atsigns) @@ -185,7 +123,6 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { } private fun promptKeys(hostname: String, port: Int, serverHostKeyAlgorithm: String, serverHostKey: ByteArray): Boolean { - // Users have no way to delete keys, so we'll prompt them for now. if (continueConnecting()) { KeyUtils.saveKnownHost(manager!!.applicationContext, "$hostname:$port", serverHostKeyAlgorithm, serverHostKey) return true @@ -217,27 +154,14 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { @Throws(IOException::class) protected fun tryPublicKey(username: String?, keyNickname: String?, pair: KeyPair?): Boolean { - //bridge.outputLine(String.format("Attempting 'publickey' with key '%s' [%s]...", keyNickname, trileadKey.toString())); val success = connection!!.authenticateWithPublicKey(username, pair) if (!success) bridge!!.outputLine(manager!!.res!!.getString(R.string.terminal_auth_pubkey_fail, keyNickname)) return success } - /** - * Internal method to request actual PTY terminal once we've finished - * authentication. If called before authenticated, it will just fail. - */ protected fun finishConnection() { authenticated = true -// for (PortForwardBean portForward : portForwards) { -// try { -// enablePortForward(portForward); -// bridge.outputLine(manager.res.getString(R.string.terminal_enable_portfoward, portForward.getDescription())); -// } catch (Exception e) { -// Log.e(TAG, "Error setting up port forward during connect", e); -// } -// } if (!host!!.wantSession) { outputLine(R.string.terminal_no_session) bridge!!.onConnected() @@ -261,17 +185,6 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { } } -// val options: Map -// get() { -// val options: MutableMap = HashMap() -// options["compression"] = java.lang.Boolean.toString(compression) -// return options -// } -// -// fun setOptions(options: Map) { -// if (options.containsKey("compression")) compression = java.lang.Boolean.parseBoolean(options["compression"]) -// } - protected fun onDisconnect() { bridge!!.dispatchDisconnect(false) } @@ -280,183 +193,15 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { onDisconnect() } -// fun canForwardPorts(): Boolean { -// return true -// } - - // @Override - // public List getPortForwards() { - // return portForwards; - // } - // - // @Override - // public boolean addPortForward(PortForwardBean portForward) { - // return portForwards.add(portForward); - // } - // - // @Override - // public boolean removePortForward(PortForwardBean portForward) { - // // Make sure we don't have a phantom forwarder. - // disablePortForward(portForward); - // - // return portForwards.remove(portForward); - // } - // - // @Override - // public boolean enablePortForward(PortForwardBean portForward) { - // if (!portForwards.contains(portForward)) { - // Log.e(TAG, "Attempt to enable port forward not in list"); - // return false; - // } - // - // if (!authenticated) - // return false; - // - // if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { - // LocalPortForwarder lpf = null; - // try { - // lpf = connection.createLocalPortForwarder( - // new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort()), - // portForward.getDestAddr(), portForward.getDestPort()); - // } catch (Exception e) { - // Log.e(TAG, "Could not create local port forward", e); - // return false; - // } - // - // if (lpf == null) { - // Log.e(TAG, "returned LocalPortForwarder object is null"); - // return false; - // } - // - // portForward.setIdentifier(lpf); - // portForward.setEnabled(true); - // return true; - // } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { - // try { - // connection.requestRemotePortForwarding("", portForward.getSourcePort(), portForward.getDestAddr(), portForward.getDestPort()); - // } catch (Exception e) { - // Log.e(TAG, "Could not create remote port forward", e); - // return false; - // } - // - // portForward.setEnabled(true); - // return true; - // } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { - // DynamicPortForwarder dpf = null; - // - // try { - // dpf = connection.createDynamicPortForwarder( - // new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort())); - // } catch (Exception e) { - // Log.e(TAG, "Could not create dynamic port forward", e); - // return false; - // } - // - // portForward.setIdentifier(dpf); - // portForward.setEnabled(true); - // return true; - // } else { - // // Unsupported type - // Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); - // return false; - // } - // } - // @Override - // public boolean disablePortForward(PortForwardBean portForward) { - // if (!portForwards.contains(portForward)) { - // Log.e(TAG, "Attempt to disable port forward not in list"); - // return false; - // } - // - // if (!authenticated) - // return false; - // - // if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { - // LocalPortForwarder lpf = null; - // lpf = (LocalPortForwarder) portForward.getIdentifier(); - // - // if (!portForward.isEnabled() || lpf == null) { - // Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); - // return false; - // } - // - // portForward.setEnabled(false); - // - // lpf.close(); - // - // return true; - // } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { - // portForward.setEnabled(false); - // - // try { - // connection.cancelRemotePortForwarding(portForward.getSourcePort()); - // } catch (IOException e) { - // Log.e(TAG, "Could not stop remote port forwarding, setting enabled to false", e); - // return false; - // } - // - // return true; - // } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { - // DynamicPortForwarder dpf = null; - // dpf = (DynamicPortForwarder) portForward.getIdentifier(); - // - // if (!portForward.isEnabled() || dpf == null) { - // Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); - // return false; - // } - // - // portForward.setEnabled(false); - // - // dpf.close(); - // - // return true; - // } else { - // // Unsupported type - // Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); - // return false; - // } - // } - -// -// fun getDefaultNickname(username: String?, hostname: String?, port: Int): String { -// return if (port == defaultPort) { -// String.format(Locale.US, "%s@%s", username, hostname) -// } else { -// String.format(Locale.US, "%s@%s:%d", username, hostname, port) -// } -// } - - /** - * Handle challenges from keyboard-interactive authentication mode. - */ override fun replyToChallenge(name: String, instruction: String, numPrompts: Int, prompt: Array, echo: BooleanArray): Array { interactiveCanContinue = true val responses = arrayOfNulls(numPrompts) for (i in 0 until numPrompts) { - // request response from user for each prompt responses[i] = bridge!!.promptHelper!!.requestPrompt(instruction, prompt[i], isBool = false) } return responses } -// fun createHost(uri: Uri): HostBean { -// val host = HostBean() -// host.protocol = protocolName -// host.hostname = uri.host -// var port = uri.port -// if (port < 0) port = defaultPort -// host.port = port -// host.username = uri.userInfo -// val nickname = uri.fragment -// if (nickname == null || nickname.isEmpty()) { -// host.nickname = getDefaultNickname(host.username, -// host.hostname, host.port) -// } else { -// host.nickname = uri.fragment -// } -// return host -// } - fun setUseAuthAgent(useAuthAgent: String) { this.useAuthAgent = useAuthAgent } @@ -467,10 +212,13 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { val pair = value?.pair try { pubKeys[key] = when (pair?.private) { - is RSAPrivateKey -> RSASHA1Verify.encodeSSHRSAPublicKey(pair.public as RSAPublicKey) - is DSAPrivateKey -> DSASHA1Verify.encodeSSHDSAPublicKey(pair.public as DSAPublicKey) - is ECPrivateKey -> ECDSASHA2Verify.encodeSSHECDSAPublicKey(pair.public as ECPublicKey) - is EdDSAPrivateKey -> Ed25519Verify.encodeSSHEd25519PublicKey(pair.public as EdDSAPublicKey) + is RSAPrivateKey -> RSASHA1Verify.get().encodePublicKey(pair.public as RSAPublicKey) + is DSAPrivateKey -> DSASHA1Verify.get().encodePublicKey(pair.public as DSAPublicKey) + is ECPrivateKey -> { + val verifier = ECDSASHA2Verify.getVerifierForKey(pair.public as ECPublicKey) + verifier.encodePublicKey(pair.public as ECPublicKey) + } + is EdDSAPrivateKey -> Ed25519Verify.get().encodePublicKey(pair.public as EdDSAPublicKey) else -> ByteArray(0) } } catch (e: IOException) { @@ -499,7 +247,6 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { override fun addIdentity(pair: KeyPair, comment: String, confirmUse: Boolean, lifetime: Int): Boolean { val pubkey = PubKeyBean() - // pubkey.setType(PubkeyDatabase.KEY_TYPE_IMPORTED); pubkey.nickname = comment pubkey.isConfirmUse = confirmUse pubkey.lifetime = lifetime diff --git a/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt b/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt index 8749310ab..1f9aec8a6 100644 --- a/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt +++ b/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt @@ -1,19 +1,3 @@ -/* - * ConnectBot: simple, powerful, open-source SSH client for Android - * Copyright 2007 Kenny Root, Jeffrey Sharkey - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package io.treehouses.remote.ssh import com.trilead.ssh2.crypto.Base64 @@ -40,10 +24,7 @@ object PubKeyUtils { const val PKCS8_START = "-----BEGIN PRIVATE KEY-----" const val PKCS8_END = "-----END PRIVATE KEY-----" - // Size in bytes of salt to use. private const val SALT_SIZE = 8 - - // Number of iterations for password hashing. PKCS#5 recommends 1000 private const val ITERATIONS = 1000 fun formatKey(key: Key): String { val algo = key.algorithm @@ -105,231 +86,43 @@ object PubKeyUtils { } } - // static String getAlgorithmForOid(String oid) throws NoSuchAlgorithmException { - // if ("1.2.840.10045.2.1".equals(oid)) { - // return "EC"; - // } else if ("1.2.840.113549.1.1.1".equals(oid)) { - // return "RSA"; - // } else if ("1.2.840.10040.4.1".equals(oid)) { - // return "DSA"; - // } else { - // throw new NoSuchAlgorithmException("Unknown algorithm OID " + oid); - // } - // } - // - // static String getOidFromPkcs8Encoded(byte[] encoded) throws NoSuchAlgorithmException { - // if (encoded == null) { - // throw new NoSuchAlgorithmException("encoding is null"); - // } - // - // try { - // SimpleDERReader reader = new SimpleDERReader(encoded); - // reader.resetInput(reader.readSequenceAsByteArray()); - // reader.readInt(); - // reader.resetInput(reader.readSequenceAsByteArray()); - // return reader.readOid(); - // } catch (IOException e) { - // Log.w(TAG, "Could not read OID", e); - // throw new NoSuchAlgorithmException("Could not read key", e); - // } - // } - // static BigInteger getRSAPublicExponentFromPkcs8Encoded(byte[] encoded) throws InvalidKeySpecException { - // if (encoded == null) { - // throw new InvalidKeySpecException("encoded key is null"); - // } - // - // try { - // SimpleDERReader reader = new SimpleDERReader(encoded); - // reader.resetInput(reader.readSequenceAsByteArray()); - // if (!reader.readInt().equals(BigInteger.ZERO)) { - // throw new InvalidKeySpecException("PKCS#8 is not version 0"); - // } - // - // reader.readSequenceAsByteArray(); // OID sequence - // reader.resetInput(reader.readOctetString()); // RSA key bytes - // reader.resetInput(reader.readSequenceAsByteArray()); // RSA key sequence - // - // if (!reader.readInt().equals(BigInteger.ZERO)) { - // throw new InvalidKeySpecException("RSA key is not version 0"); - // } - // - // reader.readInt(); // modulus - // return reader.readInt(); // public exponent - // } catch (IOException e) { - // Log.w(TAG, "Could not read public exponent", e); - // throw new InvalidKeySpecException("Could not read key", e); - // } - // } - // public static KeyPair convertToKeyPair(PubKeyBean keybean, String password) throws BadPasswordException { - //// if (PubkeyDatabase.KEY_TYPE_IMPORTED.equals(keybean.getType())) { - //// // load specific key using pem format - //// try { - //// return PEMDecoder.decode(new String(keybean.getPrivateKey(), "UTF-8").toCharArray(), password); - //// } catch (Exception e) { - //// Log.e(TAG, "Cannot decode imported key", e); - //// throw new BadPasswordException(); - //// } - //// } else { - // // load using internal generated format - // try { - // PrivateKey privKey = PubKeyUtils.decodePrivate(keybean.getPrivateKey(), keybean.getType(), password); - // PublicKey pubKey = PubKeyUtils.decodePublic(keybean.getPublicKey(), keybean.getType()); - // Log.d(TAG, "Unlocked key " + PubKeyUtils.formatKey(pubKey)); - // - // return new KeyPair(pubKey, privKey); - // } catch (Exception e) { - // Log.e(TAG, "Cannot decode pubkey from database", e); - // throw new BadPasswordException(); - // } - //// } - // } - // public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException, - // InvalidKeySpecException { - // final String algo = getAlgorithmForOid(getOidFromPkcs8Encoded(encoded)); - // - // final KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded); - // - // final KeyFactory kf = KeyFactory.getInstance(algo); - // final PrivateKey priv = kf.generatePrivate(privKeySpec); - // - // return new KeyPair(recoverPublicKey(kf, priv), priv); - // } - // - // static PublicKey recoverPublicKey(KeyFactory kf, PrivateKey priv) - // throws NoSuchAlgorithmException, InvalidKeySpecException { - // if (priv instanceof RSAPrivateCrtKey) { - // RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) priv; - // return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv - // .getPublicExponent())); - // } else if (priv instanceof RSAPrivateKey) { - // BigInteger publicExponent = getRSAPublicExponentFromPkcs8Encoded(priv.getEncoded()); - // RSAPrivateKey rsaPriv = (RSAPrivateKey) priv; - // return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), publicExponent)); - // } else if (priv instanceof DSAPrivateKey) { - // DSAPrivateKey dsaPriv = (DSAPrivateKey) priv; - // DSAParams params = dsaPriv.getParams(); - // - // // Calculate public key Y - // BigInteger y = params.getG().modPow(dsaPriv.getX(), params.getP()); - // - // return kf.generatePublic(new DSAPublicKeySpec(y, params.getP(), params.getQ(), params - // .getG())); - // } else if (priv instanceof ECPrivateKey) { - // ECPrivateKey ecPriv = (ECPrivateKey) priv; - // ECParameterSpec params = ecPriv.getParams(); - // - // // Calculate public key Y - // ECPoint generator = params.getGenerator(); - // BigInteger[] wCoords = EcCore.multiplyPointA(new BigInteger[] { generator.getAffineX(), - // generator.getAffineY() }, ecPriv.getS(), params); - // ECPoint w = new ECPoint(wCoords[0], wCoords[1]); - // - // return kf.generatePublic(new ECPublicKeySpec(w, params)); - // } else { - // throw new NoSuchAlgorithmException("Key type must be RSA, DSA, or EC"); - // } - // } - /* - * OpenSSH compatibility methods - */ - fun convertToOpenSSHFormat(pk: PublicKey, nickName: String) : String { - val rsaKey = if (pk is RSAPublicKey) String(Base64.encode(RSASHA1Verify.encodeSSHRSAPublicKey(pk))) else "" + fun convertToOpenSSHFormat(pk: PublicKey, nickName: String): String { return when (pk) { - is RSAPublicKey -> "ssh-rsa $rsaKey $nickName" - is DSAPublicKey -> "ssh-dss ${String(Base64.encode(DSASHA1Verify.encodeSSHDSAPublicKey(pk)))} $nickName" + is RSAPublicKey -> { + val rsaKey = String(Base64.encode(RSASHA1Verify.get().encodePublicKey(pk))) + "ssh-rsa $rsaKey $nickName" + } + is DSAPublicKey -> { + val dsaKey = String(Base64.encode(DSASHA1Verify.get().encodePublicKey(pk))) + "ssh-dss $dsaKey $nickName" + } is ECPublicKey -> { - val keyType = ECDSASHA2Verify.getCurveName(pk.params.curve.field.fieldSize) - val data = String(Base64.encode(ECDSASHA2Verify.encodeSSHECDSAPublicKey(pk))) - "${ECDSASHA2Verify.ECDSA_SHA2_PREFIX} $keyType $data $nickName" + val verifier = ECDSASHA2Verify.getVerifierForKey(pk) + val keyType = verifier.keyFormat + val data = String(Base64.encode(verifier.encodePublicKey(pk))) + "$keyType $data $nickName" + } + is EdDSAPublicKey -> { + val edKey = String(Base64.encode(Ed25519Verify.get().encodePublicKey(pk))) + "${Ed25519Verify.get().keyFormat} $edKey $nickName" } - is EdDSAPublicKey -> "${Ed25519Verify.ED25519_ID} ${String(Base64.encode(Ed25519Verify.encodeSSHEd25519PublicKey(pk)))} $nickName" else -> throw InvalidKeyException("Unknown Key Type") } } - /* - * OpenSSH compatibility methods - */ - /** - * @param pair KeyPair to convert to an OpenSSH public key - * @return OpenSSH-encoded pubkey - */ + fun extractOpenSSHPublic(pair: KeyPair?): ByteArray? { return try { when (val pubKey = pair?.public) { - is RSAPublicKey -> RSASHA1Verify.encodeSSHRSAPublicKey(pubKey) - is DSAPublicKey -> DSASHA1Verify.encodeSSHDSAPublicKey(pubKey) - is ECPublicKey -> ECDSASHA2Verify.encodeSSHECDSAPublicKey(pubKey) - is EdDSAPublicKey -> Ed25519Verify.encodeSSHEd25519PublicKey(pubKey) + is RSAPublicKey -> RSASHA1Verify.get().encodePublicKey(pubKey) + is DSAPublicKey -> DSASHA1Verify.get().encodePublicKey(pubKey) + is ECPublicKey -> ECDSASHA2Verify.getVerifierForKey(pubKey).encodePublicKey(pubKey) + is EdDSAPublicKey -> Ed25519Verify.get().encodePublicKey(pubKey) else -> null } } catch (e: IOException) { null } - } // public static String exportPEM(PrivateKey key, String secret) throws NoSuchAlgorithmException, InvalidParameterSpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException, IllegalBlockSizeException, IOException { - - // StringBuilder sb = new StringBuilder(); - // - // byte[] data = key.getEncoded(); - // - // sb.append(PKCS8_START); - // sb.append('\n'); - // - // if (secret != null) { - // byte[] salt = new byte[8]; - // SecureRandom random = new SecureRandom(); - // random.nextBytes(salt); - // - // PBEParameterSpec defParams = new PBEParameterSpec(salt, 1); - // AlgorithmParameters params = AlgorithmParameters.getInstance(key.getAlgorithm()); - // - // params.init(defParams); - // - // PBEKeySpec pbeSpec = new PBEKeySpec(secret.toCharArray()); - // - // SecretKeyFactory keyFact = SecretKeyFactory.getInstance(key.getAlgorithm()); - // Cipher cipher = Cipher.getInstance(key.getAlgorithm()); - // cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), params); - // - // byte[] wrappedKey = cipher.wrap(key); - // - // EncryptedPrivateKeyInfo pinfo = new EncryptedPrivateKeyInfo(params, wrappedKey); - // - // data = pinfo.getEncoded(); - // - // sb.append("Proc-Type: 4,ENCRYPTED\n"); - // sb.append("DEK-Info: DES-EDE3-CBC,"); - // sb.append(encodeHex(salt)); - // sb.append("\n\n"); - // } - // - // int i = sb.length(); - // sb.append(Base64.encode(data)); - // for (i += 63; i < sb.length(); i += 64) { - // sb.insert(i, "\n"); - // } - // - // sb.append('\n'); - // sb.append(PKCS8_END); - // sb.append('\n'); - // - // return sb.toString(); - // } - // private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', - // '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; - // static String encodeHex(byte[] bytes) { - // final char[] hex = new char[bytes.length * 2]; - // - // int i = 0; - // for (byte b : bytes) { - // hex[i++] = HEX_DIGITS[(b >> 4) & 0x0f]; - // hex[i++] = HEX_DIGITS[b & 0x0f]; - // } - // - // return String.valueOf(hex); - // } - // - // public static class BadPasswordException extends Exception { - // } + } init { insertIfNeeded() } diff --git a/build.gradle b/build.gradle index accf7497d..e715dc085 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ buildscript { ext.kotlin_version = '2.1.0' repositories { - jcenter() google() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.7.2' @@ -24,8 +24,7 @@ allprojects { repositories { google() maven { url "https://jitpack.io" } - jcenter() - + mavenCentral() } } From 6e275834d8c349a7a97780b4f3820ba6a9042127 Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Fri, 13 Dec 2024 19:45:22 +0300 Subject: [PATCH 4/7] revert commented code --- .../io/treehouses/remote/bases/BaseSSH.kt | 258 +++++++++++++++++- .../io/treehouses/remote/ssh/PubKeyUtils.kt | 220 ++++++++++++++- 2 files changed, 474 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt index c22ea2549..505353621 100644 --- a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt +++ b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt @@ -1,3 +1,19 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.treehouses.remote.bases import com.trilead.ssh2.* @@ -7,10 +23,10 @@ import com.trilead.ssh2.signature.Ed25519Verify import com.trilead.ssh2.signature.RSASHA1Verify import io.treehouses.remote.R import io.treehouses.remote.ssh.Ed25519Provider.Companion.insertIfNeeded -import io.treehouses.remote.ssh.beans.HostBean -import io.treehouses.remote.ssh.beans.PubKeyBean import io.treehouses.remote.ssh.terminal.TerminalBridge import io.treehouses.remote.ssh.terminal.TerminalManager +import io.treehouses.remote.ssh.beans.HostBean +import io.treehouses.remote.ssh.beans.PubKeyBean import io.treehouses.remote.utils.KeyUtils import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPublicKey @@ -22,6 +38,9 @@ import java.security.interfaces.* import java.util.* import java.util.regex.Pattern +/** + * @author Kenny Root + */ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { var host: HostBean? = null var bridge: TerminalBridge? = null @@ -30,7 +49,10 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { var emulation: String? = null companion object { + //const val protocolName = "ssh" const val TAG = "CB.SSH" + + //const val defaultPort = 22 const val AUTH_PUBLICKEY = "publickey" const val AUTH_PASSWORD = "password" const val AUTH_KEYBOARDINTERACTIVE = "keyboard-interactive" @@ -42,7 +64,38 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { or ChannelCondition.CLOSED or ChannelCondition.EOF) +// fun getUri(input: String?): Uri? { +// val matcher = hostmask.matcher(input) +// if (!matcher.matches()) return null +// val sb = StringBuilder() +// sb.append(protocolName) +// .append("://") +// .append(Uri.encode(matcher.group(1))) +// .append('@') +// .append(Uri.encode(matcher.group(2))) +// val portString = matcher.group(6) +// var port = defaultPort +// if (portString != null) { +// try { +// port = portString.toInt() +// if (port < 1 || port > 65535) { +// port = defaultPort +// } +// } catch (nfe: NumberFormatException) { +// // Keep the default port +// } +// } +// if (port != defaultPort) { +// sb.append(':') +// .append(port) +// } +// sb.append("/#") +// .append(Uri.encode(input)) +// return Uri.parse(sb.toString()) +// } + init { + // Since this class deals with EdDSA keys, we need to make sure this is available. insertIfNeeded() } } @@ -64,6 +117,8 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { protected var stdin: OutputStream? = null protected var stdout: InputStream? = null protected var stderr: InputStream? = null + + // private List portForwards = new ArrayList<>() protected var columns = 0 protected var rows = 0 private val width = 0 @@ -74,6 +129,8 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { inner class HostKeyVerifier : ExtendedServerHostKeyVerifier() { @Throws(IOException::class) override fun verifyServerHostKey(hostname: String, port: Int, serverHostKeyAlgorithm: String, serverHostKey: ByteArray): Boolean { + + // read in all known hosts from hostdb val hosts = KeyUtils.getAllKnownHosts(manager!!.applicationContext) val matchName = String.format(Locale.US, "%s:%d", hostname, port) val print = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey) @@ -88,6 +145,10 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { val hostWarn = manager!!.res!!.getString(id2, hostname) bridge!!.outputLine(hostWarn) bridge!!.outputLine(hostPrint) + // if (result) { +// // save this key in known database +// manager.hostdb.saveKnownHost(hostname, port, serverHostKeyAlgorithm, serverHostKey); +// } promptKeys(hostname, port, serverHostKeyAlgorithm, serverHostKey) } KnownHosts.HOSTKEY_HAS_CHANGED -> { @@ -123,6 +184,7 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { } private fun promptKeys(hostname: String, port: Int, serverHostKeyAlgorithm: String, serverHostKey: ByteArray): Boolean { + // Users have no way to delete keys, so we'll prompt them for now. if (continueConnecting()) { KeyUtils.saveKnownHost(manager!!.applicationContext, "$hostname:$port", serverHostKeyAlgorithm, serverHostKey) return true @@ -154,14 +216,27 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { @Throws(IOException::class) protected fun tryPublicKey(username: String?, keyNickname: String?, pair: KeyPair?): Boolean { + //bridge.outputLine(String.format("Attempting 'publickey' with key '%s' [%s]...", keyNickname, trileadKey.toString())); val success = connection!!.authenticateWithPublicKey(username, pair) if (!success) bridge!!.outputLine(manager!!.res!!.getString(R.string.terminal_auth_pubkey_fail, keyNickname)) return success } + /** + * Internal method to request actual PTY terminal once we've finished + * authentication. If called before authenticated, it will just fail. + */ protected fun finishConnection() { authenticated = true +// for (PortForwardBean portForward : portForwards) { +// try { +// enablePortForward(portForward); +// bridge.outputLine(manager.res.getString(R.string.terminal_enable_portfoward, portForward.getDescription())); +// } catch (Exception e) { +// Log.e(TAG, "Error setting up port forward during connect", e); +// } +// } if (!host!!.wantSession) { outputLine(R.string.terminal_no_session) bridge!!.onConnected() @@ -185,6 +260,17 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { } } +// val options: Map +// get() { +// val options: MutableMap = HashMap() +// options["compression"] = java.lang.Boolean.toString(compression) +// return options +// } +// +// fun setOptions(options: Map) { +// if (options.containsKey("compression")) compression = java.lang.Boolean.parseBoolean(options["compression"]) +// } + protected fun onDisconnect() { bridge!!.dispatchDisconnect(false) } @@ -193,10 +279,160 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { onDisconnect() } +// fun canForwardPorts(): Boolean { +// return true +// } + +// @Override +// public List getPortForwards() { +// return portForwards; +// } +// +// @Override +// public boolean addPortForward(PortForwardBean portForward) { +// return portForwards.add(portForward); +// } +// +// @Override +// public boolean removePortForward(PortForwardBean portForward) { +// // Make sure we don't have a phantom forwarder. +// disablePortForward(portForward); +// +// return portForwards.remove(portForward); +// } +// +// @Override +// public boolean enablePortForward(PortForwardBean portForward) { +// if (!portForwards.contains(portForward)) { +// Log.e(TAG, "Attempt to enable port forward not in list"); +// return false; +// } +// +// if (!authenticated) +// return false; +// +// if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { +// LocalPortForwarder lpf = null; +// try { +// lpf = connection.createLocalPortForwarder( +// new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort()), +// portForward.getDestAddr(), portForward.getDestPort()); +// } catch (Exception e) { +// Log.e(TAG, "Could not create local port forward", e); +// return false; +// } +// +// if (lpf == null) { +// Log.e(TAG, "returned LocalPortForwarder object is null"); +// return false; +// } +// +// portForward.setIdentifier(lpf); +// portForward.setEnabled(true); +// return true; +// } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { +// try { +// connection.requestRemotePortForwarding("", portForward.getSourcePort(), portForward.getDestAddr(), portForward.getDestPort()); +// } catch (Exception e) { +// Log.e(TAG, "Could not create remote port forward", e); +// return false; +// } +// +// portForward.setEnabled(true); +// return true; +// } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { +// DynamicPortForwarder dpf = null; +// +// try { +// dpf = connection.createDynamicPortForwarder( +// new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort())); +// } catch (Exception e) { +// Log.e(TAG, "Could not create dynamic port forward", e); +// return false; +// } +// +// portForward.setIdentifier(dpf); +// portForward.setEnabled(true); +// return true; +// } else { +// // Unsupported type +// Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); +// return false; +// } +// } +// @Override +// public boolean disablePortForward(PortForwardBean portForward) { +// if (!portForwards.contains(portForward)) { +// Log.e(TAG, "Attempt to disable port forward not in list"); +// return false; +// } +// +// if (!authenticated) +// return false; +// +// if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { +// LocalPortForwarder lpf = null; +// lpf = (LocalPortForwarder) portForward.getIdentifier(); +// +// if (!portForward.isEnabled() || lpf == null) { +// Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); +// return false; +// } +// +// portForward.setEnabled(false); +// +// lpf.close(); +// +// return true; +// } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { +// portForward.setEnabled(false); +// +// try { +// connection.cancelRemotePortForwarding(portForward.getSourcePort()); +// } catch (IOException e) { +// Log.e(TAG, "Could not stop remote port forwarding, setting enabled to false", e); +// return false; +// } +// +// return true; +// } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { +// DynamicPortForwarder dpf = null; +// dpf = (DynamicPortForwarder) portForward.getIdentifier(); +// +// if (!portForward.isEnabled() || dpf == null) { +// Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); +// return false; +// } +// +// portForward.setEnabled(false); +// +// dpf.close(); +// +// return true; +// } else { +// // Unsupported type +// Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); +// return false; +// } +// } + +// +// fun getDefaultNickname(username: String?, hostname: String?, port: Int): String { +// return if (port == defaultPort) { +// String.format(Locale.US, "%s@%s", username, hostname) +// } else { +// String.format(Locale.US, "%s@%s:%d", username, hostname, port) +// } +// } + + /** + * Handle challenges from keyboard-interactive authentication mode. + */ override fun replyToChallenge(name: String, instruction: String, numPrompts: Int, prompt: Array, echo: BooleanArray): Array { interactiveCanContinue = true val responses = arrayOfNulls(numPrompts) for (i in 0 until numPrompts) { + // request response from user for each prompt responses[i] = bridge!!.promptHelper!!.requestPrompt(instruction, prompt[i], isBool = false) } return responses @@ -206,6 +442,24 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { this.useAuthAgent = useAuthAgent } +// fun createHost(uri: Uri): HostBean { +// val host = HostBean() +// host.protocol = protocolName +// host.hostname = uri.host +// var port = uri.port +// if (port < 0) port = defaultPort +// host.port = port +// host.username = uri.userInfo +// val nickname = uri.fragment +// if (nickname == null || nickname.isEmpty()) { +// host.nickname = getDefaultNickname(host.username, +// host.hostname, host.port) +// } else { +// host.nickname = uri.fragment +// } +// return host +// } + override fun retrieveIdentities(): Map { val pubKeys: MutableMap = HashMap(manager!!.loadedKeypairs.size) for ((key, value) in manager!!.loadedKeypairs) { diff --git a/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt b/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt index 1f9aec8a6..96a52c0ca 100644 --- a/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt +++ b/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt @@ -1,3 +1,19 @@ +/* + * ConnectBot: simple, powerful, open-source SSH client for Android + * Copyright 2007 Kenny Root, Jeffrey Sharkey + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package io.treehouses.remote.ssh import com.trilead.ssh2.crypto.Base64 @@ -24,7 +40,10 @@ object PubKeyUtils { const val PKCS8_START = "-----BEGIN PRIVATE KEY-----" const val PKCS8_END = "-----END PRIVATE KEY-----" + // Size in bytes of salt to use. private const val SALT_SIZE = 8 + + // Number of iterations for password hashing. PKCS#5 recommends 1000 private const val ITERATIONS = 1000 fun formatKey(key: Key): String { val algo = key.algorithm @@ -86,6 +105,133 @@ object PubKeyUtils { } } +// static String getAlgorithmForOid(String oid) throws NoSuchAlgorithmException { +// if ("1.2.840.10045.2.1".equals(oid)) { +// return "EC"; +// } else if ("1.2.840.113549.1.1.1".equals(oid)) { +// return "RSA"; +// } else if ("1.2.840.10040.4.1".equals(oid)) { +// return "DSA"; +// } else { +// throw new NoSuchAlgorithmException("Unknown algorithm OID " + oid); +// } +// } +// +// static String getOidFromPkcs8Encoded(byte[] encoded) throws NoSuchAlgorithmException { +// if (encoded == null) { +// throw new NoSuchAlgorithmException("encoding is null"); +// } +// +// try { +// SimpleDERReader reader = new SimpleDERReader(encoded); +// reader.resetInput(reader.readSequenceAsByteArray()); +// reader.readInt(); +// reader.resetInput(reader.readSequenceAsByteArray()); +// return reader.readOid(); +// } catch (IOException e) { +// Log.w(TAG, "Could not read OID", e); +// throw new NoSuchAlgorithmException("Could not read key", e); +// } +// } +// static BigInteger getRSAPublicExponentFromPkcs8Encoded(byte[] encoded) throws InvalidKeySpecException { +// if (encoded == null) { +// throw new InvalidKeySpecException("encoded key is null"); +// } +// +// try { +// SimpleDERReader reader = new SimpleDERReader(encoded); +// reader.resetInput(reader.readSequenceAsByteArray()); +// if (!reader.readInt().equals(BigInteger.ZERO)) { +// throw new InvalidKeySpecException("PKCS#8 is not version 0"); +// } +// +// reader.readSequenceAsByteArray(); // OID sequence +// reader.resetInput(reader.readOctetString()); // RSA key bytes +// reader.resetInput(reader.readSequenceAsByteArray()); // RSA key sequence +// +// if (!reader.readInt().equals(BigInteger.ZERO)) { +// throw new InvalidKeySpecException("RSA key is not version 0"); +// } +// +// reader.readInt(); // modulus +// return reader.readInt(); // public exponent +// } catch (IOException e) { +// Log.w(TAG, "Could not read public exponent", e); +// throw new InvalidKeySpecException("Could not read key", e); +// } +// } +// public static KeyPair convertToKeyPair(PubKeyBean keybean, String password) throws BadPasswordException { +//// if (PubkeyDatabase.KEY_TYPE_IMPORTED.equals(keybean.getType())) { +//// // load specific key using pem format +//// try { +//// return PEMDecoder.decode(new String(keybean.getPrivateKey(), "UTF-8").toCharArray(), password); +//// } catch (Exception e) { +//// Log.e(TAG, "Cannot decode imported key", e); +//// throw new BadPasswordException(); +//// } +//// } else { +// // load using internal generated format +// try { +// PrivateKey privKey = PubKeyUtils.decodePrivate(keybean.getPrivateKey(), keybean.getType(), password); +// PublicKey pubKey = PubKeyUtils.decodePublic(keybean.getPublicKey(), keybean.getType()); +// Log.d(TAG, "Unlocked key " + PubKeyUtils.formatKey(pubKey)); +// +// return new KeyPair(pubKey, privKey); +// } catch (Exception e) { +// Log.e(TAG, "Cannot decode pubkey from database", e); +// throw new BadPasswordException(); +// } +//// } +// } +// public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException, +// InvalidKeySpecException { +// final String algo = getAlgorithmForOid(getOidFromPkcs8Encoded(encoded)); +// +// final KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded); +// +// final KeyFactory kf = KeyFactory.getInstance(algo); +// final PrivateKey priv = kf.generatePrivate(privKeySpec); +// +// return new KeyPair(recoverPublicKey(kf, priv), priv); +// } +// +// static PublicKey recoverPublicKey(KeyFactory kf, PrivateKey priv) +// throws NoSuchAlgorithmException, InvalidKeySpecException { +// if (priv instanceof RSAPrivateCrtKey) { +// RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) priv; +// return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv +// .getPublicExponent())); +// } else if (priv instanceof RSAPrivateKey) { +// BigInteger publicExponent = getRSAPublicExponentFromPkcs8Encoded(priv.getEncoded()); +// RSAPrivateKey rsaPriv = (RSAPrivateKey) priv; +// return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), publicExponent)); +// } else if (priv instanceof DSAPrivateKey) { +// DSAPrivateKey dsaPriv = (DSAPrivateKey) priv; +// DSAParams params = dsaPriv.getParams(); +// +// // Calculate public key Y +// BigInteger y = params.getG().modPow(dsaPriv.getX(), params.getP()); +// +// return kf.generatePublic(new DSAPublicKeySpec(y, params.getP(), params.getQ(), params +// .getG())); +// } else if (priv instanceof ECPrivateKey) { +// ECPrivateKey ecPriv = (ECPrivateKey) priv; +// ECParameterSpec params = ecPriv.getParams(); +// +// // Calculate public key Y +// ECPoint generator = params.getGenerator(); +// BigInteger[] wCoords = EcCore.multiplyPointA(new BigInteger[] { generator.getAffineX(), +// generator.getAffineY() }, ecPriv.getS(), params); +// ECPoint w = new ECPoint(wCoords[0], wCoords[1]); +// +// return kf.generatePublic(new ECPublicKeySpec(w, params)); +// } else { +// throw new NoSuchAlgorithmException("Key type must be RSA, DSA, or EC"); +// } +// } + /* + * OpenSSH compatibility methods + */ fun convertToOpenSSHFormat(pk: PublicKey, nickName: String): String { return when (pk) { is RSAPublicKey -> { @@ -109,7 +255,13 @@ object PubKeyUtils { else -> throw InvalidKeyException("Unknown Key Type") } } - + /* + * OpenSSH compatibility methods + */ + /** + * @param pair KeyPair to convert to an OpenSSH public key + * @return OpenSSH-encoded pubkey + */ fun extractOpenSSHPublic(pair: KeyPair?): ByteArray? { return try { when (val pubKey = pair?.public) { @@ -122,7 +274,71 @@ object PubKeyUtils { } catch (e: IOException) { null } - } + } // public static String exportPEM(PrivateKey key, String secret) throws NoSuchAlgorithmException, InvalidParameterSpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, InvalidKeySpecException, IllegalBlockSizeException, IOException { + + // StringBuilder sb = new StringBuilder(); + // + // byte[] data = key.getEncoded(); + // + // sb.append(PKCS8_START); + // sb.append('\n'); + // + // if (secret != null) { + // byte[] salt = new byte[8]; + // SecureRandom random = new SecureRandom(); + // random.nextBytes(salt); + // + // PBEParameterSpec defParams = new PBEParameterSpec(salt, 1); + // AlgorithmParameters params = AlgorithmParameters.getInstance(key.getAlgorithm()); + // + // params.init(defParams); + // + // PBEKeySpec pbeSpec = new PBEKeySpec(secret.toCharArray()); + // + // SecretKeyFactory keyFact = SecretKeyFactory.getInstance(key.getAlgorithm()); + // Cipher cipher = Cipher.getInstance(key.getAlgorithm()); + // cipher.init(Cipher.WRAP_MODE, keyFact.generateSecret(pbeSpec), params); + // + // byte[] wrappedKey = cipher.wrap(key); + // + // EncryptedPrivateKeyInfo pinfo = new EncryptedPrivateKeyInfo(params, wrappedKey); + // + // data = pinfo.getEncoded(); + // + // sb.append("Proc-Type: 4,ENCRYPTED\n"); + // sb.append("DEK-Info: DES-EDE3-CBC,"); + // sb.append(encodeHex(salt)); + // sb.append("\n\n"); + // } + // + // int i = sb.length(); + // sb.append(Base64.encode(data)); + // for (i += 63; i < sb.length(); i += 64) { + // sb.insert(i, "\n"); + // } + // + // sb.append('\n'); + // sb.append(PKCS8_END); + // sb.append('\n'); + // + // return sb.toString(); + // } + // private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', + // '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; + // static String encodeHex(byte[] bytes) { + // final char[] hex = new char[bytes.length * 2]; + // + // int i = 0; + // for (byte b : bytes) { + // hex[i++] = HEX_DIGITS[(b >> 4) & 0x0f]; + // hex[i++] = HEX_DIGITS[b & 0x0f]; + // } + // + // return String.valueOf(hex); + // } + // + // public static class BadPasswordException extends Exception { + // } init { insertIfNeeded() } From 008d4b1f3513b32b1fb29218b018984a23b5c580 Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Fri, 13 Dec 2024 19:48:46 +0300 Subject: [PATCH 5/7] revert baseSSH commented code --- .../io/treehouses/remote/bases/BaseSSH.kt | 283 +++++++++--------- 1 file changed, 142 insertions(+), 141 deletions(-) diff --git a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt index 505353621..2f02f41e0 100644 --- a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt +++ b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt @@ -283,147 +283,147 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { // return true // } -// @Override -// public List getPortForwards() { -// return portForwards; -// } -// -// @Override -// public boolean addPortForward(PortForwardBean portForward) { -// return portForwards.add(portForward); -// } -// -// @Override -// public boolean removePortForward(PortForwardBean portForward) { -// // Make sure we don't have a phantom forwarder. -// disablePortForward(portForward); -// -// return portForwards.remove(portForward); -// } -// -// @Override -// public boolean enablePortForward(PortForwardBean portForward) { -// if (!portForwards.contains(portForward)) { -// Log.e(TAG, "Attempt to enable port forward not in list"); -// return false; -// } -// -// if (!authenticated) -// return false; -// -// if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { -// LocalPortForwarder lpf = null; -// try { -// lpf = connection.createLocalPortForwarder( -// new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort()), -// portForward.getDestAddr(), portForward.getDestPort()); -// } catch (Exception e) { -// Log.e(TAG, "Could not create local port forward", e); -// return false; -// } -// -// if (lpf == null) { -// Log.e(TAG, "returned LocalPortForwarder object is null"); -// return false; -// } -// -// portForward.setIdentifier(lpf); -// portForward.setEnabled(true); -// return true; -// } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { -// try { -// connection.requestRemotePortForwarding("", portForward.getSourcePort(), portForward.getDestAddr(), portForward.getDestPort()); -// } catch (Exception e) { -// Log.e(TAG, "Could not create remote port forward", e); -// return false; -// } -// -// portForward.setEnabled(true); -// return true; -// } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { -// DynamicPortForwarder dpf = null; -// -// try { -// dpf = connection.createDynamicPortForwarder( -// new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort())); -// } catch (Exception e) { -// Log.e(TAG, "Could not create dynamic port forward", e); -// return false; -// } -// -// portForward.setIdentifier(dpf); -// portForward.setEnabled(true); -// return true; -// } else { -// // Unsupported type -// Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); -// return false; -// } -// } -// @Override -// public boolean disablePortForward(PortForwardBean portForward) { -// if (!portForwards.contains(portForward)) { -// Log.e(TAG, "Attempt to disable port forward not in list"); -// return false; -// } -// -// if (!authenticated) -// return false; -// -// if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { -// LocalPortForwarder lpf = null; -// lpf = (LocalPortForwarder) portForward.getIdentifier(); -// -// if (!portForward.isEnabled() || lpf == null) { -// Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); -// return false; -// } -// -// portForward.setEnabled(false); -// -// lpf.close(); -// -// return true; -// } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { -// portForward.setEnabled(false); -// -// try { -// connection.cancelRemotePortForwarding(portForward.getSourcePort()); -// } catch (IOException e) { -// Log.e(TAG, "Could not stop remote port forwarding, setting enabled to false", e); -// return false; -// } -// -// return true; -// } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { -// DynamicPortForwarder dpf = null; -// dpf = (DynamicPortForwarder) portForward.getIdentifier(); -// -// if (!portForward.isEnabled() || dpf == null) { -// Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); -// return false; -// } -// -// portForward.setEnabled(false); -// -// dpf.close(); -// -// return true; -// } else { -// // Unsupported type -// Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); -// return false; -// } -// } - -// -// fun getDefaultNickname(username: String?, hostname: String?, port: Int): String { -// return if (port == defaultPort) { -// String.format(Locale.US, "%s@%s", username, hostname) -// } else { -// String.format(Locale.US, "%s@%s:%d", username, hostname, port) -// } -// } + // @Override + // public List getPortForwards() { + // return portForwards; + // } + // + // @Override + // public boolean addPortForward(PortForwardBean portForward) { + // return portForwards.add(portForward); + // } + // + // @Override + // public boolean removePortForward(PortForwardBean portForward) { + // // Make sure we don't have a phantom forwarder. + // disablePortForward(portForward); + // + // return portForwards.remove(portForward); + // } + // + // @Override + // public boolean enablePortForward(PortForwardBean portForward) { + // if (!portForwards.contains(portForward)) { + // Log.e(TAG, "Attempt to enable port forward not in list"); + // return false; + // } + // + // if (!authenticated) + // return false; + // + // if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { + // LocalPortForwarder lpf = null; + // try { + // lpf = connection.createLocalPortForwarder( + // new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort()), + // portForward.getDestAddr(), portForward.getDestPort()); + // } catch (Exception e) { + // Log.e(TAG, "Could not create local port forward", e); + // return false; + // } + // + // if (lpf == null) { + // Log.e(TAG, "returned LocalPortForwarder object is null"); + // return false; + // } + // + // portForward.setIdentifier(lpf); + // portForward.setEnabled(true); + // return true; + // } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { + // try { + // connection.requestRemotePortForwarding("", portForward.getSourcePort(), portForward.getDestAddr(), portForward.getDestPort()); + // } catch (Exception e) { + // Log.e(TAG, "Could not create remote port forward", e); + // return false; + // } + // + // portForward.setEnabled(true); + // return true; + // } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { + // DynamicPortForwarder dpf = null; + // + // try { + // dpf = connection.createDynamicPortForwarder( + // new InetSocketAddress(InetAddress.getLocalHost(), portForward.getSourcePort())); + // } catch (Exception e) { + // Log.e(TAG, "Could not create dynamic port forward", e); + // return false; + // } + // + // portForward.setIdentifier(dpf); + // portForward.setEnabled(true); + // return true; + // } else { + // // Unsupported type + // Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); + // return false; + // } + // } + // @Override + // public boolean disablePortForward(PortForwardBean portForward) { + // if (!portForwards.contains(portForward)) { + // Log.e(TAG, "Attempt to disable port forward not in list"); + // return false; + // } + // + // if (!authenticated) + // return false; + // + // if (HostDatabase.PORTFORWARD_LOCAL.equals(portForward.getType())) { + // LocalPortForwarder lpf = null; + // lpf = (LocalPortForwarder) portForward.getIdentifier(); + // + // if (!portForward.isEnabled() || lpf == null) { + // Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); + // return false; + // } + // + // portForward.setEnabled(false); + // + // lpf.close(); + // + // return true; + // } else if (HostDatabase.PORTFORWARD_REMOTE.equals(portForward.getType())) { + // portForward.setEnabled(false); + // + // try { + // connection.cancelRemotePortForwarding(portForward.getSourcePort()); + // } catch (IOException e) { + // Log.e(TAG, "Could not stop remote port forwarding, setting enabled to false", e); + // return false; + // } + // + // return true; + // } else if (HostDatabase.PORTFORWARD_DYNAMIC5.equals(portForward.getType())) { + // DynamicPortForwarder dpf = null; + // dpf = (DynamicPortForwarder) portForward.getIdentifier(); + // + // if (!portForward.isEnabled() || dpf == null) { + // Log.d(TAG, String.format("Could not disable %s; it appears to be not enabled or have no handler", portForward.getNickname())); + // return false; + // } + // + // portForward.setEnabled(false); + // + // dpf.close(); + // + // return true; + // } else { + // // Unsupported type + // Log.e(TAG, String.format("attempt to forward unknown type %s", portForward.getType())); + // return false; + // } + // } + + // + // fun getDefaultNickname(username: String?, hostname: String?, port: Int): String { + // return if (port == defaultPort) { + // String.format(Locale.US, "%s@%s", username, hostname) + // } else { + // String.format(Locale.US, "%s@%s:%d", username, hostname, port) + // } + // } /** * Handle challenges from keyboard-interactive authentication mode. @@ -501,6 +501,7 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { override fun addIdentity(pair: KeyPair, comment: String, confirmUse: Boolean, lifetime: Int): Boolean { val pubkey = PubKeyBean() + // pubkey.setType(PubkeyDatabase.KEY_TYPE_IMPORTED); pubkey.nickname = comment pubkey.isConfirmUse = confirmUse pubkey.lifetime = lifetime From 60c1e06eebfa61dfce919457ba6e471349d95707 Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Fri, 13 Dec 2024 19:51:18 +0300 Subject: [PATCH 6/7] Update BaseSSH.kt --- .../io/treehouses/remote/bases/BaseSSH.kt | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt index 2f02f41e0..1093dce3a 100644 --- a/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt +++ b/app/src/main/kotlin/io/treehouses/remote/bases/BaseSSH.kt @@ -118,7 +118,7 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { protected var stdout: InputStream? = null protected var stderr: InputStream? = null - // private List portForwards = new ArrayList<>() + // private List portForwards = new ArrayList<>(); protected var columns = 0 protected var rows = 0 private val width = 0 @@ -172,7 +172,8 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { } private fun onHostKeyChanged(algorithmName: String, fingerprint: String) { - val header = String.format("@ %s @", manager!!.res!!.getString(R.string.host_verification_failure_warning_header)) + val header = String.format("@ %s @", + manager!!.res!!.getString(R.string.host_verification_failure_warning_header)) val atsigns = CharArray(header.length) Arrays.fill(atsigns, '@') val border = String(atsigns) @@ -416,14 +417,14 @@ open class BaseSSH : ConnectionMonitor, InteractiveCallback, AuthAgentCallback { // } // } - // - // fun getDefaultNickname(username: String?, hostname: String?, port: Int): String { - // return if (port == defaultPort) { - // String.format(Locale.US, "%s@%s", username, hostname) - // } else { - // String.format(Locale.US, "%s@%s:%d", username, hostname, port) - // } - // } +// +// fun getDefaultNickname(username: String?, hostname: String?, port: Int): String { +// return if (port == defaultPort) { +// String.format(Locale.US, "%s@%s", username, hostname) +// } else { +// String.format(Locale.US, "%s@%s:%d", username, hostname, port) +// } +// } /** * Handle challenges from keyboard-interactive authentication mode. From a97ecb95f7559ac9803a0c9243ac5b7d54a17e52 Mon Sep 17 00:00:00 2001 From: Gideon Okuro Date: Fri, 13 Dec 2024 19:53:05 +0300 Subject: [PATCH 7/7] Update PubKeyUtils.kt --- .../io/treehouses/remote/ssh/PubKeyUtils.kt | 248 +++++++++--------- 1 file changed, 124 insertions(+), 124 deletions(-) diff --git a/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt b/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt index 96a52c0ca..8f9789e53 100644 --- a/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt +++ b/app/src/main/kotlin/io/treehouses/remote/ssh/PubKeyUtils.kt @@ -105,130 +105,130 @@ object PubKeyUtils { } } -// static String getAlgorithmForOid(String oid) throws NoSuchAlgorithmException { -// if ("1.2.840.10045.2.1".equals(oid)) { -// return "EC"; -// } else if ("1.2.840.113549.1.1.1".equals(oid)) { -// return "RSA"; -// } else if ("1.2.840.10040.4.1".equals(oid)) { -// return "DSA"; -// } else { -// throw new NoSuchAlgorithmException("Unknown algorithm OID " + oid); -// } -// } -// -// static String getOidFromPkcs8Encoded(byte[] encoded) throws NoSuchAlgorithmException { -// if (encoded == null) { -// throw new NoSuchAlgorithmException("encoding is null"); -// } -// -// try { -// SimpleDERReader reader = new SimpleDERReader(encoded); -// reader.resetInput(reader.readSequenceAsByteArray()); -// reader.readInt(); -// reader.resetInput(reader.readSequenceAsByteArray()); -// return reader.readOid(); -// } catch (IOException e) { -// Log.w(TAG, "Could not read OID", e); -// throw new NoSuchAlgorithmException("Could not read key", e); -// } -// } -// static BigInteger getRSAPublicExponentFromPkcs8Encoded(byte[] encoded) throws InvalidKeySpecException { -// if (encoded == null) { -// throw new InvalidKeySpecException("encoded key is null"); -// } -// -// try { -// SimpleDERReader reader = new SimpleDERReader(encoded); -// reader.resetInput(reader.readSequenceAsByteArray()); -// if (!reader.readInt().equals(BigInteger.ZERO)) { -// throw new InvalidKeySpecException("PKCS#8 is not version 0"); -// } -// -// reader.readSequenceAsByteArray(); // OID sequence -// reader.resetInput(reader.readOctetString()); // RSA key bytes -// reader.resetInput(reader.readSequenceAsByteArray()); // RSA key sequence -// -// if (!reader.readInt().equals(BigInteger.ZERO)) { -// throw new InvalidKeySpecException("RSA key is not version 0"); -// } -// -// reader.readInt(); // modulus -// return reader.readInt(); // public exponent -// } catch (IOException e) { -// Log.w(TAG, "Could not read public exponent", e); -// throw new InvalidKeySpecException("Could not read key", e); -// } -// } -// public static KeyPair convertToKeyPair(PubKeyBean keybean, String password) throws BadPasswordException { -//// if (PubkeyDatabase.KEY_TYPE_IMPORTED.equals(keybean.getType())) { -//// // load specific key using pem format -//// try { -//// return PEMDecoder.decode(new String(keybean.getPrivateKey(), "UTF-8").toCharArray(), password); -//// } catch (Exception e) { -//// Log.e(TAG, "Cannot decode imported key", e); -//// throw new BadPasswordException(); -//// } -//// } else { -// // load using internal generated format -// try { -// PrivateKey privKey = PubKeyUtils.decodePrivate(keybean.getPrivateKey(), keybean.getType(), password); -// PublicKey pubKey = PubKeyUtils.decodePublic(keybean.getPublicKey(), keybean.getType()); -// Log.d(TAG, "Unlocked key " + PubKeyUtils.formatKey(pubKey)); -// -// return new KeyPair(pubKey, privKey); -// } catch (Exception e) { -// Log.e(TAG, "Cannot decode pubkey from database", e); -// throw new BadPasswordException(); -// } -//// } -// } -// public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException, -// InvalidKeySpecException { -// final String algo = getAlgorithmForOid(getOidFromPkcs8Encoded(encoded)); -// -// final KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded); -// -// final KeyFactory kf = KeyFactory.getInstance(algo); -// final PrivateKey priv = kf.generatePrivate(privKeySpec); -// -// return new KeyPair(recoverPublicKey(kf, priv), priv); -// } -// -// static PublicKey recoverPublicKey(KeyFactory kf, PrivateKey priv) -// throws NoSuchAlgorithmException, InvalidKeySpecException { -// if (priv instanceof RSAPrivateCrtKey) { -// RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) priv; -// return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv -// .getPublicExponent())); -// } else if (priv instanceof RSAPrivateKey) { -// BigInteger publicExponent = getRSAPublicExponentFromPkcs8Encoded(priv.getEncoded()); -// RSAPrivateKey rsaPriv = (RSAPrivateKey) priv; -// return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), publicExponent)); -// } else if (priv instanceof DSAPrivateKey) { -// DSAPrivateKey dsaPriv = (DSAPrivateKey) priv; -// DSAParams params = dsaPriv.getParams(); -// -// // Calculate public key Y -// BigInteger y = params.getG().modPow(dsaPriv.getX(), params.getP()); -// -// return kf.generatePublic(new DSAPublicKeySpec(y, params.getP(), params.getQ(), params -// .getG())); -// } else if (priv instanceof ECPrivateKey) { -// ECPrivateKey ecPriv = (ECPrivateKey) priv; -// ECParameterSpec params = ecPriv.getParams(); -// -// // Calculate public key Y -// ECPoint generator = params.getGenerator(); -// BigInteger[] wCoords = EcCore.multiplyPointA(new BigInteger[] { generator.getAffineX(), -// generator.getAffineY() }, ecPriv.getS(), params); -// ECPoint w = new ECPoint(wCoords[0], wCoords[1]); -// -// return kf.generatePublic(new ECPublicKeySpec(w, params)); -// } else { -// throw new NoSuchAlgorithmException("Key type must be RSA, DSA, or EC"); -// } -// } + // static String getAlgorithmForOid(String oid) throws NoSuchAlgorithmException { + // if ("1.2.840.10045.2.1".equals(oid)) { + // return "EC"; + // } else if ("1.2.840.113549.1.1.1".equals(oid)) { + // return "RSA"; + // } else if ("1.2.840.10040.4.1".equals(oid)) { + // return "DSA"; + // } else { + // throw new NoSuchAlgorithmException("Unknown algorithm OID " + oid); + // } + // } + // + // static String getOidFromPkcs8Encoded(byte[] encoded) throws NoSuchAlgorithmException { + // if (encoded == null) { + // throw new NoSuchAlgorithmException("encoding is null"); + // } + // + // try { + // SimpleDERReader reader = new SimpleDERReader(encoded); + // reader.resetInput(reader.readSequenceAsByteArray()); + // reader.readInt(); + // reader.resetInput(reader.readSequenceAsByteArray()); + // return reader.readOid(); + // } catch (IOException e) { + // Log.w(TAG, "Could not read OID", e); + // throw new NoSuchAlgorithmException("Could not read key", e); + // } + // } + // static BigInteger getRSAPublicExponentFromPkcs8Encoded(byte[] encoded) throws InvalidKeySpecException { + // if (encoded == null) { + // throw new InvalidKeySpecException("encoded key is null"); + // } + // + // try { + // SimpleDERReader reader = new SimpleDERReader(encoded); + // reader.resetInput(reader.readSequenceAsByteArray()); + // if (!reader.readInt().equals(BigInteger.ZERO)) { + // throw new InvalidKeySpecException("PKCS#8 is not version 0"); + // } + // + // reader.readSequenceAsByteArray(); // OID sequence + // reader.resetInput(reader.readOctetString()); // RSA key bytes + // reader.resetInput(reader.readSequenceAsByteArray()); // RSA key sequence + // + // if (!reader.readInt().equals(BigInteger.ZERO)) { + // throw new InvalidKeySpecException("RSA key is not version 0"); + // } + // + // reader.readInt(); // modulus + // return reader.readInt(); // public exponent + // } catch (IOException e) { + // Log.w(TAG, "Could not read public exponent", e); + // throw new InvalidKeySpecException("Could not read key", e); + // } + // } + // public static KeyPair convertToKeyPair(PubKeyBean keybean, String password) throws BadPasswordException { + //// if (PubkeyDatabase.KEY_TYPE_IMPORTED.equals(keybean.getType())) { + //// // load specific key using pem format + //// try { + //// return PEMDecoder.decode(new String(keybean.getPrivateKey(), "UTF-8").toCharArray(), password); + //// } catch (Exception e) { + //// Log.e(TAG, "Cannot decode imported key", e); + //// throw new BadPasswordException(); + //// } + //// } else { + // // load using internal generated format + // try { + // PrivateKey privKey = PubKeyUtils.decodePrivate(keybean.getPrivateKey(), keybean.getType(), password); + // PublicKey pubKey = PubKeyUtils.decodePublic(keybean.getPublicKey(), keybean.getType()); + // Log.d(TAG, "Unlocked key " + PubKeyUtils.formatKey(pubKey)); + // + // return new KeyPair(pubKey, privKey); + // } catch (Exception e) { + // Log.e(TAG, "Cannot decode pubkey from database", e); + // throw new BadPasswordException(); + // } + //// } + // } + // public static KeyPair recoverKeyPair(byte[] encoded) throws NoSuchAlgorithmException, + // InvalidKeySpecException { + // final String algo = getAlgorithmForOid(getOidFromPkcs8Encoded(encoded)); + // + // final KeySpec privKeySpec = new PKCS8EncodedKeySpec(encoded); + // + // final KeyFactory kf = KeyFactory.getInstance(algo); + // final PrivateKey priv = kf.generatePrivate(privKeySpec); + // + // return new KeyPair(recoverPublicKey(kf, priv), priv); + // } + // + // static PublicKey recoverPublicKey(KeyFactory kf, PrivateKey priv) + // throws NoSuchAlgorithmException, InvalidKeySpecException { + // if (priv instanceof RSAPrivateCrtKey) { + // RSAPrivateCrtKey rsaPriv = (RSAPrivateCrtKey) priv; + // return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), rsaPriv + // .getPublicExponent())); + // } else if (priv instanceof RSAPrivateKey) { + // BigInteger publicExponent = getRSAPublicExponentFromPkcs8Encoded(priv.getEncoded()); + // RSAPrivateKey rsaPriv = (RSAPrivateKey) priv; + // return kf.generatePublic(new RSAPublicKeySpec(rsaPriv.getModulus(), publicExponent)); + // } else if (priv instanceof DSAPrivateKey) { + // DSAPrivateKey dsaPriv = (DSAPrivateKey) priv; + // DSAParams params = dsaPriv.getParams(); + // + // // Calculate public key Y + // BigInteger y = params.getG().modPow(dsaPriv.getX(), params.getP()); + // + // return kf.generatePublic(new DSAPublicKeySpec(y, params.getP(), params.getQ(), params + // .getG())); + // } else if (priv instanceof ECPrivateKey) { + // ECPrivateKey ecPriv = (ECPrivateKey) priv; + // ECParameterSpec params = ecPriv.getParams(); + // + // // Calculate public key Y + // ECPoint generator = params.getGenerator(); + // BigInteger[] wCoords = EcCore.multiplyPointA(new BigInteger[] { generator.getAffineX(), + // generator.getAffineY() }, ecPriv.getS(), params); + // ECPoint w = new ECPoint(wCoords[0], wCoords[1]); + // + // return kf.generatePublic(new ECPublicKeySpec(w, params)); + // } else { + // throw new NoSuchAlgorithmException("Key type must be RSA, DSA, or EC"); + // } + // } /* * OpenSSH compatibility methods */