+
+ /// 2. VERIFY HOSTNAME
+ SSLSession newSession = null;
+ boolean verifiedHostname = true;
+ if (mHostnameVerifier != null) {
+ if (failInHandshake != null) {
+ /// 2.1 : a new SSLSession instance was NOT created in the handshake
+ X509Certificate serverCert = ((CertificateCombinedException)failInHandshake.getCause()).getServerCertificate();
+ try {
+ mHostnameVerifier.verify(host, serverCert);
+ } catch (SSLException e) {
+ verifiedHostname = false;
+ }
+
+ } else {
+ /// 2.2 : a new SSLSession instance was created in the handshake
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB) {
+ /// this is sure ONLY for Android >= 3.0 ; the same is true for mHostnameVerifier.verify(host, (SSLSocket)socket)
+ newSession = ((SSLSocket)socket).getSession();
+ if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0])))
+ verifiedHostname = mHostnameVerifier.verify(host, newSession);
+
+ } else {
+ //// performing the previous verification in Android versions under 2.3.x (and we don't know the exact value of x) WILL BREAK THE SSL CONTEXT, and any HTTP operation executed later through the socket WILL FAIL ;
+ //// it is related with A BUG IN THE OpenSSLSOcketImpl.java IN THE ANDROID CORE SYSTEM; it was fixed here:
+ //// http://gitorious.org/ginger/libcore/blobs/df349b3eaf4d1fa0643ab722173bc3bf20a266f5/luni/src/main/java/org/apache/harmony/xnet/provider/jsse/OpenSSLSocketImpl.java
+ /// but we could not find out in what Android version was released the bug fix;
+ ///
+ /// besides, due to the bug, calling ((SSLSocket)socket).getSession() IS NOT SAFE ; the next workaround is an UGLY BUT SAFE solution to get it
+ SSLSessionContext sessionContext = mSslContext.getClientSessionContext();
+ if (sessionContext != null) {
+ SSLSession session = null;
+ synchronized(sessionContext) { // a SSLSession in the SSLSessionContext can be closed while we are searching for the new one; it happens; really
+ Enumeration<byte[]> ids = sessionContext.getIds();
+ while (ids.hasMoreElements()) {
+ session = sessionContext.getSession(ids.nextElement());
+ if ( session.getPeerHost().equals(host) &&
+ session.getPeerPort() == port &&
+ (newSession == null || newSession.getCreationTime() < session.getCreationTime())) {
+ newSession = session;
+ }
+ }
+ }
+ if (newSession != null) {
+ if (!mTrustManager.isKnownServer((X509Certificate)(newSession.getPeerCertificates()[0]))) {
+ verifiedHostname = mHostnameVerifier.verify(host, newSession);
+ }
+ } else {
+ Log.d(TAG, "Hostname verification could not be performed because the new SSLSession was not found");
+ }
+ }
+ }
+ }
+ }
+
+ /// 3. Combine the exceptions to throw, if any
+ if (failInHandshake != null) {
+ if (!verifiedHostname) {
+ ((CertificateCombinedException)failInHandshake.getCause()).setSslPeerUnverifiedException(new SSLPeerUnverifiedException(host));
+ }
+ throw failInHandshake;
+ } else if (!verifiedHostname) {
+ CertificateCombinedException ce = new CertificateCombinedException((X509Certificate) newSession.getPeerCertificates()[0]);
+ SSLPeerUnverifiedException pue = new SSLPeerUnverifiedException(host);
+ ce.setSslPeerUnverifiedException(pue);
+ pue.initCause(ce);
+ throw pue;
+ }
+
+ } catch (IOException io) {
+ try {
+ socket.close();
+ } catch (Exception x) {
+ // NOTHING - irrelevant exception for the caller
+ }
+ throw io;