Estoy trabajando en una aplicación de Android que requiere autenticación de certificado de cliente y servidor. Tengo una clase SSLClient que creé que funciona muy bien en el escritorio regular de Java SE 6. Lo he movido a mi proyecto de Android y me aparece el siguiente error: "Implementación de KeyStore JKS no encontrada".Uso de certificados de cliente/servidor para autenticación bidireccional Socket SSL en Android
He buscado en línea un poco y parece que existe la posibilidad de que Java Keystores no sean compatibles con Android (¡increíble!) Pero tengo la sensación de que hay más que eso porque ninguno de los códigos de muestra que he encontrado se asemeja a lo que estoy tratando de hacer en absoluto. Todo lo que encontré habla sobre el uso de un cliente http en lugar de sockets SSL sin formato. Necesito conectores SSL para esta aplicación.
A continuación se muestra el código en mi archivo SSLClient.java. Lee el almacén de claves y el almacén de confianza, crea una conexión de socket SSL con el servidor, luego ejecuta un ciclo mientras espera las líneas de entrada del servidor y luego las maneja cuando ingresan llamando a un método en una clase diferente. Estoy muy interesado en saber de alguien con experiencia en SSL en la plataforma Android.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.security.AccessControlException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import otherpackege.OtherClass;
import android.content.Context;
import android.util.Log;
public class SSLClient
{
static SSLContext ssl_ctx;
public SSLClient(Context context)
{
try
{
// Setup truststore
KeyStore trustStore = KeyStore.getInstance("BKS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
InputStream trustStoreStream = context.getResources().openRawResource(R.raw.mysrvtruststore);
trustStore.load(trustStoreStream, "testtest".toCharArray());
trustManagerFactory.init(trustStore);
// Setup keystore
KeyStore keyStore = KeyStore.getInstance("BKS");
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
InputStream keyStoreStream = context.getResources().openRawResource(R.raw.clientkeystore);
keyStore.load(keyStoreStream, "testtest".toCharArray());
keyManagerFactory.init(keyStore, "testtest".toCharArray());
Log.d("SSL", "Key " + keyStore.size());
Log.d("SSL", "Trust " + trustStore.size());
// Setup the SSL context to use the truststore and keystore
ssl_ctx = SSLContext.getInstance("TLS");
ssl_ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
Log.d("SSL", "keyManagerFactory " + keyManagerFactory.getKeyManagers().length);
Log.d("SSL", "trustManagerFactory " + trustManagerFactory.getTrustManagers().length);
}
catch (NoSuchAlgorithmException nsae)
{
Log.d("SSL", nsae.getMessage());
}
catch (KeyStoreException kse)
{
Log.d("SSL", kse.getMessage());
}
catch (IOException ioe)
{
Log.d("SSL", ioe.getMessage());
}
catch (CertificateException ce)
{
Log.d("SSL", ce.getMessage());
}
catch (KeyManagementException kme)
{
Log.d("SSL", kme.getMessage());
}
catch(AccessControlException ace)
{
Log.d("SSL", ace.getMessage());
}
catch(UnrecoverableKeyException uke)
{
Log.d("SSL", uke.getMessage());
}
try
{
Handler handler = new Handler();
handler.start();
}
catch (IOException ioException)
{
ioException.printStackTrace();
}
}
}
//class Handler implements Runnable
class Handler extends Thread
{
private SSLSocket socket;
private BufferedReader input;
static public PrintWriter output;
private String serverUrl = "174.61.103.206";
private String serverPort = "6000";
Handler(SSLSocket socket) throws IOException
{
}
Handler() throws IOException
{
}
public void sendMessagameInfoge(String message)
{
Handler.output.println(message);
}
@Override
public void run()
{
String line;
try
{
SSLSocketFactory socketFactory = (SSLSocketFactory) SSLClient.ssl_ctx.getSocketFactory();
socket = (SSLSocket) socketFactory.createSocket(serverUrl, Integer.parseInt(serverPort));
this.input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
Handler.output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
Log.d("SSL", "Created the socket, input, and output!!");
do
{
line = input.readLine();
while (line == null)
{
line = input.readLine();
}
// Parse the message and do something with it
// Done in a different class
OtherClass.parseMessageString(line);
}
while (!line.equals("exit|"));
}
catch (IOException ioe)
{
System.out.println(ioe);
}
finally
{
try
{
input.close();
output.close();
socket.close();
}
catch(IOException ioe)
{
}
finally
{
}
}
}
}
Actualización:
realizó algunos avances en este problema. Descubrí que JKS de hecho no es compatible, tampoco está eligiendo directamente el tipo SunX509. He actualizado mi código anterior para reflejar estos cambios. Todavía estoy teniendo un problema aparentemente sin cargar el almacén de claves y el almacén de confianza. Actualizaré mientras descubro más.
Update2:
estaba haciendo mi almacén de claves y la carga de archivos en un almacén de confianza de Java Desktop manera en lugar de la forma correcta Android. Los archivos deben colocarse en la carpeta res/raw y cargarse usando getResources(). Ahora recibo un recuento de 1 y 1 para el almacén de claves y el tamaño del almacén de confianza, lo que significa que se están cargando. Todavía estoy chocando con una excepción, ¡pero cada vez más cerca! Lo actualizaré cuando lo haga funcionar.
Update3:
Parece que todo está funcionando ahora con la excepción de mi almacén de claves está configurado incorrectamente. Si deshabilito la autenticación del lado del cliente en el servidor, se conecta sin problemas. Cuando lo dejo habilitado, recibo un error handling exception: javax.net.ssl.SSLHandshakeException: null cert chain
. Entonces parece que no estoy configurando la cadena de certificados correctamente. He publicado otra pregunta preguntando cómo crear un almacén de claves del cliente en el formato BKS con la cadena de certificados adecuada: How to create a BKS (BouncyCastle) format Java Keystore that contains a client certificate chain
No funciona en Android 2.2 (funciona en 2.3) – CelinHC
Estoy seguro de que este código funciona en Android 1.6+. Se usa en nuestra aplicación anterior, que está en el mercado de Android desde hace algunos años. Tal vez he editado algo pero, en general, debería funcionar. – peceps
Ya no trabajo en este proyecto, por lo que no he confirmado personalmente esta respuesta, pero la acepto porque contiene información de reproducción detallada, y según los votos, parece ser una solución funcional. Gracias. –