Android SSHJ исключение при подключении () - "Реализация KeyFactory ECDSA не найдена"

Я пытаюсь открыть сеанс клиента SSH из моего приложения для Android. Попытка подключения к устройству в локальной сети (Raspberry Pi). Я использую библиотеку SSHJ версии 0.10.0. Это не на ssh.connect() позвонить, с TransportException что в конечном итоге вызвано NoSuchAlgorithmException, См. Дерево исключений ниже.

SSHClient ssh = new SSHClient(new AndroidConfig());
Session session = null;

try {    
    //ssh.loadKnownHosts();

    // Exception thrown on this line
    ssh.connect("192.168.1.109", 22);

    // Doesn't reach below
    ssh.authPassword("user", "password");
    session = ssh.startSession();
}
catch (net.schmizz.sshj.transport.TransportException ex) {
    ;
}

Дерево исключений:

net.schmizz.sshj.transport.TransportException
 net.schmizz.sshj.common.SSHException
  net.schmizz.sshj.common.SSHRuntimeException
   java.security.GeneralSecurityException: java.security.NoSuchAlgorithmException: KeyFactory ECDSA implementation not found
    java.security.NoSuchAlgorithmException: KeyFactory ECDSA implementation not found

Другая системная информация:

SSHJ library   : v0.10.0
Android device : Galaxy Note 3 running Android 4.4.2

Я использовал поддержку зависимостей maven в Android Studio, чтобы добавить SSHJ JAR, и он включил следующие три библиотеки в дополнение к SSHJ v0.10.0 jar...

bouncy castle...
  bcpkix-jdk15on-1.50.jar
  bcprov-jdk15on-1.50.jar
logging....
  slf4j-api-1.7.7.jar

Не имею понятия, с чего начать с этим исключением... любые предложения приветствуются! Благодарю.

ОБНОВЛЕНИЕ: 31 октября 2014

По предложению LeeDavidPainter, я включил JAR SpongyCastle 1.51.0 и добавил эту строку вверху:

Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);

Теперь я получаю другое исключение в той же строке:

net.schmizz.sshj.transport.TransportException
 net.schmizz.sshj.common.SSHException
  net.schmizz.sshj.common.SSHRuntimeException
   java.security.GeneralSecurityException: java.security.spec.InvalidKeySpecException: key spec not recognised
    java.security.spec.InvalidKeySpecException: key spec not recognised

Также обратите внимание, что я пробовал следующую строку, с тем же результатом:

Security.addProvider(new org.spongycastle.jce.provider.BouncyCastleProvider());

У меня на телефоне есть другое приложение, которое в основном выполняет именно то, чего я хочу достичь - оно называется RaspberryPiController - оно подключается к вашему RPi через SSH с использованием имени пользователя и пароля. Это работает нормально, так что, похоже, это не проблема сети.

4 ответа

Решение

С SSHJ никуда не деться, поэтому решил попробовать JSch, который предлагает те же функциональные возможности. Он также доступен в виде репозитория Maven - я использовал версию 0.1.51 jsch ('com.jcraft:jsch:0.1.51').

Впервые он работал с этим фрагментом кода;

import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;

import java.io.ByteArrayOutputStream;
import java.util.Properties;

JSch jsch = new JSch();
com.jcraft.jsch.Session session = null;
String result = "";

try {    
  session = jsch.getSession("user", "192.168.1.109", 22);
  session.setPassword("password");

  // Avoid asking for key confirmation
  Properties prop = new Properties();
  prop.put("StrictHostKeyChecking", "no");
  session.setConfig(prop);
  session.connect();

  // SSH Channel
  ChannelExec channel = (ChannelExec)session.openChannel("exec");
  ByteArrayOutputStream stream = new ByteArrayOutputStream();
  channel.setOutputStream(stream);

  // Execute command
  channel.setCommand("ls -ltr");
  channel.connect(1000);
  java.lang.Thread.sleep(500);   // this kludge seemed to be required.
  channel.disconnect();

  result = stream.toString();
}
catch (JSchException ex) {
  String s = ex.toString();
  System.out.println(s);
}
catch (InterruptedException ex) {
  String s = ex.toString();
  System.out.println(s);
}
finally {
  if (session != null)
    session.disconnect();
}

Это похоже на более надежную реализацию при использовании по сравнению с SSHJ - или это впечатление может быть вызвано тем, что они выбирают довольно консервативные таймауты. Например, если целевое устройство выключено, вызов session.connect() по умолчанию будет пытаться установить соединение в течение примерно 20 секунд, прежде чем сдаться.

Android поставляется с урезанной версией BouncyCastle, которая не включает алгоритмы ECDSA. Таким образом, даже если вы включите полную версию в свой путь к классу, версия для Android будет выбрана и использована.

Возможно, вы захотите взглянуть на http://rtyley.github.io/spongycastle/ который был создан, чтобы обойти это, его перепакованную версию Bouncycastle, которую можно установить в качестве отдельного поставщика JCE в Android. Просто установите его в качестве поставщика JCE по умолчанию, прежде чем пытаться подключиться через SSHJ (не проверено).

Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);

Сначала добавьте эту библиотеку BouncyCastle в файл app/build.gradle:

implementation 'org.bouncycastle:bcpkix-jdk15on:1.64'

Затем в вашем файле активности добавьте статический блок, чтобы удалить поставщика BouncyCastle по умолчанию, найденного в Android с нашей версией:

    static {
        Security.removeProvider("BC");//first remove default os provider
        Security.insertProviderAt(new BouncyCastleProvider(), 1);//add new provider
    }

Это решит проблему с не найденной реализацией алгоритма.

Вернитесь к sshj 0.9.0 здесь: http://mvnrepository.com/artifact/net.schmizz/sshj/0.9.0

Проблема, кажется, была введена в 0.10.x. Кроме того, я попробовал другого поставщика JCE, но попал в ту же проблему.

Jsch, скорее всего, работал, потому что он не поддерживает алгоритмы эллиптической кривой для SSH AFAIK. Если вам не нужны алгоритмы эллиптической кривой, то это ваш ответ.

Основываясь на решении LeeDavidPainter,

/**
 * Creates a new SSH client stub
 */
public SSH(final String host, final int port)
{
    SecurityUtils.setSecurityProvider(SecurityUtils.BOUNCY_CASTLE); //<-- Here
    Security.insertProviderAt(new BouncyCastleProvider(), 1); //<-- Here
    this.ssh.addHostKeyVerifier(new PromiscuousVerifier());
    this.shell = new SSHShellSession();
    this.ssh = new SSHClient();
    this.connected = false;
    this.initiated = false;
    this.host = host;
    this.port = port;
}

Две закомментированные области выше //<- вот решение.

Другие вопросы по тегам