private ProxyLdapContext(Hashtable env) throws NamingException {
      final Map<String, Object> savedEnv = new HashMap<String, Object>();
      for (final String key :
          Arrays.asList(
              Context.SECURITY_AUTHENTICATION,
              Context.SECURITY_CREDENTIALS,
              Context.SECURITY_PRINCIPAL,
              Context.SECURITY_PROTOCOL)) {
        final Object entry = env.remove(key);
        if (entry != null) {
          savedEnv.put(key, entry);
        }
      }
      delegate = new InitialLdapContext(env, null);
      tls = (StartTlsResponse) delegate.extendedOperation(new StartTlsRequest());
      tls.setHostnameVerifier(
          new HostnameVerifier() {

            @Override
            public boolean verify(String hostname, SSLSession session) {
              return true;
            }
          });
      try {
        final SSLSession negotiate = tls.negotiate();
        Logger.getLogger(this.getClass().getCanonicalName())
            .fine("LDAP is now using " + negotiate.getProtocol());
      } catch (final IOException e) {
        throw new NamingException(e.getMessage());
      }
      for (final Map.Entry<String, Object> savedEntry : savedEnv.entrySet()) {
        delegate.addToEnvironment(savedEntry.getKey(), savedEntry.getValue());
      }
    }
  /**
   * Allow the Listener a chance to customise the request. before the server does its stuff. <br>
   * This allows the required attributes to be set for SSL requests. <br>
   * The requirements of the Servlet specs are:
   *
   * <ul>
   *   <li>an attribute named "javax.servlet.request.cipher_suite" of type String.
   *   <li>an attribute named "javax.servlet.request.key_size" of type Integer.
   *   <li>an attribute named "javax.servlet.request.X509Certificate" of type
   *       java.security.cert.X509Certificate[]. This is an array of objects of type
   *       X509Certificate, the order of this array is defined as being in ascending order of trust.
   *       The first certificate in the chain is the one set by the client, the next is the one used
   *       to authenticate the first, and so on.
   * </ul>
   *
   * @param socket The Socket the request arrived on. This should be a javax.net.ssl.SSLSocket.
   * @param request HttpRequest to be customised.
   */
  protected void customizeRequest(Socket socket, HttpRequest request) {
    super.customizeRequest(socket, request);

    if (!(socket instanceof javax.net.ssl.SSLSocket))
      return; // I'm tempted to let it throw an exception...

    try {
      SSLSocket sslSocket = (SSLSocket) socket;
      SSLSession sslSession = sslSocket.getSession();
      String cipherSuite = sslSession.getCipherSuite();
      Integer keySize;
      X509Certificate[] certs;

      CachedInfo cachedInfo = (CachedInfo) sslSession.getValue(CACHED_INFO_ATTR);
      if (cachedInfo != null) {
        keySize = cachedInfo.getKeySize();
        certs = cachedInfo.getCerts();
      } else {
        keySize = new Integer(ServletSSL.deduceKeyLength(cipherSuite));
        certs = getCertChain(sslSession);
        cachedInfo = new CachedInfo(keySize, certs);
        sslSession.putValue(CACHED_INFO_ATTR, cachedInfo);
      }

      if (certs != null) request.setAttribute("javax.servlet.request.X509Certificate", certs);
      else if (_needClientAuth) // Sanity check
      throw new HttpException(HttpResponse.__403_Forbidden);

      request.setAttribute("javax.servlet.request.cipher_suite", cipherSuite);
      request.setAttribute("javax.servlet.request.key_size", keySize);
    } catch (Exception e) {
      log.warn(LogSupport.EXCEPTION, e);
    }
  }
