7

Chciałbym utworzyć aplikację, która łączy się z serwerem. Ten serwer używa uwierzytelniania klienta SSL. Użytkownik aplikacji powinien mieć możliwość wybrania certyfikatu i zezwolenia na jego użycie, tak jak jest to zaimplementowane w aplikacji przeglądarki.Uwierzytelnianie klienta SSL w systemie Android 4.x

W aplikacji przeglądarki uwierzytelnianie działa zgodnie z oczekiwaniami, więc używany certyfikat jest ważny.

Kiedy próbuję połączyć w mojej aplikacji pojawia się następujący błąd:

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) 

Próbowałem śledzić android dokumentacji dla mojej realizacji.

Oto kod mojego próbki Aktywny:

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; 
} 
} 

jest wyjątek na urlConnection.getInputStream();

Oto przechwytywania uzgadniania między serwerem a c Lient. Network capture of the SSL Handshake

Dzięki za wszelkie sugestie i wskazówki.

Odpowiedz

4

Nigdy nie inicjujesz KeyManagera za pomocą swojego klucza prywatnego, więc nie ma możliwości, aby uwierzytelnianie klienta mogło go odebrać.

Będziesz musiał zaimplementować X509KeyManager, aby zwrócić klucz prywatny i kilka zakodowanych aliasów. Oto jeden z zasobów aplikacji e-mail (ICS +) w celach informacyjnych. Być może będziesz musiał go nieco zmodyfikować, ale powinno być łatwe do naśladowania: w zasadzie po prostu zapisuje klucz, alias i łańcuch certyfikatów do pól i zwraca je za pomocą odpowiednich metod (StubKeyManager po prostu zgłasza wyjątki dla metod niezatwierdzonych i niepotrzebnych):

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

Dzięki za tę wiadomość. Ale jak mogę utworzyć taki KeyManager z moim kluczem prywatnym? Ta część brakuje w przykładzie na [Android Doc for HttpsURLConnection] (http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html) – surffan

+0

Nie wydaje się, aby można było zrobić to bezpośrednio to. Będziesz musiał zaimplementować 'X509KeyManager', aby zwrócić' PrivateKey' i trochę zakodowanego aliasu. Nie trzeba wdrażać metod związanych z serwerem. http://developer.android.com/reference/javax/net/ssl/X509KeyManager.html –

+0

Zobacz zaktualizowaną odpowiedź na przykładowy kod. –

Powiązane problemy