Java >> Java Tutorial >  >> Java

So fügen Sie SSLContext mit TLSv1.2 in Android Kitkat hinzu

Kürzlich bin ich auf ein Problem gestoßen, bei dem ein Server in meiner Android-Anwendung, die in der Kitkat-Version von Android ausgeführt wird, nicht reagiert hat. Ja, Kitkat ist alt und warum sollte man es verwenden, wenn wir immer auf eine höhere Version von Android upgraden können. Wir können streiten, Vor- und Nachteile finden, aber darum geht es nicht. Wenn wir Technologie haben, sollte sie flexibler sein und manchmal wird ein Kunde nicht in der Lage sein, sich für eine höhere Version von Android zu entscheiden. Wenn die Technologie mit der Zeit spielt, haben bestimmte Dinge keine Kontrolle. Wenn also Websites TLSv1.2 mit der Veröffentlichung von Android Kitkat zur Pflicht gemacht hatten, blieb Google nichts anderes übrig, als eine Lösung zu veröffentlichen. Aber das war nicht der Fall.

Zurück in die Gegenwart versucht meine Android-Anwendung, einen Server zu kontaktieren, auf dem TLSv1.2 aktiviert ist. In meiner Android-Anwendung habe ich von Android bereitgestellten DefaultHttpClient verwendet .

Das Problem ist also „Wie fügen wir diesem DefaultHttpClient einen SSLContext hinzu „?

Lösung –

Erstellen Sie eine HTTP-Client-Socket-Factory –

Wir werden eine Socket-Factory bauen, die LayeredSocketFactory implementiert wie unten:

public class TlsSniSocketFactory implements LayeredSocketFactory {

  private final static HostnameVerifier hostnameVerifier = new StrictHostnameVerifier();

  private final boolean acceptAllCertificates;
  private final String selfSignedCertificateKey;

  public TlsSniSocketFactory() 
        {
    this.acceptAllCertificates = false;
    this.selfSignedCertificateKey = null;
  }

  public TlsSniSocketFactory(String certKey) 
        {
    this.acceptAllCertificates = false;
    this.selfSignedCertificateKey = certKey;
  }

  public TlsSniSocketFactory(boolean acceptAllCertificates) 
        {
    this.acceptAllCertificates = acceptAllCertificates;
    this.selfSignedCertificateKey = null;
  }

  // Plain TCP/IP (layer below TLS)

  @Override
  public Socket connectSocket(Socket s, String host, int port, InetAddress localAddress, int localPort,
                HttpParams params) throws IOException {
    return null;
  }

  @Override
  public Socket createSocket() throws IOException {
    return null;
  }

  @Override
  public boolean isSecure(Socket s) throws IllegalArgumentException {
    if (s instanceof SSLSocket) {
      return s.isConnected();
    }
    return false;
  }

  // TLS layer

  @Override	
  public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose) throws IOException 
        {
    if (autoClose) 
                {
      plainSocket.close();
    }

    SSLCertificateSocketFactory sslSocketFactory =
        (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);

    // For self-signed certificates use a custom trust manager
    if (acceptAllCertificates) {
      sslSocketFactory.setTrustManagers(new TrustManager[]{new IgnoreSSLTrustManager()});
    } else if (selfSignedCertificateKey != null) {
      sslSocketFactory.setTrustManagers(new TrustManager[]{new SelfSignedTrustManager(selfSignedCertificateKey)});
    }

    // create and connect SSL socket, but don't do hostname/certificate verification yet
    SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(InetAddress.getByName(host), port);

    // enable TLSv1.1/1.2 if available
    // ssl.setEnabledProtocols(ssl.getSupportedProtocols());
                // this can be hard coded too
                ssl.setEnabledProtocols(new String[] {"TLSv1.2"});

    // set up SNI before the handshake
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      sslSocketFactory.setHostname(ssl, host);
    } else {
      try {
        java.lang.reflect.Method setHostnameMethod = ssl.getClass().getMethod("setHostname", String.class);
        setHostnameMethod.invoke(ssl, host);
      } catch (Exception e) {
        Log.d(TlsSniSocketFactory.class.getSimpleName(), "SNI not usable: " + e);
      }
    }

    // verify hostname and certificate
    SSLSession session = ssl.getSession();
    if (!(acceptAllCertificates || selfSignedCertificateKey != null) && !hostnameVerifier.verify(host, session)) {
      throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
    }

    return ssl;
  }

}

Registrieren Sie ein HTTPS-Schema

Wir werden ein Schema registrieren, das unsere benutzerdefinierte Socket-Factory verwendet.

SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(new Scheme("https", new TlsSniSocketFactory(),443));

ClientConnectionManager ccm = new ThreadSafeClientConnManager(httpparams, schemeRegistry);
DefaultHttpClient defaultHttpClient = new DefaultHttpClient(ccm, httpparams);

Nun verwenden Sie defaultHttpClient Um eine GET- oder POST-Anforderung aufzurufen, sollten wir in der Lage sein, eine Verbindung zu einem Server herzustellen, der mit TLSv1.2 aktiviert ist.

Schlussfolgerung

In diesem Beitrag haben wir gezeigt, wie man DefaultHttpClient verwendet in Android Kitkat mit TLSv1.2. Wenn Ihnen dieser Beitrag gefallen hat, abonnieren Sie hier meinen Blog.

Referenzen

  1. TLS-Socket-Factory
  2. Standard-HTTP-Client

Java-Tag