The SSH protocol uses encryption to secure the connection between a client and a server. All user authentication, commands, output, and file transfers are encrypted to protect against attacks in the network. More often than not you would need to SSH into your cloud Virtual Machines or a remote shell. Usually, we need an SSH client to establish an SSH connection. For Windows, the free (libre) GUI client PuTTY is used for this purpose. Here is how it looks like accessing a remote Linux shell in PuTTY:
The following tutorial illustrates how the same can be achieved in android. Note that we are going to implement this project using the Java language. Library: Apache MINA SSHD
Step by Step Implementation
Step 1: Create a New Project
To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.
Step 2: Adding dependencies
Now we need to add the required dependencies in order to use the apache mina sshd library. Open app/src/build.gradle file in your project directory and add the following under dependencies:
// Adding implementations required for apache mina library
implementation ‘org.apache.mina:mina-core:3.0.0-M2’
implementation ‘org.apache.sshd:sshd-core:2.1.0’
implementation ‘org.apache.sshd:sshd-putty:2.1.0’
implementation ‘org.apache.sshd:sshd-common:2.1.0’
implementation ‘org.slf4j:slf4j-api:1.7.5’
implementation ‘org.slf4j:slf4j-simple:1.6.4’
Step 3: Add permission to the internet in your AndroidManifest.xml file
Add below two lines inside your src/res/AndroidManifest.xml file inside <manifest></manifest> tags.
<uses-permission android:name=”android.permission.INTERNET” />
Step 4: Modify the strings.xml file
Open app > src > main > res > values > strings.xml and add the following code:
XML
< resources > < string name = "app_name" >gfg_ssh</ string > < string name = "host" >Host</ string > < string name = "port" >Port</ string > < string name = "defaultPort" >22</ string > < string name = "username" >Username</ string > < string name = "password" >Password</ string > < string name = "button_send" >SEND</ string > </ resources > |
Step 5: Building the UI
We are going to have two activities in our app.
- MainActivity: Here, we’ll enter the login details
- sshActivity: Here, we’ll see the shell output.
Working with the activity_main.xml file:
Now, Navigate to the app > src > main > res > layout > activity_main.xml and add the below code to the file.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" android:background = "@color/black" tools:context = ".MainActivity" > < LinearLayout android:id = "@+id/linear" android:layout_width = "270dp" android:layout_height = "413dp" android:layout_margin = "50dp" android:orientation = "vertical" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toTopOf = "parent" app:layout_constraintWidth_percent = ".8" > < EditText android:id = "@+id/editText" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_gravity = "center" android:autofillHints = "host" android:background = "@color/white" android:ems = "10" android:hint = "@string/host" android:inputType = "textPersonName" android:padding = "10dp" android:textColor = "@color/black" android:textColorHint = "@color/black" app:layout_constraintWidth_percent = ".8" /> < EditText android:id = "@+id/portField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "22dp" android:autofillHints = "port" android:background = "@color/white" android:ems = "10" android:hint = "@string/port" android:inputType = "textPersonName" android:padding = "10dp" android:textColor = "@color/black" android:textColorHint = "@color/black" app:layout_constraintHorizontal_bias = "0.5" app:layout_constraintWidth_percent = ".8" tools:text = "@string/defaultPort" /> < EditText android:id = "@+id/usernameField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "22dp" android:autofillHints = "username" android:background = "@color/white" android:ems = "10" android:hint = "@string/username" android:inputType = "textPersonName" android:padding = "10dp" android:textColor = "@color/black" android:textColorHint = "@color/black" app:layout_constraintBottom_toTopOf = "@+id/passwordField" app:layout_constraintEnd_toEndOf = "parent" app:layout_constraintHorizontal_bias = "0.5" app:layout_constraintStart_toStartOf = "parent" app:layout_constraintTop_toBottomOf = "@+id/portField" app:layout_constraintWidth_percent = ".8" /> < EditText android:id = "@+id/passwordField" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "22dp" android:autofillHints = "password" android:background = "@color/white" android:ems = "10" android:hint = "@string/password" android:inputType = "textPassword" android:padding = "10dp" android:textColor = "@color/black" android:textColorHint = "@color/black" app:layout_constraintWidth_percent = ".8" /> < Button android:id = "@+id/button" android:layout_width = "194dp" android:layout_height = "61dp" android:layout_gravity = "center" android:layout_marginTop = "69dp" android:layout_marginBottom = "373dp" android:background = "#594FAA" android:onClick = "authenticate" android:text = "@string/button_send" android:textColor = "@android:color/background_light" tools:text = "@string/button_send" /> </ LinearLayout > </ androidx.constraintlayout.widget.ConstraintLayout > |
First, create a new activity sshActivity by right-clicking on the activity folder (com.example.gfg_ssh) and then select New > Activity > Empty Activity and Now add the below code to the activity_ssh.xml file for UI of this activity.
XML
<? xml version = "1.0" encoding = "utf-8" ?> < androidx.coordinatorlayout.widget.CoordinatorLayout android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".sshActivity" > < TextView android:id = "@+id/textView" android:layout_width = "match_parent" android:layout_height = "688dp" android:background = "@android:color/black" android:textColor = "@color/white" android:textSize = "18sp" /> </ androidx.coordinatorlayout.widget.CoordinatorLayout > |
Step 6: Working with the MainActivity.java file
Go to the MainActivity.java file and refer to the following code. Below is the code for the MainActivity.java file. Comments are added inside the code to understand the code in more detail.
Java
import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.EditText; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void authenticate(View view) { // Create an intent for sshActivity Intent intent = new Intent( this , sshActivity. class ); // Declare fields EditText editText = (EditText) findViewById(R.id.editText); EditText portField = (EditText) findViewById(R.id.portField); EditText usernameField = (EditText) findViewById(R.id.usernameField); EditText passwordField = (EditText) findViewById(R.id.passwordField); // Get input data from fields String host = editText.getText().toString(); String port = portField.getText().toString(); String username = usernameField.getText().toString(); String password = passwordField.getText().toString(); // Pass on data to sshActivity via intent intent.putExtra( "host" , host); intent.putExtra( "port" , port); intent.putExtra( "username" , username); intent.putExtra( "password" , password); startActivity(intent); finish(); } } |
Step 7: Working with the sshActivity.java file
This is the activity where we import the Apache MINA SSHD library and connect to our remote machine via SSH. We obtain the login data from the intent. Below is the code for the sshActivity.java file. Comments are added inside the code to understand the code in more detail.
Java
import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.channel.ClientChannel; import org.apache.sshd.client.channel.ClientChannelEvent; import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.channel.Channel; import org.apache.sshd.server.forward.AcceptAllForwardingFilter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.util.EnumSet; import java.util.concurrent.TimeUnit; public class sshActivity extends AppCompatActivity { ClientChannel channel; TextView shellOutput; String host, username, password; Integer port; String command; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_ssh); // set output field shellOutput = findViewById(R.id.textView); // Get user credentials from indent Intent intent = getIntent(); host = intent.getStringExtra( "host" ); port = Integer.parseInt(intent.getStringExtra( "port" )); username = intent.getStringExtra( "username" ); password = intent.getStringExtra( "password" ); // Command which will be executed command = "pwd\n" ; // Setting user.com property manually // since isn't set by default in android String key = "user.home" ; Context Syscontext; Syscontext = getApplicationContext(); String val = Syscontext.getApplicationInfo().dataDir; System.setProperty(key, val); // Creating a client instance SshClient client = SshClient.setUpDefaultClient(); client.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE); client.start(); // Starting new thread because network processes // can interfere with UI if started in main thread Thread thread = new Thread( new Runnable() { @Override public void run() { try { // Connection establishment and authentication try (ClientSession session = client.connect(username, host, port).verify( 10000 ).getSession()) { session.addPasswordIdentity(password); session.auth().verify( 50000 ); System.out.println( "Connection establihed" ); // Create a channel to communicate channel = session.createChannel(Channel.CHANNEL_SHELL); System.out.println( "Starting shell" ); ByteArrayOutputStream responseStream = new ByteArrayOutputStream(); channel.setOut(responseStream); // Open channel channel.open().verify( 5 , TimeUnit.SECONDS); try (OutputStream pipedIn = channel.getInvertedIn()) { pipedIn.write(command.getBytes()); pipedIn.flush(); } // Close channel channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED), TimeUnit.SECONDS.toMillis( 5 )); // Output after converting to string type String responseString = new String(responseStream.toByteArray()); System.out.println(responseString); shellOutput.setText(responseString); } catch (IOException e) { e.printStackTrace(); } finally { client.stop(); } } catch (Exception e) { e.printStackTrace(); } } }); thread.start(); } } |
Output:
Logging in:
SSH command execution: