Loading core/java/android/security/net/config/ApplicationConfig.java 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import android.util.Pair; import java.util.Locale; import java.util.Set; import javax.net.ssl.X509TrustManager; /** * An application's network security configuration. * * <p>{@link #getConfigForHostname(String)} provides a means to obtain network security * configuration to be used for communicating with a specific hostname.</p> * * @hide */ public final class ApplicationConfig { private Set<Pair<Domain, NetworkSecurityConfig>> mConfigs; private NetworkSecurityConfig mDefaultConfig; private X509TrustManager mTrustManager; private ConfigSource mConfigSource; private boolean mInitialized; private final Object mLock = new Object(); public ApplicationConfig(ConfigSource configSource) { mConfigSource = configSource; mInitialized = false; } /** * @hide */ public boolean hasPerDomainConfigs() { ensureInitialized(); return mConfigs == null || !mConfigs.isEmpty(); } /** * Get the {@link NetworkSecurityConfig} corresponding to the provided hostname. * When matching the most specific matching domain rule will be used, if no match exists * then the default configuration will be returned. * * {@code NetworkSecurityConfig} objects returned by this method can be safely cached for * {@code hostname}. Subsequent calls with the same hostname will always return the same * {@code NetworkSecurityConfig}. * * @return {@link NetworkSecurityConfig} to be used to determine * the network security configuration for connections to {@code hostname}. */ public NetworkSecurityConfig getConfigForHostname(String hostname) { ensureInitialized(); if (hostname.isEmpty() || mConfigs == null) { return mDefaultConfig; } if (hostname.charAt(0) == '.') { throw new IllegalArgumentException("hostname must not begin with a ."); } // Domains are case insensitive. hostname = hostname.toLowerCase(Locale.US); // Normalize hostname by removing trailing . if present, all Domain hostnames are // absolute. if (hostname.charAt(hostname.length() - 1) == '.') { hostname = hostname.substring(0, hostname.length() - 1); } // Find the Domain -> NetworkSecurityConfig entry with the most specific matching // Domain entry for hostname. // TODO: Use a smarter data structure for the lookup. Pair<Domain, NetworkSecurityConfig> bestMatch = null; for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) { Domain domain = entry.first; NetworkSecurityConfig config = entry.second; // Check for an exact match. if (domain.hostname.equals(hostname)) { return config; } // Otherwise check if the Domain includes sub-domains and that the hostname is a // sub-domain of the Domain. if (domain.subdomainsIncluded && hostname.endsWith(domain.hostname) && hostname.charAt(hostname.length() - domain.hostname.length() - 1) == '.') { if (bestMatch == null) { bestMatch = entry; } else if (domain.hostname.length() > bestMatch.first.hostname.length()) { bestMatch = entry; } } } if (bestMatch != null) { return bestMatch.second; } // If no match was found use the default configuration. return mDefaultConfig; } /** * Returns the {@link X509TrustManager} that implements the checking of trust anchors and * certificate pinning based on this configuration. */ public X509TrustManager getTrustManager() { ensureInitialized(); return mTrustManager; } private void ensureInitialized() { synchronized(mLock) { if (mInitialized) { return; } mConfigs = mConfigSource.getPerDomainConfigs(); mDefaultConfig = mConfigSource.getDefaultConfig(); mConfigSource = null; mTrustManager = new RootTrustManager(this); mInitialized = true; } } } core/java/android/security/net/config/CertificateSource.java 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import java.util.Set; import java.security.cert.X509Certificate; /** @hide */ public interface CertificateSource { Set<X509Certificate> getCertificates(); } core/java/android/security/net/config/CertificatesEntryRef.java 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import android.util.ArraySet; import java.util.Set; import java.security.cert.X509Certificate; /** @hide */ public final class CertificatesEntryRef { private final CertificateSource mSource; private final boolean mOverridesPins; public CertificatesEntryRef(CertificateSource source, boolean overridesPins) { mSource = source; mOverridesPins = overridesPins; } public Set<TrustAnchor> getTrustAnchors() { // TODO: cache this [but handle mutable sources] Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>(); for (X509Certificate cert : mSource.getCertificates()) { anchors.add(new TrustAnchor(cert, mOverridesPins)); } return anchors; } } core/java/android/security/net/config/ConfigSource.java 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import android.util.Pair; import java.util.Set; /** @hide */ public interface ConfigSource { Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs(); NetworkSecurityConfig getDefaultConfig(); } core/java/android/security/net/config/Domain.java 0 → 100644 +57 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import java.util.Locale; /** @hide */ public final class Domain { /** * Lower case hostname for this domain rule. */ public final String hostname; /** * Whether this domain includes subdomains. */ public final boolean subdomainsIncluded; public Domain(String hostname, boolean subdomainsIncluded) { if (hostname == null) { throw new NullPointerException("Hostname must not be null"); } this.hostname = hostname.toLowerCase(Locale.US); this.subdomainsIncluded = subdomainsIncluded; } @Override public int hashCode() { return hostname.hashCode() ^ (subdomainsIncluded ? 1231 : 1237); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Domain)) { return false; } Domain otherDomain = (Domain) other; return otherDomain.subdomainsIncluded == this.subdomainsIncluded && otherDomain.hostname.equals(this.hostname); } } Loading
core/java/android/security/net/config/ApplicationConfig.java 0 → 100644 +132 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import android.util.Pair; import java.util.Locale; import java.util.Set; import javax.net.ssl.X509TrustManager; /** * An application's network security configuration. * * <p>{@link #getConfigForHostname(String)} provides a means to obtain network security * configuration to be used for communicating with a specific hostname.</p> * * @hide */ public final class ApplicationConfig { private Set<Pair<Domain, NetworkSecurityConfig>> mConfigs; private NetworkSecurityConfig mDefaultConfig; private X509TrustManager mTrustManager; private ConfigSource mConfigSource; private boolean mInitialized; private final Object mLock = new Object(); public ApplicationConfig(ConfigSource configSource) { mConfigSource = configSource; mInitialized = false; } /** * @hide */ public boolean hasPerDomainConfigs() { ensureInitialized(); return mConfigs == null || !mConfigs.isEmpty(); } /** * Get the {@link NetworkSecurityConfig} corresponding to the provided hostname. * When matching the most specific matching domain rule will be used, if no match exists * then the default configuration will be returned. * * {@code NetworkSecurityConfig} objects returned by this method can be safely cached for * {@code hostname}. Subsequent calls with the same hostname will always return the same * {@code NetworkSecurityConfig}. * * @return {@link NetworkSecurityConfig} to be used to determine * the network security configuration for connections to {@code hostname}. */ public NetworkSecurityConfig getConfigForHostname(String hostname) { ensureInitialized(); if (hostname.isEmpty() || mConfigs == null) { return mDefaultConfig; } if (hostname.charAt(0) == '.') { throw new IllegalArgumentException("hostname must not begin with a ."); } // Domains are case insensitive. hostname = hostname.toLowerCase(Locale.US); // Normalize hostname by removing trailing . if present, all Domain hostnames are // absolute. if (hostname.charAt(hostname.length() - 1) == '.') { hostname = hostname.substring(0, hostname.length() - 1); } // Find the Domain -> NetworkSecurityConfig entry with the most specific matching // Domain entry for hostname. // TODO: Use a smarter data structure for the lookup. Pair<Domain, NetworkSecurityConfig> bestMatch = null; for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) { Domain domain = entry.first; NetworkSecurityConfig config = entry.second; // Check for an exact match. if (domain.hostname.equals(hostname)) { return config; } // Otherwise check if the Domain includes sub-domains and that the hostname is a // sub-domain of the Domain. if (domain.subdomainsIncluded && hostname.endsWith(domain.hostname) && hostname.charAt(hostname.length() - domain.hostname.length() - 1) == '.') { if (bestMatch == null) { bestMatch = entry; } else if (domain.hostname.length() > bestMatch.first.hostname.length()) { bestMatch = entry; } } } if (bestMatch != null) { return bestMatch.second; } // If no match was found use the default configuration. return mDefaultConfig; } /** * Returns the {@link X509TrustManager} that implements the checking of trust anchors and * certificate pinning based on this configuration. */ public X509TrustManager getTrustManager() { ensureInitialized(); return mTrustManager; } private void ensureInitialized() { synchronized(mLock) { if (mInitialized) { return; } mConfigs = mConfigSource.getPerDomainConfigs(); mDefaultConfig = mConfigSource.getDefaultConfig(); mConfigSource = null; mTrustManager = new RootTrustManager(this); mInitialized = true; } } }
core/java/android/security/net/config/CertificateSource.java 0 → 100644 +25 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import java.util.Set; import java.security.cert.X509Certificate; /** @hide */ public interface CertificateSource { Set<X509Certificate> getCertificates(); }
core/java/android/security/net/config/CertificatesEntryRef.java 0 → 100644 +41 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import android.util.ArraySet; import java.util.Set; import java.security.cert.X509Certificate; /** @hide */ public final class CertificatesEntryRef { private final CertificateSource mSource; private final boolean mOverridesPins; public CertificatesEntryRef(CertificateSource source, boolean overridesPins) { mSource = source; mOverridesPins = overridesPins; } public Set<TrustAnchor> getTrustAnchors() { // TODO: cache this [but handle mutable sources] Set<TrustAnchor> anchors = new ArraySet<TrustAnchor>(); for (X509Certificate cert : mSource.getCertificates()) { anchors.add(new TrustAnchor(cert, mOverridesPins)); } return anchors; } }
core/java/android/security/net/config/ConfigSource.java 0 → 100644 +26 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import android.util.Pair; import java.util.Set; /** @hide */ public interface ConfigSource { Set<Pair<Domain, NetworkSecurityConfig>> getPerDomainConfigs(); NetworkSecurityConfig getDefaultConfig(); }
core/java/android/security/net/config/Domain.java 0 → 100644 +57 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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.security.net.config; import java.util.Locale; /** @hide */ public final class Domain { /** * Lower case hostname for this domain rule. */ public final String hostname; /** * Whether this domain includes subdomains. */ public final boolean subdomainsIncluded; public Domain(String hostname, boolean subdomainsIncluded) { if (hostname == null) { throw new NullPointerException("Hostname must not be null"); } this.hostname = hostname.toLowerCase(Locale.US); this.subdomainsIncluded = subdomainsIncluded; } @Override public int hashCode() { return hostname.hashCode() ^ (subdomainsIncluded ? 1231 : 1237); } @Override public boolean equals(Object other) { if (other == this) { return true; } if (!(other instanceof Domain)) { return false; } Domain otherDomain = (Domain) other; return otherDomain.subdomainsIncluded == this.subdomainsIncluded && otherDomain.hostname.equals(this.hostname); } }