Beispiel #3
0
 public Certificate[] getLocalCertificates() {
   SSLSession sslSession = (SSLSession) ioSession.getAttribute(SslFilter.SSL_SESSION);
   if (sslSession != null) {
     return sslSession.getLocalCertificates();
   }
   return new Certificate[0];
 }
  @Override
  public Object[] getPeerCertificateChain(boolean force) throws IOException, RemoteException {
    try {
      // Look up the current SSLSession
      if (session == null) return null;

      // Convert JSSE's certificate format to the ones we need
      X509Certificate[] jsseCerts = null;
      try {
        jsseCerts = session.getPeerCertificateChain();
      } catch (Exception bex) {
        // ignore.
      }
      if (jsseCerts == null) jsseCerts = new X509Certificate[0];
      if (jsseCerts.length <= 0 && force && ssl != null) {
        session.invalidate();
        handShake();
        session = ssl.getSession();
      }
      return getX509Certificates(session);
    } catch (Exception excp) {
      excp.printStackTrace();
    }
    return null;
  }
  /*
   * Create and size the buffers appropriately.
   */
  private void createBuffers() {

    /*
     * We'll assume the buffer sizes are the same
     * between client and server.
     */
    SSLSession session = clientEngine.getSession();
    int appBufferMax = session.getApplicationBufferSize();
    int netBufferMax = session.getPacketBufferSize();

    /*
     * We'll make the input buffers a bit bigger than the max needed
     * size, so that unwrap()s following a successful data transfer
     * won't generate BUFFER_OVERFLOWS.
     *
     * We'll use a mix of direct and indirect ByteBuffers for
     * tutorial purposes only.  In reality, only use direct
     * ByteBuffers when they give a clear performance enhancement.
     */
    clientIn = ByteBuffer.allocate(appBufferMax + 50);
    serverIn = ByteBuffer.allocate(appBufferMax + 50);

    cTOs = ByteBuffer.allocateDirect(netBufferMax);
    sTOc = ByteBuffer.allocateDirect(netBufferMax);

    clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
    serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
  }
Beispiel #6
0
 private static void printConnectionInfo(SSLSocket s) {
   SSLSession currentSession = s.getSession();
   System.out.println("Protocol: " + currentSession.getProtocol());
   System.out.println("Cipher Suite: " + currentSession.getCipherSuite());
   System.out.println("Host: " + currentSession.getPeerHost());
   System.out.println("Host Port: " + currentSession.getPeerPort());
 }
  public static void main(String[] args) throws Exception {
    SSLContext sslContext = createSSLContext();
    SSLServerSocketFactory fact = sslContext.getServerSocketFactory();
    SSLServerSocket sSock = (SSLServerSocket) fact.createServerSocket(Utils.PORT_NO);

    // client authenticate where possible
    sSock.setWantClientAuth(true);

    for (; ; ) {
      SSLSocket sslSock = (SSLSocket) sSock.accept();

      try {
        sslSock.startHandshake();
      } catch (IOException e) {
        continue;
      }

      readRequest(sslSock.getInputStream());

      SSLSession session = sslSock.getSession();

      try {
        Principal clientID = session.getPeerPrincipal();

        System.out.println("client identified as: " + clientID);
      } catch (SSLPeerUnverifiedException e) {
        System.out.println("client not authenticated");
      }

      sendResponse(sslSock.getOutputStream());

      sslSock.close();
    }
  }
  private void createBuffers() {

    SSLSession session = sslStateMachine.sslEngine.getSession();
    appBufferMax = session.getApplicationBufferSize();
    netBufferMax = session.getPacketBufferSize();

    if (logger.isLoggingEnabled(LogWriter.TRACE_DEBUG)) {
      logger.logDebug("appBufferMax=" + appBufferMax + " netBufferMax=" + netBufferMax);
    }
  }
  // Server identity checking is done according to RFC 2818: HTTP over TLS
  // Section 3.1 Server Identity
  private void checkURLSpoofing(HostnameVerifier hostnameVerifier) throws IOException {
    //
    // Get authenticated server name, if any
    //
    boolean done = false;
    String host = url.getHost();

    // if IPv6 strip off the "[]"
    if (host != null && host.startsWith("[") && host.endsWith("]")) {
      host = host.substring(1, host.length() - 1);
    }

    Certificate[] peerCerts = null;
    try {
      // get the subject's certificate
      peerCerts = session.getPeerCertificates();

      X509Certificate peerCert;
      if (peerCerts[0] instanceof java.security.cert.X509Certificate) {
        peerCert = (java.security.cert.X509Certificate) peerCerts[0];
      } else {
        throw new SSLPeerUnverifiedException("");
      }

      HostnameChecker checker = HostnameChecker.getInstance(HostnameChecker.TYPE_TLS);

      checker.match(host, peerCert);

      // if it doesn't throw an exception, we passed. Return.
      return;

    } catch (SSLPeerUnverifiedException e) {

      //
      // client explicitly changed default policy and enabled
      // anonymous ciphers; we can't check the standard policy
      //
      // ignore
    } catch (java.security.cert.CertificateException cpe) {
      // ignore
    }

    // Assume the peerCerts are already cloned.
    String cipher = session.getCipherSuite();
    if ((cipher != null) && (cipher.indexOf("_anon_") != -1)) {
      return;
    } else if ((hostnameVerifier != null) && (hostnameVerifier.verify(host, session))) {
      return;
    }

    serverSocket.close();
    session.invalidate();

    throw new IOException("HTTPS hostname wrong:  should be <" + url.getHost() + ">");
  }
Beispiel #10
0
 public Certificate[] getPeerCertificates() {
   try {
     SSLSession sslSession = (SSLSession) ioSession.getAttribute(SslFilter.SSL_SESSION);
     if (sslSession != null) {
       return sslSession.getPeerCertificates();
     }
   } catch (SSLPeerUnverifiedException e) {
     Log.warn("Error retrieving client certificates of: " + session, e);
   }
   return new Certificate[0];
 }
Beispiel #11
0
 private static void printSocketInfo(SSLSocket s) {
   LOGGER.info("Socket class: " + s.getClass());
   LOGGER.info("   Remote address = " + s.getInetAddress().toString());
   LOGGER.info("   Remote port = " + s.getPort());
   LOGGER.info("   Local socket address = " + s.getLocalSocketAddress().toString());
   LOGGER.info("   Local address = " + s.getLocalAddress().toString());
   LOGGER.info("   Local port = " + s.getLocalPort());
   LOGGER.info("   Need client authentication = " + s.getNeedClientAuth());
   SSLSession ss = s.getSession();
   LOGGER.info("   Cipher suite = " + ss.getCipherSuite());
   LOGGER.info("   Protocol = " + ss.getProtocol());
 }
  @Override
  public void handshake(Socket sock) throws IOException {
    // We do getSession instead of startHandshake() so we can call this multiple times
    SSLSession session = ((SSLSocket) sock).getSession();
    if (session.getCipherSuite().equals("SSL_NULL_WITH_NULL_NULL"))
      throw new IOException(
          "SSL handshake failed. Ciper suite in SSL Session is SSL_NULL_WITH_NULL_NULL");

    if (!allowUnsafeLegacyRenegotiation && !rfc5746Supported) {
      // Prevent further handshakes by removing all cipher suites
      ((SSLSocket) sock).setEnabledCipherSuites(new String[0]);
    }
  }
 public SSLSocketChannelWrapper(SSLContext sslContext, SocketChannel sc, boolean client)
     throws Exception {
   super(sc);
   sslEngine = sslContext.createSSLEngine();
   sslEngine.setUseClientMode(client);
   sslEngine.setEnableSessionCreation(true);
   SSLSession session = sslEngine.getSession();
   in = ByteBuffer.allocate(64 * 1024);
   emptyBuffer = ByteBuffer.allocate(0);
   int netBufferMax = session.getPacketBufferSize();
   netOutBuffer = ByteBuffer.allocate(netBufferMax);
   netInBuffer = ByteBuffer.allocate(netBufferMax);
 }
  @Override
  public Socket createSocket(Socket plainSocket, String host, int port, boolean autoClose)
      throws IOException {
    String peerHost = this.conn.getRequestProperty("Host");
    if (peerHost == null) peerHost = host;
    Log.i(TAG, "customized createSocket. host: " + peerHost);
    InetAddress address = plainSocket.getInetAddress();
    if (autoClose) {
      // we don't need the plainSocket
      plainSocket.close();
    }
    // create and connect SSL socket, but don't do hostname/certificate verification yet
    SSLCertificateSocketFactory sslSocketFactory =
        (SSLCertificateSocketFactory) SSLCertificateSocketFactory.getDefault(0);
    SSLSocket ssl = (SSLSocket) sslSocketFactory.createSocket(address, port);

    // enable TLSv1.1/1.2 if available
    ssl.setEnabledProtocols(ssl.getSupportedProtocols());

    // set up SNI before the handshake
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      Log.i(TAG, "Setting SNI hostname");
      sslSocketFactory.setHostname(ssl, peerHost);
    } else {
      Log.d(TAG, "No documented SNI support on Android <4.2, trying with reflection");
      try {
        java.lang.reflect.Method setHostnameMethod =
            ssl.getClass().getMethod("setHostname", String.class);
        setHostnameMethod.invoke(ssl, peerHost);
      } catch (Exception e) {
        Log.w(TAG, "SNI not useable", e);
      }
    }

    // verify hostname and certificate
    SSLSession session = ssl.getSession();

    if (!hostnameVerifier.verify(peerHost, session))
      throw new SSLPeerUnverifiedException("Cannot verify hostname: " + peerHost);

    Log.i(
        TAG,
        "Established "
            + session.getProtocol()
            + " connection with "
            + session.getPeerHost()
            + " using "
            + session.getCipherSuite());

    return ssl;
  }
  public boolean verify(String hostName, SSLSession session) {
    System.out.println("Server: " + hostName + ":" + session.getPeerPort());
    try {
      X509Certificate[] chain = session.getPeerCertificateChain();
      for (X509Certificate cert : chain) {

        System.out.println("DN: " + cert.getSubjectDN());
      }
    } catch (SSLPeerUnverifiedException e) {
      e.printStackTrace();
    }
    System.out.println("-----");
    return true;
  }
