首页 > 解决方案 > 使用 JSch 和 PEM 私钥的 SSH,而路由器使用 openssl 密钥

问题描述

我想在 Kotlin 中使用 JSch 使用 SSL 密钥通过 SSH 连接到我的 OpenWRT 路由器。OpenWRT 支持使用 ssh-keygen ( https://openwrt.org/docs/guide-user/security/dropbear.public-key.auth ) 生成的密钥。

ssh-keygen -t rsa -b 4096

这会生成一个公共 id_rsa.pub 和私有 id_rsa 密钥。OpenWRT 接受这个 id_rsa.pub。我希望 JSch 使用私钥:

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()
            }
        }
    }

但是,这会产生错误:

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

JSch 不支持这种密钥格式,它需要是 PEM。所以我转换了私钥:

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

但现在错误是:

com.jcraft.jsch.JSchException: Auth fail

是不是 OpenWRT 上的公钥和 JSch 上的私钥格式不同的问题?

编辑 13Feb2021:我添加了这一行:

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

我还使用此代码向 JSch 添加了一个记录器,这是输出:

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'

这是 OpenWRT 路由器上的日志(最高日志级别):

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

使用相同私钥的 Mobaxterm 连接到 SSH 时,相同的 OpenWRT 日志:

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

标签: javaandroidandroid-studiokotlinjsch

解决方案


您有两个 Jsch 实例,因此会话不会获得您添加到另一个实例的身份。改成

val jSch = JSch()
jSch.addIdentity("blabla123", privateKey.toByteArray(), null, null)
session = jSch.getSession(username, host, port)

推荐阅读