1515 */
1616package org .conscrypt ;
1717
18+ import org .conscrypt .io .IoUtils ;
19+
1820import java .io .IOException ;
1921import java .io .InputStream ;
22+ import java .lang .reflect .InvocationTargetException ;
23+ import java .lang .reflect .Method ;
2024import java .nio .ByteBuffer ;
2125import java .security .KeyManagementException ;
2226import java .security .PrivateKey ;
2327import java .security .Provider ;
2428import java .security .cert .X509Certificate ;
2529import java .util .Properties ;
30+
2631import javax .net .ssl .HostnameVerifier ;
2732import javax .net .ssl .HttpsURLConnection ;
2833import javax .net .ssl .SSLContext ;
3742import javax .net .ssl .SSLSocketFactory ;
3843import javax .net .ssl .TrustManager ;
3944import javax .net .ssl .X509TrustManager ;
40- import org .conscrypt .io .IoUtils ;
4145
4246/**
4347 * Core API for creating and configuring all Conscrypt types.
@@ -80,9 +84,15 @@ private Version(int major, int minor, int patch) {
8084 this .patch = patch ;
8185 }
8286
83- public int major () { return major ; }
84- public int minor () { return minor ; }
85- public int patch () { return patch ; }
87+ public int major () {
88+ return major ;
89+ }
90+ public int minor () {
91+ return minor ;
92+ }
93+ public int patch () {
94+ return patch ;
95+ }
8696 }
8797
8898 private static final Version VERSION ;
@@ -215,8 +225,8 @@ public ProviderBuilder isTlsV1Enabled(boolean enabledTlsV1) {
215225 }
216226
217227 public Provider build () {
218- return new OpenSSLProvider (name , provideTrustManager ,
219- defaultTlsProtocol , deprecatedTlsV1 , enabledTlsV1 );
228+ return new OpenSSLProvider (
229+ name , provideTrustManager , defaultTlsProtocol , deprecatedTlsV1 , enabledTlsV1 );
220230 }
221231 }
222232
@@ -441,9 +451,23 @@ public static void setChannelIdPrivateKey(SSLSocket socket, PrivateKey privateKe
441451 *
442452 * @param socket the socket
443453 * @return the selected protocol or {@code null} if no protocol was agreed upon.
454+ * @throws IllegalArgumentException if the socket is not a Conscrypt socket.
444455 */
445456 public static String getApplicationProtocol (SSLSocket socket ) {
446- return toConscrypt (socket ).getApplicationProtocol ();
457+ if (isConscrypt (socket )) {
458+ return toConscrypt (socket ).getApplicationProtocol ();
459+ }
460+ try {
461+ if (!Class .forName ("com.android.org.conscrypt.AbstractConscryptSocket" )
462+ .isInstance (socket )) {
463+ throw new IllegalArgumentException (
464+ "Not a conscrypt socket: " + socket .getClass ().getName ());
465+ }
466+ return invokeConscryptMethod (socket , "getApplicationProtocol" );
467+ } catch (ClassNotFoundException e ) {
468+ throw new IllegalArgumentException (
469+ "Not a conscrypt socket: " + socket .getClass ().getName (), e );
470+ }
447471 }
448472
449473 /**
@@ -453,8 +477,8 @@ public static String getApplicationProtocol(SSLSocket socket) {
453477 * @param socket the socket
454478 * @param selector the ALPN protocol selector
455479 */
456- public static void setApplicationProtocolSelector (SSLSocket socket ,
457- ApplicationProtocolSelector selector ) {
480+ public static void setApplicationProtocolSelector (
481+ SSLSocket socket , ApplicationProtocolSelector selector ) {
458482 toConscrypt (socket ).setApplicationProtocolSelector (selector );
459483 }
460484
@@ -504,8 +528,8 @@ public static byte[] getTlsUnique(SSLSocket socket) {
504528 * completed or the connection has been closed.
505529 * @throws SSLException if the value could not be exported.
506530 */
507- public static byte [] exportKeyingMaterial (SSLSocket socket , String label , byte [] context ,
508- int length ) throws SSLException {
531+ public static byte [] exportKeyingMaterial (
532+ SSLSocket socket , String label , byte [] context , int length ) throws SSLException {
509533 return toConscrypt (socket ).exportKeyingMaterial (label , context , length );
510534 }
511535
@@ -711,8 +735,8 @@ public static String[] getApplicationProtocols(SSLEngine engine) {
711735 * @param engine the engine
712736 * @param selector the ALPN protocol selector
713737 */
714- public static void setApplicationProtocolSelector (SSLEngine engine ,
715- ApplicationProtocolSelector selector ) {
738+ public static void setApplicationProtocolSelector (
739+ SSLEngine engine , ApplicationProtocolSelector selector ) {
716740 toConscrypt (engine ).setApplicationProtocolSelector (selector );
717741 }
718742
@@ -721,9 +745,23 @@ public static void setApplicationProtocolSelector(SSLEngine engine,
721745 *
722746 * @param engine the engine
723747 * @return the selected protocol or {@code null} if no protocol was agreed upon.
748+ * @throws IllegalArgumentException if the engine is not a Conscrypt engine.
724749 */
725750 public static String getApplicationProtocol (SSLEngine engine ) {
726- return toConscrypt (engine ).getApplicationProtocol ();
751+ if (isConscrypt (engine )) {
752+ return toConscrypt (engine ).getApplicationProtocol ();
753+ }
754+ try {
755+ if (!Class .forName ("com.android.org.conscrypt.AbstractConscryptEngine" )
756+ .isInstance (engine )) {
757+ throw new IllegalArgumentException (
758+ "Not a conscrypt engine: " + engine .getClass ().getName ());
759+ }
760+ return invokeConscryptMethod (engine , "getApplicationProtocol" );
761+ } catch (ClassNotFoundException e ) {
762+ throw new IllegalArgumentException (
763+ "Not a conscrypt engine: " + engine .getClass ().getName (), e );
764+ }
727765 }
728766
729767 /**
@@ -748,8 +786,8 @@ public static byte[] getTlsUnique(SSLEngine engine) {
748786 * completed or the connection has been closed.
749787 * @throws SSLException if the value could not be exported.
750788 */
751- public static byte [] exportKeyingMaterial (SSLEngine engine , String label , byte [] context ,
752- int length ) throws SSLException {
789+ public static byte [] exportKeyingMaterial (
790+ SSLEngine engine , String label , byte [] context , int length ) throws SSLException {
753791 return toConscrypt (engine ).exportKeyingMaterial (label , context , length );
754792 }
755793
@@ -764,7 +802,7 @@ public static boolean isConscrypt(TrustManager trustManager) {
764802 private static TrustManagerImpl toConscrypt (TrustManager trustManager ) {
765803 if (!isConscrypt (trustManager )) {
766804 throw new IllegalArgumentException (
767- "Not a Conscrypt trust manager: " + trustManager .getClass ().getName ());
805+ "Not a Conscrypt trust manager: " + trustManager .getClass ().getName ());
768806 }
769807 return (TrustManagerImpl ) trustManager ;
770808 }
@@ -784,19 +822,22 @@ public synchronized static void setDefaultHostnameVerifier(ConscryptHostnameVeri
784822 *
785823 * @see #setDefaultHostnameVerifier(ConscryptHostnameVerifier)
786824 */
787- public synchronized static ConscryptHostnameVerifier getDefaultHostnameVerifier (TrustManager trustManager ) {
825+ public synchronized static ConscryptHostnameVerifier getDefaultHostnameVerifier (
826+ TrustManager trustManager ) {
788827 return TrustManagerImpl .getDefaultHostnameVerifier ();
789828 }
790829
791830 /**
792831 * Set the hostname verifier that will be used for HTTPS endpoint identification by the
793832 * given trust manager. If {@code null} (the default), endpoint identification will use the
794- * default hostname verifier set in {@link #setDefaultHostnameVerifier(ConscryptHostnameVerifier)}.
833+ * default hostname verifier set in {@link
834+ * #setDefaultHostnameVerifier(ConscryptHostnameVerifier)}.
795835 *
796836 * @throws IllegalArgumentException if the provided trust manager is not a Conscrypt trust
797837 * manager per {@link #isConscrypt(TrustManager)}
798838 */
799- public static void setHostnameVerifier (TrustManager trustManager , ConscryptHostnameVerifier verifier ) {
839+ public static void setHostnameVerifier (
840+ TrustManager trustManager , ConscryptHostnameVerifier verifier ) {
800841 toConscrypt (trustManager ).setHostnameVerifier (verifier );
801842 }
802843
@@ -816,11 +857,58 @@ public static ConscryptHostnameVerifier getHostnameVerifier(TrustManager trustMa
816857 * Wraps the HttpsURLConnection.HostnameVerifier into a ConscryptHostnameVerifier
817858 */
818859 public static ConscryptHostnameVerifier wrapHostnameVerifier (final HostnameVerifier verifier ) {
819- return new ConscryptHostnameVerifier () {
860+ return new ConscryptHostnameVerifier () {
820861 @ Override
821- public boolean verify (X509Certificate [] certificates , String hostname , SSLSession session ) {
862+ public boolean verify (
863+ X509Certificate [] certificates , String hostname , SSLSession session ) {
822864 return verifier .verify (hostname , session );
823865 }
824866 };
825867 }
868+
869+ /**
870+ * Generic helper method for invoking methods on potentially non-Conscrypt SSLSocket/SSLEngine
871+ * instances via reflection.
872+ *
873+ * @param instance The SSLSocket or SSLEngine instance.
874+ * @param methodName The name of the method to invoke.
875+ * @return String.
876+ * @throws IllegalArgumentException if the method cannot be invoked or throws a checked
877+ * exception.
878+ * @throws IllegalStateException if the method throws an IllegalStateException.
879+ * @throws RuntimeException if the method throws a RuntimeException.
880+ * @throws Error if the method throws an Error.
881+ */
882+ private static String invokeConscryptMethod (Object instance , String methodName )
883+ throws IllegalArgumentException {
884+ try {
885+ Method method = instance .getClass ().getMethod (methodName );
886+ Object result = method .invoke (instance );
887+ return (String ) result ;
888+ } catch (InvocationTargetException e ) {
889+ Throwable cause = e .getCause ();
890+ if (cause instanceof SSLException || cause instanceof IOException ) {
891+ IllegalArgumentException wrapped = new IllegalArgumentException (
892+ "Reflected method '" + methodName + "' threw a checked exception." , cause );
893+ throw wrapped ;
894+ } else if (cause instanceof IllegalStateException ) {
895+ throw (IllegalStateException ) cause ;
896+ } else if (cause instanceof RuntimeException ) {
897+ throw (RuntimeException ) cause ;
898+ } else if (cause instanceof Error ) {
899+ throw (Error ) cause ;
900+ } else {
901+ throw new RuntimeException (
902+ "Reflected method '" + methodName + "' threw an unexpected exception" ,
903+ cause );
904+ }
905+ } catch (Exception e ) {
906+ String className = instance .getClass ().getName ();
907+ IllegalArgumentException wrapped = new IllegalArgumentException (
908+ "Failed reflection fallback for method '" + methodName + "' on class '"
909+ + className + ", message: " + e .getMessage (),
910+ e );
911+ throw wrapped ;
912+ }
913+ }
826914}
0 commit comments