Beispiel #16
0
  /**
   * Close the {@link SSLEngine} attached to this channel
   *
   * @throws Exception
   */
  private void handleClose() throws Exception {
    if (sslEngine.isOutboundDone()) {
      return;
    }
    sslEngine.closeOutbound();

    SSLSession session = getSSLSession();
    int packetBufferSize = Math.max(session.getPacketBufferSize(), MIN_BUFFER_SIZE);

    this.netOutBuffer =
        (this.netOutBuffer == null)
            ? ByteBuffer.allocateDirect(packetBufferSize)
            : this.netOutBuffer.compact();
    this.netInBuffer =
        (this.netInBuffer == null)
            ? ByteBuffer.allocateDirect(packetBufferSize)
            : this.netInBuffer.compact();

    while (!sslEngine.isOutboundDone()) {
      // Get close message
      SSLEngineResult res = sslEngine.wrap(this.netInBuffer, this.netOutBuffer);

      switch (res.getStatus()) {
        case OK:
          // Execute tasks if we need to
          tryTasks();
          while (this.netOutBuffer.hasRemaining()) {
            if (this.channel.write(this.netOutBuffer).get() < 0) {
              break;
            }
            this.netOutBuffer.compact();
          }
          break;
        case BUFFER_OVERFLOW:
          ByteBuffer tmp =
              ByteBuffer.allocateDirect(packetBufferSize + this.netOutBuffer.capacity());
          this.netOutBuffer.flip();
          tmp.put(this.netOutBuffer);
          this.netOutBuffer = tmp;

          break;
        case BUFFER_UNDERFLOW:
          // Cannot happens in case of wrap
        case CLOSED:
          // Already closed, so return
          break;
      }
    }
  }
  /** javax.net.ssl.SSLSession#getValue(String name) */
  public void test_getValue() {
    SSLSession s = clientSession;
    mySSLSessionBindingListener sbl = new mySSLSessionBindingListener();

    try {
      s.getValue(null);
      fail("IllegalArgumentException wasn't thrown");
    } catch (IllegalArgumentException expected) {
      // expected
    }

    s.putValue("Name", sbl);
    Object obj = s.getValue("Name");
    assertTrue(obj instanceof SSLSessionBindingListener);
  }
  /** Copied from <code>org.apache.catalina.valves.CertificateValve</code> */
  @Override
  public Integer getKeySize() throws IOException, RemoteException {
    try {
      // Look up the current SSLSession
      org.apache.tomcat.util.net.SSLSupportCipherDataRemoteInterface c_aux[] = ciphers;
      if (session == null) return null;

      Integer keySize = null;
      synchronized (keySizeCache) {
        keySize = keySizeCache.get(session);
      }

      if (keySize == null) {
        int size = 0;
        String cipherSuite = session.getCipherSuite();
        for (int i = 0; i < c_aux.length; i++) {
          if (cipherSuite.indexOf(c_aux[i].getPhrase()) >= 0) {
            size = c_aux[i].getKeySize();
            break;
          }
        }
        keySize = Integer.valueOf(size);
        synchronized (keySizeCache) {
          keySizeCache.put(session, keySize);
        }
      }
      return keySize;
    } catch (Exception excp) {
      excp.printStackTrace();
    }
    return null;
  }
 protected boolean verify(String hostname, SSLSession session, boolean interactive) {
   LOGGER.log(
       Level.FINE, "hostname verifier for " + hostname + ", trying default verifier first");
   // if the default verifier accepts the hostname, we are done
   if (defaultVerifier.verify(hostname, session)) {
     LOGGER.log(Level.FINE, "default verifier accepted " + hostname);
     return true;
   }
   // otherwise, we check if the hostname is an alias for this cert in our keystore
   try {
     X509Certificate cert = (X509Certificate) session.getPeerCertificates()[0];
     // Log.d(TAG, "cert: " + cert);
     if (cert.equals(appKeyStore.getCertificate(hostname.toLowerCase(Locale.US)))) {
       LOGGER.log(Level.FINE, "certificate for " + hostname + " is in our keystore. accepting.");
       return true;
     } else {
       LOGGER.log(
           Level.FINE, "server " + hostname + " provided wrong certificate, asking user.");
       if (interactive) {
         return interactHostname(cert, hostname);
       } else {
         return false;
       }
     }
   } catch (Exception e) {
     e.printStackTrace();
     return false;
   }
 }
 /** Invalidate the session this support object is associated with. */
 @Override
 public void invalidateSession() throws RemoteException {
   try {
     session.invalidate();
   } catch (Exception excp) {
     excp.printStackTrace();
   }
 }
  @Override
  public void handshakeCompleted(HandshakeCompletedEvent event) {
    SSLSession session = event.getSession();
    String protocol = session.getProtocol();
    String cipherSuite = session.getCipherSuite();
    String peerName = null;

    try {
      peerName = session.getPeerPrincipal().getName();
      Log.d(TAG, "peerName: " + peerName);
    } catch (SSLPeerUnverifiedException e) {
      e.printStackTrace();
    }
    Log.d(TAG, "session: " + session);
    Log.d(TAG, "protocol: " + protocol);
    Log.d(TAG, "cipherSuite: " + cipherSuite);
  }
 public final void verify(String s, SSLSocket sslsocket) throws IOException {
   if (s == null) {
     throw new NullPointerException("host to verify is null");
   }
   SSLSession sslsession1 = sslsocket.getSession();
   SSLSession sslsession = sslsession1;
   if (sslsession1 == null) {
     sslsocket.getInputStream().available();
     SSLSession sslsession2 = sslsocket.getSession();
     sslsession = sslsession2;
     if (sslsession2 == null) {
       sslsocket.startHandshake();
       sslsession = sslsocket.getSession();
     }
   }
   verify(s, (X509Certificate) sslsession.getPeerCertificates()[0]);
 }
 public final boolean verify(String s, SSLSession sslsession) {
   try {
     verify(s, (X509Certificate) sslsession.getPeerCertificates()[0]);
   }
   // Misplaced declaration of an exception variable
   catch (String s) {
     return false;
   }
   return true;
 }
