0

I want to use JSch in Kotlin to connect to my OpenWRT router via SSH, with an SSL key. OpenWRT supports keys, generated with ssh-keygen (https://openwrt.org/docs/guide-user/security/dropbear.public-key.auth).

ssh-keygen -t rsa -b 4096

This generates a public id_rsa.pub and private id_rsa key. OpenWRT accepts this id_rsa.pub. I want JSch to use the private key:

private fun sshCommand(
            username: String? = "root",
            host: String? = "192.168.1.1",
            port: Int = 22,
            command: String?
    ) {
        GlobalScope.launch(Dispatchers.IO) {
            var session: Session? = null
            var channel: ChannelExec? = null

            val privateKey: String = "-----BEGIN OPENSSH PRIVATE KEY-----\n" +
                    "...\n" +
                    "...\n" +
                    "-----END OPENSSH PRIVATE KEY-----"

            try {
                JSch().addIdentity("name", privateKey.toByteArray(), null, null)
                session = JSch().getSession(username, host, port)
                session.setConfig("PreferredAuthentications", "publickey");
                session.setConfig("StrictHostKeyChecking", "no")
                session.connect()
                channel = session.openChannel("exec") as ChannelExec
                channel.setCommand(command)
                val responseStream = ByteArrayOutputStream()
                channel.outputStream = responseStream
                channel.connect()
                while (channel.isConnected) {
                    Thread.sleep(100)
                }
                val responseString = String(responseStream.toByteArray())
                println(responseString)
            } finally {
                session?.disconnect()
                channel?.disconnect()
            }
        }
    }

However, this gives an error:

com.jcraft.jsch.JSchException: invalid privatekey: [B@6166596

JSch doesn't support this key format, it needs to be PEM. So I convert the private key:

-----BEGIN RSA PRIVATE KEY-----
.
.
.
-----END RSA PRIVATE KEY-----

But now the error is:

com.jcraft.jsch.JSchException: Auth fail

Is the problem that the public key on OpenWRT and the private key in JSch are in different formats?

EDIT 13Feb2021: I have added this line:

session.setConfig("PreferredAuthentications", "publickey");

I've also added a logger to JSch, using this code, and this is the output:

W/System.err: INFO: Connecting to 192.168.1.1 port 22
W/System.err: INFO: Connection established
W/System.err: INFO: Remote version string: SSH-2.0-dropbear
W/System.err: INFO: Local version string: SSH-2.0-JSCH-0.1.54
    INFO: CheckCiphers: aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc,aes192-cbc,aes128-cbc,3des-ctr,arcfour,arcfour128,arcfour256
D/EGL_emulation: eglMakeCurrent: 0xf0a7ee60: ver 3 0 (tinfo 0xf0aca800)
D/EGL_emulation: eglMakeCurrent: 0xf0a7ee60: ver 3 0 (tinfo 0xf0aca800)
W/System.err: INFO: CheckKexes: diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521
W/System.err: INFO: CheckSignatures: ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
W/System.err: INFO: SSH_MSG_KEXINIT sent
W/System.err: INFO: SSH_MSG_KEXINIT received
W/System.err: INFO: kex: server: curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,kexguess2@matt.ucc.asn.au
W/System.err: INFO: kex: server: ssh-rsa
W/System.err: INFO: kex: server: aes128-ctr,aes256-ctr
W/System.err: INFO: kex: server: aes128-ctr,aes256-ctr
W/System.err: INFO: kex: server: hmac-sha1,hmac-sha2-256
W/System.err: INFO: kex: server: hmac-sha1,hmac-sha2-256
    INFO: kex: server: none
W/System.err: INFO: kex: server: none
W/System.err: INFO: kex: server: 
    INFO: kex: server: 
W/System.err: INFO: kex: client: ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1
W/System.err: INFO: kex: client: ssh-rsa,ssh-dss,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521
W/System.err: INFO: kex: client: aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc
W/System.err: INFO: kex: client: aes128-ctr,aes128-cbc,3des-ctr,3des-cbc,blowfish-cbc,aes192-ctr,aes192-cbc,aes256-ctr,aes256-cbc
W/System.err: INFO: kex: client: hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96
W/System.err: INFO: kex: client: hmac-md5,hmac-sha1,hmac-sha2-256,hmac-sha1-96,hmac-md5-96
W/System.err: INFO: kex: client: none
W/System.err: INFO: kex: client: none
    INFO: kex: client: 
W/System.err: INFO: kex: client: 
W/System.err: INFO: kex: server->client aes128-ctr hmac-sha1 none
W/System.err: INFO: kex: client->server aes128-ctr hmac-sha1 none
W/System.err: INFO: SSH_MSG_KEXDH_INIT sent
W/System.err: INFO: expecting SSH_MSG_KEXDH_REPLY
W/System.err: INFO: ssh_rsa_verify: signature true
W/System.err: WARN: Permanently added '192.168.1.1' (RSA) to the list of known hosts.
W/System.err: INFO: SSH_MSG_NEWKEYS sent
W/System.err: INFO: SSH_MSG_NEWKEYS received
W/System.err: INFO: SSH_MSG_SERVICE_REQUEST sent
W/System.err: INFO: SSH_MSG_SERVICE_ACCEPT received
W/System.err: INFO: Authentications that can continue: publickey
    INFO: Next authentication method: publickey
W/System.err: INFO: Disconnecting from 192.168.1.1 port 22
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-1
    Process: com.example.ps4nuker, PID: 30981
    com.jcraft.jsch.JSchException: Auth fail
        at com.jcraft.jsch.Session.connect(Session.java:519)
        at com.jcraft.jsch.Session.connect(Session.java:183)
        at com.example.ps4nuker.MainActivity$sshCommand$1.invokeSuspend(MainActivity.kt:113)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
I/Process: Sending signal. PID: 30981 SIG: 9
Disconnected from the target VM, address: 'localhost:56256', transport: 'socket'

This is the log on the OpenWRT router (at maximum log level):

Sat Feb 13 15:47:51 2021 authpriv.info dropbear[7783]: Child connection from 192.168.1.23:56269
Sat Feb 13 15:47:52 2021 authpriv.info dropbear[7783]: Exit before auth (user 'root', 0 fails): Disconnect received

Same OpenWRT log when connecting to SSH with Mobaxterm with the same private key:

Thu Feb 18 00:24:51 2021 authpriv.info dropbear[17648]: Child connection from 192.168.1.23:50127
Thu Feb 18 00:24:52 2021 authpriv.notice dropbear[17648]: Pubkey auth succeeded for 'root' with key sha1!! 85:eb:c4:d2:dd:d9:15:d3:ee:29:3a:80:df:48:8f:6c:41:73:a8:5e from 192.168.1.23:50127
Nikotine
  • 3
  • 5
  • The format should not be the Problem. You could try: a) use the openssh key format with a jsch fork (see my answer https://stackoverflow.com/a/65933464/2290153) b) load the private key by filename to avoid mistakes c) define jsch logger to see more details. It could also be a problem with unsupported or limits towards algorithms. – Matthias Wiedemann Feb 10 '21 at 23:34
  • @MatthiasWiedemann, thanks for the suggestions. I have implemented a logger and have updated my question with the output. I'd like to avoid option (b) for the moment, simply because I haven't studied yet how to do file access in Android. – Nikotine Feb 13 '21 at 15:11
  • It does not seem to be a problem with the library. I don't have OpenWrt, but I tried a docker image with dropbear inside. I got the same error message, when username is wrong or public key is not installed on server. But it works with correct settings. Can you connect with ssh command outside of java? have you tried setting up a non root user with its public key in ~/.ssh/authorized_keys ? – Matthias Wiedemann Feb 16 '21 at 07:42
  • Thanks for testing. Yes, I can connect with putty with a password or with the same private key. I can also connect with JSch when I use a password instead of a key. I just want to avoid having to hard-code the password. I need to run a script on Openwrt via SSH, which changes firewall rules, so I believe I need root for this. I'd like to test your suggestion (b), but where should I store the key file on Android? – Nikotine Feb 17 '21 at 09:01
  • I've added the log from OpenWRT when connecting with Mobaxterm with the same private key. – Nikotine Feb 17 '21 at 23:28

1 Answers1

1

You have two Jsch instances, so the session does not get the identity you added to the other instance. Change to

val jSch = JSch()
jSch.addIdentity("blabla123", privateKey.toByteArray(), null, null)
session = jSch.getSession(username, host, port)
Matthias Wiedemann
  • 1,313
  • 12
  • 22