I am unable to login to server using key authentication from my client Android app. I am able to login to server from my app using password authentication. I am able to login to server using key authentication from other clients on my network.
protected String sshConnect() {
String stdoutString = null;
String alias = "KEYPAIRTEST";
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry entry = keyStore.getEntry(alias, null);
PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey();
KeyPair keyPair = new KeyPair(publicKey, privateKey);
// create a client instance
try (SshClient client = SshClient.setUpDefaultClient()) {
client.setServerKeyVerifier(AcceptAllServerKeyVerifier.INSTANCE); // should be default
client.addPublicKeyIdentity(keyPair);
//client.addPasswordIdentity(this.password);
client.start();
// connection and authentication
try (ClientSession session = client.connect(username, host, port).verify(TimeUnit.SECONDS.toMillis(defaultTimeoutSeconds)).getSession()) {
session.auth().verify(TimeUnit.SECONDS.toMillis(defaultTimeoutSeconds));
// create a channel to communicate
ClientChannel channel = session.createExecChannel(this.command);
ByteArrayOutputStream stdoutStream = new ByteArrayOutputStream();
channel.setOut(stdoutStream);
// open channel
channel.open().verify(5, TimeUnit.SECONDS);
// close channel
channel.waitFor(EnumSet.of(ClientChannelEvent.CLOSED),
TimeUnit.SECONDS.toMillis(5));
// output after converting to string type
stdoutString = stdoutStream.toString();
} catch (Exception e) {
Log.d(TAG, "sshConnect(): client.connect(): Exception: " + e);
}
} catch (Exception e) {
Log.d(TAG, "sshConnect(): client.start(): Exception: " + e);
}
return stdoutString;
}
I got a string version of the public key with the following..
keyStore.getCertificate(alias).getPublicKey();
byte[] encodedPublicKey = publicKey.getEncoded();
String b64PublicKey = Base64.getEncoder().encodeToString(encodedPublicKey);
I pasted that public key on the server in ~/.ssh/authorized_keys
.
ssh-rsa MlsdfijANBgkqhkiG9w0BAQE..
I also tried..
rsa-sha2-256 MlsdfijANBgkqhkiG9w0BAQE..
The keys were generated and stored stored on the client in the Android KeyStore using the following ..
KeyPairGenerator kpg = KeyPairGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(
alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
.setKeySize(2048)
.build());
kpg.generateKeyPair();
I tried to use the keys but login failed, the following warnings in my client logs..
W 60 [AsyncTask #1] INFO org.apache.sshd.common.io.DefaultIoServiceFactoryFactory - No detected/configured IoServiceFactoryFactory using Nio2ServiceFactoryFactory
WARN org.apache.sshd.client.keyverifier.AcceptAllServerKeyVerifier - Server at /192.168.13.4:22 presented unverified EC key: SHA256:nNMe+ZsasdILlkGIZfcLwl41ZvVTzTaEOeA
and finally the exception..
Exception: org.apache.sshd.common.SshException: No more authentication methods available
What am I doing wrong?
Edit: I tried providing a KeyPairProvider
with no luck..
try (SshClient client = SshClient.setUpDefaultClient()) {
try {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
KeyStore.Entry entry = keyStore.getEntry(alias, null);
PrivateKey privateKey = ((KeyStore.PrivateKeyEntry) entry).getPrivateKey();
PublicKey publicKey = keyStore.getCertificate(alias).getPublicKey();
final KeyPair keyPair = new KeyPair(publicKey, privateKey);
KeyPairProvider keyPairProvider = new KeyPairProvider() {
@Override
public Iterable<KeyPair> loadKeys(SessionContext session) {
return Collections.singletonList(keyPair);
}
};
client.setKeyIdentityProvider(keyPairProvider);
I also added the purposes encrypt and decrypt with no luck..
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
I was reading the docs for authorized_keys
and the supported key types are..
sk-ecdsa-sha2-nistp256@openssh.com
ecdsa-sha2-nistp256
ecdsa-sha2-nistp384
ecdsa-sha2-nistp521
sk-ssh-ed25519@openssh.com
ssh-ed25519
ssh-dss
ssh-rsa
Can the "AndroidKeyStore" create any of these key types? What makes an RSA
key an ssh-rsa
key?
From tons of research, and through a heavy fog of ignorance, I started changing the structure of my RSA key generation to be more compatible to openSSH RSA.
A huge step involved converting my RSA Public Key to the ssh-rsa format with the following..
public static byte[] encode(RSAPublicKey key)
throws IOException
{
ByteArrayOutputStream buf = new ByteArrayOutputStream();
byte[] name = "ssh-rsa".getBytes(StandardCharsets.US_ASCII);
write(name, buf);
write(key.getPublicExponent().toByteArray(), buf);
write(key.getModulus().toByteArray(), buf);
return buf.toByteArray();
}
private static void write(byte[] str, OutputStream os)
throws IOException
{
for (int shift = 24; shift >= 0; shift -= 8)
os.write((str.length >>> shift) & 0xFF);
os.write(str);
}
RSAPublicKey rsaPublicKey = (RSAPublicKey) keyStore.getCertificate(alias).getPublicKey();
byte [] opensshPublicKey = encode(rsaPublicKey);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
String b64PublicKey = Base64.getEncoder().encodeToString(opensshPublicKey);
}
Then I started getting exceptions like "INCOMPATIBLE_DIGEST" and "INCOMPATIBLE_PURPOSE".
Then, through more research and trial and error, this led me to correct my RSA key generation to the following..
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
kpg.initialize(new KeyGenParameterSpec.Builder(alias,
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1)
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setUserAuthenticationRequired(false)
.setKeySize(2048)
.build());
kpg.generateKeyPair();
Then, miracle, it authenticated correctly.