Beispiel #24
0
 /** Bypass host name verification */
 public boolean verify(String hostname, SSLSession session) {
   System.out.println("Bypassing verification of hostname: " + hostname);
   try {
     System.out.println("Peer principal: " + session.getPeerPrincipal().toString());
   } catch (SSLPeerUnverifiedException e) {
     System.err.println("Unable to get peer principal");
     e.printStackTrace();
   }
   return true;
 }
  public boolean verify(String hostname, SSLSession session) {
    if (trustAllServerCerts) {
      return true;
    }

    boolean approve = true;
    X509Certificate peercert = null;
    String cn = null;

    try {
      X509Certificate[] peercerts = (X509Certificate[]) session.getPeerCertificates();
      peercert = peercerts[0];
      String subjectDN = peercert.getSubjectDN().getName();
      cn = new X500Name(subjectDN).getCommonName();
    } catch (Exception ex) {
      debug.error("AMHostnameVerifier:" + ex.toString());
    }

    if (cn == null) return false;

    if (!sslTrustHosts.isEmpty()) {
      if (sslTrustHosts.contains(cn.toLowerCase())) {
        return true;
      }
    }

    if (resolveIPAddress) {
      try {
        approve =
            InetAddress.getByName(cn)
                .getHostAddress()
                .equals(InetAddress.getByName(hostname).getHostAddress());
      } catch (UnknownHostException ex) {
        if (debug.messageEnabled()) {
          debug.message("AMHostnameVerifier:", ex);
        }
        approve = false;
      }
    } else {
      approve = false;
    }

    if (checkSubjectAltName && !approve) {
      try {
        Iterator i = (Iterator) peercert.getSubjectAlternativeNames().iterator();
        for (; !approve && i.hasNext(); ) {
          approve = compareHosts((GeneralName) i.next(), hostname);
        }
      } catch (Exception ex) {
        return false;
      }
    }

    return approve;
  }
 /**
  * Wraps an ssl socket over an existing socket and compares the host name from the address to the
  * common name in the server certificate.
  *
  * @param bareSocket plain socket connected to the server
  * @param address destination of the <tt>bareSocket</tt>
  * @param port destination of the <tt>bareSocket</tt>
  * @return SSL socket wrapped around original socket with server identity verified
  */
 private SSLSocket sslWrap(Socket bareSocket, InetAddress address, int port) throws IOException {
   final String hostName = address.getHostName();
   final SSLSocket sslSocket =
       (SSLSocket) m_factory.createSocket(bareSocket, hostName, port, true);
   sslSocket.startHandshake();
   final SSLSession session = sslSocket.getSession();
   final String DN = session.getPeerCertificateChain()[0].getSubjectDN().getName();
   final String CN = getCN(DN);
   if (!hostName.equals(CN)) {
     final String message = "Host name mismatch, expected '" + hostName + "' recevied DN is " + DN;
     throw new IOException(message);
   }
   if (getLogger().isDebugEnabled()) {
     final String message = "DN of the server " + DN;
     getLogger().debug(message);
     final String message2 = "Session id " + bytesToString(session.getId());
     getLogger().debug(message2);
   }
   return sslSocket;
 }
