Authentication Providers

Lee Painter

SSH supports a number of different authentication types, primarily either password based or using public keys / certificates. Authentication implementations are added to a factory instance so first we must create the factory. In your configure method create and set the factory onto the SshContext.

DefaultAuthenticationMechanismFactory authFactory = new DefaultAuthenticationMechanismFactory();
sshContext.setAuthenicationMechanismFactory(authFactory);


Password Authentication


Password authentication is supported through the PasswordAuthenticationProvider abstract class. Create an implementation of this to support password authentication. The requirements are very straight forward, implement the verifyPassword method to authenticate the user. If you support password change you can throw a PasswordChangeException however this is not required. Only if you have thrown PasswordChangeException will changePassword be called.

class PasswordAuthenticationProviderImpl extends PasswordAuthenticationProvider {
@Override
public boolean changePassword(Connection con, String username,
String oldPassword, String newPassword) throws PasswordChangeException {
return false;
}

@Override public boolean verifyPassword(Connection con, String username,
String password) throws PasswordChangeException {
if(username.equals("admin") && password.equals("admin")) {
return true;
}
return false;
}
}

 

This can then be added to the factory

authFactory.addProvider(new PasswordAuthenticationProviderImpl());

 

Public Key Authentication


Public key authentication is supported through the PublicKeyAuthenticationProvider interface, again this can be added to the DefaultAuthenticationMechanismFactory to support authentication using public keys. 

The quickest and simplest way to support this is to add the AuthorizedKeysPublicKeyAuthenticationProvider class.

authFactory.addProvider(new AuthorizedKeysPublicKeyAuthenticationProvider());


This will install support for OpenSSH style public key authentication, that is, a file in the users ${HOME}/.ssh folder called authorized_keys that lists the keys authorized to authenticate the user. 

If you want to store keys or authenticate the user differently just extend AbstractPublicKeyAuthenticationProvider class and implement as a minimum the isAuthorizedKey method. This method should simply return true if the key can be used by the user of the Connection to authenticate. The API will perform validation of the key and its signature.

 

 Keyboard Interactive Authentication

 

Keyboard interactive provides a general purpose challenge-response authentication mechanism. Many systems use this to provide password authentication too but it can be configured for other types of access.

To configure your server so that is supports password over keyboard interactive add the following code. Here we are using the PasswordAuthenticationProviderImpl shown above so that password and keyboard-interactive are essentially using the same authentication provider. You can pass multiple PasswordAuthenticationProvider implementations through to handle multiple different sources or types of password authentication.

 

authFactory.addProvider(new KeyboardInteractiveAuthenticationProvider() {
@Override
public KeyboardInteractiveProvider createInstance(Connection con) {
return new PasswordKeyboardInteractiveProvider(
new PasswordAuthenticationProvider[] { new PasswordAuthenticationProviderImpl() }, con);
}
});

 

If you want to provide your own challenge-response mechanism you simply implement your own KeyboardInteractiveProvider.

public interface KeyboardInteractiveProvider {
KBIPrompt[] init(Connection con, KeyboardInteractiveAuthentication provider);
KBIPrompt[] setResponse(String[] answers);
String getName();
String getInstruction();
boolean hasAuthenticated();
}


With this interface, from the init method you return a set of KBIPrompts that should be presented to the user. The users response will be provided back to you by the system by calling your setResponse method. If further prompts/information is required from the user then return them in setResponse this cycle will continue until you return null from  setResponse indicating no further prompts are required. 

You indicate whether authentication succeeded or failed by returning a boolean result from your hasAuthenticated method.