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;
}
Две закомментированные области выше //<- вот решение.