Beispiel #27
0
 @Override
 public boolean verify(String hostname, SSLSession session) {
   X509Certificate peerCertificate;
   try {
     peerCertificate = (X509Certificate) session.getPeerCertificates()[0];
   } catch (SSLPeerUnverifiedException e) {
     throw new IllegalStateException("The session does not contain a peer X.509 certificate.");
   }
   String peerCertificateCN = getCommonName(peerCertificate);
   return hostname.equals(peerCertificateCN);
 }
 @Override
 public String getCipherSuite() throws IOException, RemoteException {
   try {
     // Look up the current SSLSession
     if (session == null) return null;
     return session.getCipherSuite();
   } catch (Exception excp) {
     excp.printStackTrace();
   }
   return null;
 }
  public void handShake() throws IOException, RemoteException {
    try {
      if (ssl.getWantClientAuth()) {
        log.debug(sm.getString("jsseSupport.noCertWant"));
      } else {
        ssl.setNeedClientAuth(true);
      }

      if (ssl.getEnabledCipherSuites().length == 0) {
        // Handshake is never going to be successful.
        // Assume this is because handshakes are disabled
        log.warn(sm.getString("jsseSupport.serverRenegDisabled"));
        session.invalidate();
        ssl.close();
        return;
      }

      InputStream in = ssl.getInputStream();
      int oldTimeout = ssl.getSoTimeout();
      ssl.setSoTimeout(1000);
      byte[] b = new byte[1];
      listener.reset();
      ssl.startHandshake();
      int maxTries = 60; // 60 * 1000 = example 1 minute time out
      for (int i = 0; i < maxTries; i++) {
        if (log.isTraceEnabled()) log.trace("Reading for try #" + i);
        try {
          int read = in.read(b);
          if (read > 0) {
            // Shouldn't happen as all input should have been swallowed
            // before trying to do the handshake. If it does, something
            // went wrong so lets bomb out now.
            throw new SSLException(sm.getString("jsseSupport.unexpectedData"));
          }
        } catch (SSLException sslex) {
          log.info(sm.getString("jsseSupport.clientCertError"), sslex);
          throw sslex;
        } catch (IOException e) {
          // ignore - presumably the timeout
        }
        if (listener.isCompleted()) {
          break;
        }
      }
      ssl.setSoTimeout(oldTimeout);
      if (listener.isCompleted() == false) {
        throw new SocketException("SSL Cert handshake timeout");
      }

    } catch (Exception excp) {
      excp.printStackTrace();
    }
  }
  @SuppressLint("NewApi")
  private Socket enableSNI(SSLSocket ssl, String host) throws SSLPeerUnverifiedException {
    // enable TLSv1.1/1.2 if available
    // (see https://github.com/rfc2822/davdroid/issues/229)
    ssl.setEnabledProtocols(ssl.getSupportedProtocols());

    // set up SNI before the handshake
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      AppLog.i(T.API, "Setting SNI hostname");
      mFactory.setHostname(ssl, host);
    } else {
      AppLog.i(T.API, "No documented SNI support on Android <4.2, trying with reflection");
      try {
        java.lang.reflect.Method setHostnameMethod =
            ssl.getClass().getMethod("setHostname", String.class);
        setHostnameMethod.invoke(ssl, host);
      } catch (Exception e) {
        AppLog.e(T.API, "SNI not useable", e);
      }
    }

    // verify hostname and certificate
    SSLSession session = ssl.getSession();
    if (!mHostnameVerifier.verify(host, session)) {
      throw new SSLPeerUnverifiedException("Cannot verify hostname: " + host);
    }

    AppLog.i(
        T.API,
        "Established "
            + session.getProtocol()
            + " connection with "
            + session.getPeerHost()
            + " using "
            + session.getCipherSuite());

    return ssl;
  }