diff options
author | Kenny Root <kroot@google.com> | 2014-03-25 21:23:18 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-03-25 21:23:18 +0000 |
commit | 6ab41d71eb9acbe07257c6cf208d662de82cc22e (patch) | |
tree | 4c9ac495c2933ba84fc5153b237dda06384b18e3 /core/java/android/net | |
parent | 4abb86c05dcdf5db69873ca484dc018b014e0eb4 (diff) | |
parent | 266de3efcd4b57ba3b301d87ff0fb692090d9a95 (diff) | |
download | frameworks_base-6ab41d71eb9acbe07257c6cf208d662de82cc22e.zip frameworks_base-6ab41d71eb9acbe07257c6cf208d662de82cc22e.tar.gz frameworks_base-6ab41d71eb9acbe07257c6cf208d662de82cc22e.tar.bz2 |
am 266de3ef: am dd3e6399: am c1d863e7: am 77ceb5e8: Merge "Use X509ExtendedTrustManager and not Conscrypt"
* commit '266de3efcd4b57ba3b301d87ff0fb692090d9a95':
Use X509ExtendedTrustManager and not Conscrypt
Diffstat (limited to 'core/java/android/net')
4 files changed, 387 insertions, 34 deletions
diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index 1e476fc..a28b5a7 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -16,22 +16,26 @@ package android.net.http; -import com.android.org.conscrypt.SSLParametersImpl; -import com.android.org.conscrypt.TrustManagerImpl; +import android.util.Slog; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.lang.reflect.Method; import java.security.GeneralSecurityException; -import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -import javax.net.ssl.DefaultHostnameVerifier; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; -import javax.net.ssl.X509TrustManager; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import javax.net.ssl.X509ExtendedTrustManager; /** * Class responsible for all server certificate validation functionality @@ -39,28 +43,51 @@ import javax.net.ssl.X509TrustManager; * {@hide} */ public class CertificateChainValidator { + private static final String TAG = "CertificateChainValidator"; - /** - * The singleton instance of the certificate chain validator - */ - private static final CertificateChainValidator sInstance - = new CertificateChainValidator(); + private static class NoPreloadHolder { + /** + * The singleton instance of the certificate chain validator. + */ + private static final CertificateChainValidator sInstance = new CertificateChainValidator(); + + /** + * The singleton instance of the hostname verifier. + */ + private static final HostnameVerifier sVerifier = HttpsURLConnection + .getDefaultHostnameVerifier(); + } - private static final DefaultHostnameVerifier sVerifier - = new DefaultHostnameVerifier(); + private X509ExtendedTrustManager mTrustManager; /** * @return The singleton instance of the certificates chain validator */ public static CertificateChainValidator getInstance() { - return sInstance; + return NoPreloadHolder.sInstance; } /** * Creates a new certificate chain validator. This is a private constructor. * If you need a Certificate chain validator, call getInstance(). */ - private CertificateChainValidator() {} + private CertificateChainValidator() { + try { + TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509"); + for (TrustManager tm : tmf.getTrustManagers()) { + if (tm instanceof X509ExtendedTrustManager) { + mTrustManager = (X509ExtendedTrustManager) tm; + } + } + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("X.509 TrustManager factory must be available", e); + } + + if (mTrustManager == null) { + throw new RuntimeException( + "None of the X.509 TrustManagers are X509ExtendedTrustManager"); + } + } /** * Performs the handshake and server certificates validation @@ -136,14 +163,27 @@ public class CertificateChainValidator { * Handles updates to credential storage. */ public static void handleTrustStorageUpdate() { - + TrustManagerFactory tmf; try { - X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager(); - if( x509TrustManager instanceof TrustManagerImpl ) { - TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager; - trustManager.handleTrustStorageUpdate(); + tmf = TrustManagerFactory.getInstance("X.509"); + } catch (NoSuchAlgorithmException e) { + Slog.w(TAG, "Couldn't find default X.509 TrustManagerFactory"); + return; + } + + TrustManager[] tms = tmf.getTrustManagers(); + boolean sentUpdate = false; + for (TrustManager tm : tms) { + try { + Method updateMethod = tm.getClass().getDeclaredMethod("handleTrustStorageUpdate"); + updateMethod.setAccessible(true); + updateMethod.invoke(tm); + sentUpdate = true; + } catch (Exception e) { } - } catch (KeyManagementException ignored) { + } + if (!sentUpdate) { + Slog.w(TAG, "Didn't find a TrustManager to handle CA list update"); } } @@ -166,7 +206,8 @@ public class CertificateChainValidator { boolean valid = domain != null && !domain.isEmpty() - && sVerifier.verify(domain, currCertificate); + && NoPreloadHolder.sVerifier.verify(domain, + new DelegatingSSLSession.CertificateWrap(currCertificate)); if (!valid) { if (HttpLog.LOGV) { HttpLog.v("certificate not for this host: " + domain); @@ -175,13 +216,8 @@ public class CertificateChainValidator { } try { - X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultX509TrustManager(); - if (x509TrustManager instanceof TrustManagerImpl) { - TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager; - trustManager.checkServerTrusted(chain, authType, domain); - } else { - x509TrustManager.checkServerTrusted(chain, authType); - } + getInstance().getTrustManager().checkServerTrusted(chain, authType, + new DelegatingSocketWrapper(domain)); return null; // No errors. } catch (GeneralSecurityException e) { if (HttpLog.LOGV) { @@ -192,6 +228,12 @@ public class CertificateChainValidator { } } + /** + * Returns the platform default {@link X509ExtendedTrustManager}. + */ + private X509ExtendedTrustManager getTrustManager() { + return mTrustManager; + } private void closeSocketThrowException( SSLSocket socket, String errorMessage, String defaultErrorMessage) @@ -217,4 +259,4 @@ public class CertificateChainValidator { throw new SSLHandshakeException(errorMessage); } -} +}
\ No newline at end of file diff --git a/core/java/android/net/http/DelegatingSSLSession.java b/core/java/android/net/http/DelegatingSSLSession.java new file mode 100644 index 0000000..ff75b24 --- /dev/null +++ b/core/java/android/net/http/DelegatingSSLSession.java @@ -0,0 +1,172 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.http; + +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSessionContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedTrustManager; + +/** + * This is used when only a {@code hostname} is available but usage of the new API + * {@link X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, Socket)} + * requires a {@link SSLSocket}. + * + * @hide + */ +public class DelegatingSSLSession implements SSLSession { + protected DelegatingSSLSession() { + } + + public static class HostnameWrap extends DelegatingSSLSession { + private final String mHostname; + + public HostnameWrap(String hostname) { + mHostname = hostname; + } + + @Override + public String getPeerHost() { + return mHostname; + } + } + + public static class CertificateWrap extends DelegatingSSLSession { + private final Certificate mCertificate; + + public CertificateWrap(Certificate certificate) { + mCertificate = certificate; + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + return new Certificate[] { mCertificate }; + } + } + + + @Override + public int getApplicationBufferSize() { + throw new UnsupportedOperationException(); + } + + @Override + public String getCipherSuite() { + throw new UnsupportedOperationException(); + } + + @Override + public long getCreationTime() { + throw new UnsupportedOperationException(); + } + + @Override + public byte[] getId() { + throw new UnsupportedOperationException(); + } + + @Override + public long getLastAccessedTime() { + throw new UnsupportedOperationException(); + } + + @Override + public Certificate[] getLocalCertificates() { + throw new UnsupportedOperationException(); + } + + @Override + public Principal getLocalPrincipal() { + throw new UnsupportedOperationException(); + } + + @Override + public int getPacketBufferSize() { + throw new UnsupportedOperationException(); + } + + @Override + public javax.security.cert.X509Certificate[] getPeerCertificateChain() + throws SSLPeerUnverifiedException { + throw new UnsupportedOperationException(); + } + + @Override + public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { + throw new UnsupportedOperationException(); + } + + @Override + public String getPeerHost() { + throw new UnsupportedOperationException(); + } + + @Override + public int getPeerPort() { + throw new UnsupportedOperationException(); + } + + @Override + public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { + throw new UnsupportedOperationException(); + } + + @Override + public String getProtocol() { + throw new UnsupportedOperationException(); + } + + @Override + public SSLSessionContext getSessionContext() { + throw new UnsupportedOperationException(); + } + + @Override + public Object getValue(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getValueNames() { + throw new UnsupportedOperationException(); + } + + @Override + public void invalidate() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isValid() { + throw new UnsupportedOperationException(); + } + + @Override + public void putValue(String name, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeValue(String name) { + throw new UnsupportedOperationException(); + } +}
\ No newline at end of file diff --git a/core/java/android/net/http/DelegatingSocketWrapper.java b/core/java/android/net/http/DelegatingSocketWrapper.java new file mode 100644 index 0000000..230d017 --- /dev/null +++ b/core/java/android/net/http/DelegatingSocketWrapper.java @@ -0,0 +1,127 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.http; + +import java.io.IOException; + +import javax.net.ssl.HandshakeCompletedListener; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedTrustManager; + +/** + * This is used when only a {@code hostname} is available for + * {@link X509ExtendedTrustManager#checkServerTrusted(java.security.cert.X509Certificate[], String, Socket)} + * but we want to use the new API that requires a {@link SSLSocket}. + */ +class DelegatingSocketWrapper extends SSLSocket { + private String hostname; + + public DelegatingSocketWrapper(String hostname) { + this.hostname = hostname; + } + + @Override + public String[] getSupportedCipherSuites() { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getEnabledCipherSuites() { + throw new UnsupportedOperationException(); + } + + @Override + public void setEnabledCipherSuites(String[] suites) { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getSupportedProtocols() { + throw new UnsupportedOperationException(); + } + + @Override + public String[] getEnabledProtocols() { + throw new UnsupportedOperationException(); + } + + @Override + public void setEnabledProtocols(String[] protocols) { + throw new UnsupportedOperationException(); + } + + @Override + public SSLSession getSession() { + return new DelegatingSSLSession.HostnameWrap(hostname); + } + + @Override + public void addHandshakeCompletedListener(HandshakeCompletedListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void removeHandshakeCompletedListener(HandshakeCompletedListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void startHandshake() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void setUseClientMode(boolean mode) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean getUseClientMode() { + throw new UnsupportedOperationException(); + } + + @Override + public void setNeedClientAuth(boolean need) { + throw new UnsupportedOperationException(); + } + + @Override + public void setWantClientAuth(boolean want) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean getNeedClientAuth() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean getWantClientAuth() { + throw new UnsupportedOperationException(); + } + + @Override + public void setEnableSessionCreation(boolean flag) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean getEnableSessionCreation() { + throw new UnsupportedOperationException(); + } +}
\ No newline at end of file diff --git a/core/java/android/net/http/X509TrustManagerExtensions.java b/core/java/android/net/http/X509TrustManagerExtensions.java index db71279..a500bcf 100644 --- a/core/java/android/net/http/X509TrustManagerExtensions.java +++ b/core/java/android/net/http/X509TrustManagerExtensions.java @@ -22,14 +22,25 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.List; +import javax.net.ssl.SSLParameters; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509ExtendedTrustManager; import javax.net.ssl.X509TrustManager; /** * X509TrustManager wrapper exposing Android-added features. - * - * <p> The checkServerTrusted method allows callers to perform additional - * verification of certificate chains after they have been successfully - * verified by the platform.</p> + * <p> + * The checkServerTrusted method allows callers to perform additional + * verification of certificate chains after they have been successfully verified + * by the platform. + * </p> + * <p> + * If the returned certificate list is not needed, see also + * {@code X509ExtendedTrustManager#checkServerTrusted(X509Certificate[], String, java.net.Socket)} + * where an {@link SSLSocket} can be used to verify the given hostname during + * handshake using + * {@code SSLParameters#setEndpointIdentificationAlgorithm(String)}. + * </p> */ public class X509TrustManagerExtensions { @@ -61,7 +72,8 @@ public class X509TrustManagerExtensions { */ public List<X509Certificate> checkServerTrusted(X509Certificate[] chain, String authType, String host) throws CertificateException { - return mDelegate.checkServerTrusted(chain, authType, host); + return mDelegate.checkServerTrusted(chain, authType, + new DelegatingSSLSession.HostnameWrap(host)); } /** |