7

Vorrei creare un'app che si connetta a un server. Questo server utilizza l'autenticazione client SSL. L'utente dell'app dovrebbe essere in grado di scegliere il certificato e consentirne l'utilizzo come se fosse implementato nell'app browser.Autenticazione client SSL in Android 4.x

Nell'app browser l'autenticazione funziona come previsto, quindi il certificato che utilizzo è valido.

Quando provo a collegare nella mia app ottengo il seguente errore:

javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: 
SSL handshake terminated: ssl=0x2a2d3b38: 
Failure in SSL library, usually a protocol error 
error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure 
(external/openssl/ssl/s3_pkt.c:1290 0x2a2df880:0x00000003) 

ho cercato di seguire la documentazione di Android per la mia realizzazione.

Ecco il codice del mio campione attività:

public class ClientCertificateActivity extends Activity implements 
    KeyChainAliasCallback { 

protected static final String TAG = "CERT_TEST"; 
private String alias; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    choseCertificate(); 
    LinearLayout layout = new LinearLayout(this); 
    Button connectToServer = new Button(this); 
    connectToServer.setText("Try to connect to Server"); 
    connectToServer.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); 
    connectToServer.setOnClickListener(new OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      connectToServer(); 
     } 
    }); 
    layout.addView(connectToServer); 
    addContentView(layout, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); 
} 

protected void connectToServer() { 
    final Context ctx = this; 
    new AsyncTask<Void, Void, Boolean>() { 

     private Exception error; 

     @Override 
     protected Boolean doInBackground(Void... arg) { 
      try { 
       PrivateKey pk = KeyChain.getPrivateKey(ctx, alias); 
       X509Certificate[] chain = KeyChain.getCertificateChain(ctx, 
         alias); 

       KeyStore keyStore = KeyStore.getInstance("AndroidCAStore"); 
       TrustManagerFactory tmf = TrustManagerFactory 
         .getInstance(TrustManagerFactory 
           .getDefaultAlgorithm()); 
       tmf.init(keyStore); 

       SSLContext context = SSLContext.getInstance("TLS"); 
       context.init(null, tmf.getTrustManagers(), null); 

       URL url = new URL("https://usecert.example.com/"); 
       HttpsURLConnection urlConnection = (HttpsURLConnection) url 
         .openConnection(); 
       urlConnection.setSSLSocketFactory(context 
         .getSocketFactory()); 
       InputStream in = urlConnection.getInputStream(); 

       return true; 
      } catch (Exception e) { 
       e.printStackTrace(); 
       error = e; 
       return false; 
      } 
     } 

     @Override 
     protected void onPostExecute(Boolean valid) { 
      if (error != null) { 
       Toast.makeText(ctx, "Error: " + error.getMessage(), 
         Toast.LENGTH_LONG).show(); 
       return; 
      } 
      Toast.makeText(ctx, "Success: ", Toast.LENGTH_SHORT).show(); 
     } 
    }.execute(); 

} 

protected void choseCertificate() { 
    KeyChain.choosePrivateKeyAlias(this, this, 
      new String[] { "RSA", "DSA" }, null, "m.ergon.ch", 443, null); 
} 

@Override 
public void alias(String alias) { 
    this.alias = alias; 
} 
} 

L'eccezione viene generata in urlConnection.getInputStream();

Ecco la cattura della stretta di mano tra il server e il c UIDA DI RIFERIMENTO. Network capture of the SSL Handshake

Grazie per eventuali suggerimenti e suggerimenti.

risposta

4

Non si inizializza mai un KeyManager con la chiave privata, quindi non è possibile che l'autenticazione del client possa raccoglierlo.

Dovresti implementare X509KeyManager per restituire il tuo PrivateKey e qualche alias hardcoded. Ecco quello dall'applicazione Email stock (ICS +) per riferimento. Potrebbe essere necessario modificarlo in qualche modo, ma dovrebbe essere facile da seguire: in pratica salva solo la chiave, l'alias e la catena di certificati nei campi e li restituisce tramite i metodi appropriati (StubKeyManager lancia solo eccezioni per i metodi non implementati e non necessari):

public static class KeyChainKeyManager extends StubKeyManager { 
    private final String mClientAlias; 
    private final X509Certificate[] mCertificateChain; 
    private final PrivateKey mPrivateKey; 

    public static KeyChainKeyManager fromAlias(Context context, String alias) 
      throws CertificateException { 
     X509Certificate[] certificateChain; 
     try { 
      certificateChain = KeyChain.getCertificateChain(context, alias); 
     } catch (KeyChainException e) { 
      logError(alias, "certificate chain", e); 
      throw new CertificateException(e); 
     } catch (InterruptedException e) { 
      logError(alias, "certificate chain", e); 
      throw new CertificateException(e); 
     } 

     PrivateKey privateKey; 
     try { 
      privateKey = KeyChain.getPrivateKey(context, alias); 
     } catch (KeyChainException e) { 
      logError(alias, "private key", e); 
      throw new CertificateException(e); 
     } catch (InterruptedException e) { 
      logError(alias, "private key", e); 
      throw new CertificateException(e); 
     } 

     if (certificateChain == null || privateKey == null) { 
      throw new CertificateException("Can't access certificate from keystore"); 
     } 

     return new KeyChainKeyManager(alias, certificateChain, privateKey); 
    } 

    private KeyChainKeyManager(
      String clientAlias, X509Certificate[] certificateChain, 
      PrivateKey privateKey) { 
     mClientAlias = clientAlias; 
     mCertificateChain = certificateChain; 
     mPrivateKey = privateKey; 
    } 


    @Override 
    public String chooseClientAlias(String[] keyTypes, Principal[] issuers, Socket socket) { 
     return mClientAlias; 
    } 

    @Override 
    public X509Certificate[] getCertificateChain(String alias) { 
      return mCertificateChain; 
    } 

    @Override 
    public PrivateKey getPrivateKey(String alias) { 
      return mPrivateKey; 
    } 
} 
+0

Grazie per quel tipp. Ma come posso creare un tale KeyManager con la mia chiave privata? Questa parte è mancante nell'esempio in [Android Doc per HttpsURLConnection] (http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html) – surffan

+0

Non sembra esserci un modo diretto per fare Questo. Dovresti implementare 'X509KeyManager' per restituire il tuo' PrivateKey' e qualche alias hard-coded. Non è necessario implementare i metodi relativi al server. http://developer.android.com/reference/javax/net/ssl/X509KeyManager.html –

+0

Vedere la risposta aggiornata per il codice di esempio. –