summaryrefslogtreecommitdiff
path: root/service
diff options
context:
space:
mode:
authorPeter Qiu <zqiu@google.com>2016-11-17 15:17:34 -0800
committerPeter Qiu <zqiu@google.com>2016-12-01 09:53:56 -0800
commitae791278c9032a8b10cf818b98b571c0396add4a (patch)
tree49b23f0abb58defdf4e31fc95b5e5d42c131af36 /service
parent818b30de68475ac6a4f55c84f2f279714d915911 (diff)
hotspot2: install Passpoint certificates and keys in keystore
When adding a Passpoint provider, install the certificates and keys specified in the configuration to the keystore. While there, move the object creation for Passpoint related objects out of the WifiInjector and into newly created PasspointObjectFactory. Bug: 32619189 Test: frameworks/opt/net/wifi/tests/wifitests/runtests.sh Change-Id: I42ee22a31d30e2c9fa05ece8713b95ebea71256e
Diffstat (limited to 'service')
-rw-r--r--service/java/com/android/server/wifi/WifiInjector.java13
-rw-r--r--service/java/com/android/server/wifi/WifiKeyStore.java31
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointManager.java33
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java53
-rw-r--r--service/java/com/android/server/wifi/hotspot2/PasspointProvider.java162
5 files changed, 271 insertions, 21 deletions
diff --git a/service/java/com/android/server/wifi/WifiInjector.java b/service/java/com/android/server/wifi/WifiInjector.java
index 3d1356463..fffac5d8a 100644
--- a/service/java/com/android/server/wifi/WifiInjector.java
+++ b/service/java/com/android/server/wifi/WifiInjector.java
@@ -39,8 +39,8 @@ import com.android.internal.R;
import com.android.server.am.BatteryStatsService;
import com.android.server.net.DelayedDiskWrite;
import com.android.server.net.IpConfigStore;
-import com.android.server.wifi.hotspot2.PasspointEventHandler;
import com.android.server.wifi.hotspot2.PasspointManager;
+import com.android.server.wifi.hotspot2.PasspointObjectFactory;
import com.android.server.wifi.util.WifiPermissionsUtil;
import com.android.server.wifi.util.WifiPermissionsWrapper;
@@ -170,7 +170,8 @@ public class WifiInjector {
mWifiPermissionsUtil = new WifiPermissionsUtil(mWifiPermissionsWrapper, mContext,
mSettingsStore, UserManager.get(mContext), new NetworkScorerAppManager(mContext));
mSimAccessor = new SIMAccessor(mContext);
- mPasspointManager = new PasspointManager(mContext, this, mSimAccessor);
+ mPasspointManager = new PasspointManager(mContext, mWifiNative, mWifiKeyStore, mClock,
+ mSimAccessor, new PasspointObjectFactory());
}
/**
@@ -331,14 +332,6 @@ public class WifiInjector {
}
/**
- * Create a PasspointEventHandler instance with the given callbacks.
- */
- public PasspointEventHandler makePasspointEventHandler(
- PasspointEventHandler.Callbacks callbacks) {
- return new PasspointEventHandler(mWifiNative, callbacks);
- }
-
- /**
* Obtain an instance of WifiScanner.
* If it was not already created, then obtain an instance. Note, this must be done lazily since
* WifiScannerService is separate and created later.
diff --git a/service/java/com/android/server/wifi/WifiKeyStore.java b/service/java/com/android/server/wifi/WifiKeyStore.java
index f921988a5..b667fd4c9 100644
--- a/service/java/com/android/server/wifi/WifiKeyStore.java
+++ b/service/java/com/android/server/wifi/WifiKeyStore.java
@@ -158,7 +158,14 @@ public class WifiKeyStore {
return ret;
}
- private boolean putCertInKeyStore(String name, Certificate cert) {
+ /**
+ * Install a certificate into the keystore.
+ *
+ * @param name The alias name of the certificate to be installed
+ * @param cert The certificate to be installed
+ * @return true on success
+ */
+ public boolean putCertInKeyStore(String name, Certificate cert) {
try {
byte[] certData = Credentials.convertToPem(cert);
if (mVerboseLoggingEnabled) Log.d(TAG, "putting certificate " + name + " in keystore");
@@ -171,6 +178,28 @@ public class WifiKeyStore {
}
/**
+ * Install a key into the keystore.
+ *
+ * @param name The alias name of the key to be installed
+ * @param key The key to be installed
+ * @return true on success
+ */
+ public boolean putKeyInKeyStore(String name, Key key) {
+ byte[] privKeyData = key.getEncoded();
+ return mKeyStore.importKey(name, privKeyData, Process.WIFI_UID, KeyStore.FLAG_NONE);
+ }
+
+ /**
+ * Remove a certificate or key entry specified by the alias name from the keystore.
+ *
+ * @param name The alias name of the entry to be removed
+ * @return true on success
+ */
+ public boolean removeEntryFromKeyStore(String name) {
+ return mKeyStore.delete(name, Process.WIFI_UID);
+ }
+
+ /**
* Remove enterprise keys from the network config.
*
* @param config Config corresponding to the network.
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
index 3e668a76d..d130e012d 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java
@@ -34,9 +34,11 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import com.android.server.wifi.Clock;
import com.android.server.wifi.IMSIParameter;
import com.android.server.wifi.SIMAccessor;
-import com.android.server.wifi.WifiInjector;
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiNative;
import com.android.server.wifi.anqp.ANQPElement;
import com.android.server.wifi.anqp.Constants;
@@ -55,6 +57,9 @@ public class PasspointManager {
private final PasspointEventHandler mHandler;
private final SIMAccessor mSimAccessor;
+ private final WifiKeyStore mKeyStore;
+ private final Clock mClock;
+ private final PasspointObjectFactory mObjectFactory;
private final Map<String, PasspointProvider> mProviders;
private class CallbackHandler implements PasspointEventHandler.Callbacks {
@@ -104,9 +109,14 @@ public class PasspointManager {
}
}
- public PasspointManager(Context context, WifiInjector wifiInjector, SIMAccessor simAccessor) {
- mHandler = wifiInjector.makePasspointEventHandler(new CallbackHandler(context));
+ public PasspointManager(Context context, WifiNative wifiNative, WifiKeyStore keyStore,
+ Clock clock, SIMAccessor simAccessor, PasspointObjectFactory objectFactory) {
+ mHandler = objectFactory.makePasspointEventHandler(wifiNative,
+ new CallbackHandler(context));
+ mKeyStore = keyStore;
+ mClock = clock;
mSimAccessor = simAccessor;
+ mObjectFactory = objectFactory;
mProviders = new HashMap<>();
// TODO(zqiu): load providers from the persistent storage.
}
@@ -144,19 +154,25 @@ public class PasspointManager {
}
}
- // TODO(b/32619189): install new key and certificates to the keystore.
+ // Create a provider and install the necessary certificates and keys.
+ PasspointProvider newProvider = mObjectFactory.makePasspointProvider(
+ config, mKeyStore, mClock.getWallClockMillis());
+
+ if (!newProvider.installCertsAndKeys()) {
+ Log.e(TAG, "Failed to install certificates and keys to keystore");
+ return false;
+ }
// Detect existing configuration in the same base domain.
PasspointProvider existingProvider = findProviderInSameBaseDomain(config.homeSp.fqdn);
if (existingProvider != null) {
Log.d(TAG, "Replacing configuration for " + existingProvider.getConfig().homeSp.fqdn
+ " with " + config.homeSp.fqdn);
- // TODO(b/32619189): Remove existing key and certificates from the keystore.
-
+ existingProvider.uninstallCertsAndKeys();
mProviders.remove(existingProvider.getConfig().homeSp.fqdn);
}
- mProviders.put(config.homeSp.fqdn, new PasspointProvider(config));
+ mProviders.put(config.homeSp.fqdn, newProvider);
// TODO(b/31065385): Persist updated providers configuration to the persistent storage.
@@ -175,8 +191,7 @@ public class PasspointManager {
return false;
}
- // TODO(b/32619189): Remove key and certificates from the keystore.
-
+ mProviders.get(fqdn).uninstallCertsAndKeys();
mProviders.remove(fqdn);
return true;
}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
new file mode 100644
index 000000000..41ec9fa73
--- /dev/null
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointObjectFactory.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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 com.android.server.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+
+import com.android.server.wifi.WifiKeyStore;
+import com.android.server.wifi.WifiNative;
+
+/**
+ * Factory class for creating Passpoint related objects. Useful for mocking object creations
+ * in the unit tests.
+ */
+public class PasspointObjectFactory{
+ /**
+ * Create a PasspointEventHandler instance.
+ *
+ * @param wifiNative Instance of {@link WifiNative}
+ * @param callbacks Instance of {@link PasspointEventHandler.Callbacks}
+ * @return {@link PasspointEventHandler}
+ */
+ public PasspointEventHandler makePasspointEventHandler(WifiNative wifiNative,
+ PasspointEventHandler.Callbacks callbacks) {
+ return new PasspointEventHandler(wifiNative, callbacks);
+ }
+
+ /**
+ * Create a PasspointProvider instance.
+ *
+ * @param keyStore Instance of {@link WifiKeyStore}
+ * @param config Configuration for the provider
+ * @param providerId Unique identifier for the provider
+ * @return {@link PasspointProvider}
+ */
+ public PasspointProvider makePasspointProvider(PasspointConfiguration config,
+ WifiKeyStore keyStore, long providerId) {
+ return new PasspointProvider(config, keyStore, providerId);
+ }
+}
diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
index 9c38ac725..6d120902a 100644
--- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
+++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java
@@ -17,21 +17,181 @@
package com.android.server.wifi.hotspot2;
import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.security.Credentials;
+import android.util.Log;
+
+import com.android.server.wifi.WifiKeyStore;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
/**
* Abstraction for Passpoint service provider. This class contains the both static
* Passpoint configuration data and the runtime data (e.g. blacklisted SSIDs, statistics).
*/
public class PasspointProvider {
+ private static final String TAG = "PasspointProvider";
+
+ // Prefix for certificates and keys aliases.
+ private static final String ALIAS_PREFIX = "HS2_";
+
private final PasspointConfiguration mConfig;
+ private final WifiKeyStore mKeyStore;
+
+ // Unique identifier for this provider. Used as part of the alias names for identifying
+ // certificates and keys installed on the keystore.
+ private final long mProviderId;
+
+ // Aliases for the private keys and certificates installed in the keystore.
+ private String mCaCertificateAlias;
+ private String mClientPrivateKeyAlias;
+ private String mClientCertificateAlias;
- public PasspointProvider(PasspointConfiguration config) {
+ public PasspointProvider(PasspointConfiguration config, WifiKeyStore keyStore,
+ long providerId) {
// Maintain a copy of the configuration to avoid it being updated by others.
mConfig = new PasspointConfiguration(config);
+ mKeyStore = keyStore;
+ mProviderId = providerId;
}
public PasspointConfiguration getConfig() {
// Return a copy of the configuration to avoid it being updated by others.
return new PasspointConfiguration(mConfig);
}
+
+ public String getCaCertificateAlias() {
+ return mCaCertificateAlias;
+ }
+
+ public String getClientPrivateKeyAlias() {
+ return mClientPrivateKeyAlias;
+ }
+
+ public String getClientCertificateAlias() {
+ return mClientCertificateAlias;
+ }
+
+ /**
+ * Install certificates and key based on current configuration.
+ * Note: the certificates and keys in the configuration will get cleared once
+ * they're installed in the keystore.
+ *
+ * @return true on success
+ */
+ public boolean installCertsAndKeys() {
+ // Install CA certificate.
+ if (mConfig.credential.caCertificate != null) {
+ String alias = formatAliasName(Credentials.CA_CERTIFICATE, mProviderId);
+ if (!mKeyStore.putCertInKeyStore(alias, mConfig.credential.caCertificate)) {
+ Log.e(TAG, "Failed to install CA Certificate");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ mCaCertificateAlias = alias;
+ }
+
+ // Install the client private key.
+ if (mConfig.credential.clientPrivateKey != null) {
+ String alias = formatAliasName(Credentials.USER_PRIVATE_KEY, mProviderId);
+ if (!mKeyStore.putKeyInKeyStore(alias, mConfig.credential.clientPrivateKey)) {
+ Log.e(TAG, "Failed to install client private key");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ mClientPrivateKeyAlias = alias;
+ }
+
+ // Install the client certificate.
+ if (mConfig.credential.clientCertificateChain != null) {
+ X509Certificate clientCert =
+ getClientCertificate(mConfig.credential.clientCertificateChain,
+ mConfig.credential.certCredential.certSha256FingerPrint);
+ if (clientCert == null) {
+ Log.e(TAG, "Failed to locate client certificate");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ String alias = formatAliasName(Credentials.USER_CERTIFICATE, mProviderId);
+ if (!mKeyStore.putCertInKeyStore(alias, clientCert)) {
+ Log.e(TAG, "Failed to install client certificate");
+ uninstallCertsAndKeys();
+ return false;
+ }
+ mClientCertificateAlias = alias;
+ }
+
+ // Clear the keys and certificates in the configuration.
+ mConfig.credential.caCertificate = null;
+ mConfig.credential.clientPrivateKey = null;
+ mConfig.credential.clientCertificateChain = null;
+ return true;
+ }
+
+ /**
+ * Remove any installed certificates and key.
+ */
+ public void uninstallCertsAndKeys() {
+ if (mCaCertificateAlias != null) {
+ if (!mKeyStore.removeEntryFromKeyStore(mCaCertificateAlias)) {
+ Log.e(TAG, "Failed to remove entry: " + mCaCertificateAlias);
+ }
+ mCaCertificateAlias = null;
+ }
+ if (mClientPrivateKeyAlias != null) {
+ if (!mKeyStore.removeEntryFromKeyStore(mClientPrivateKeyAlias)) {
+ Log.e(TAG, "Failed to remove entry: " + mClientPrivateKeyAlias);
+ }
+ mClientPrivateKeyAlias = null;
+ }
+ if (mClientCertificateAlias != null) {
+ if (!mKeyStore.removeEntryFromKeyStore(mClientCertificateAlias)) {
+ Log.e(TAG, "Failed to remove entry: " + mClientCertificateAlias);
+ }
+ mClientCertificateAlias = null;
+ }
+ }
+
+ /**
+ * Create and return a certificate or key alias name based on the given prefix and uid.
+ *
+ * @param type The key or certificate type string
+ * @param uid The UID of the alias
+ * @return String
+ */
+ private static String formatAliasName(String type, long uid) {
+ return type + ALIAS_PREFIX + uid;
+ }
+
+ /**
+ * Retrieve the client certificate from the certificates chain. The certificate
+ * with the matching SHA256 digest is the client certificate.
+ *
+ * @param certChain The client certificates chain
+ * @param expectedSha256Fingerprint The expected SHA256 digest of the client certificate
+ * @return {@link java.security.cert.X509Certificate}
+ */
+ private static X509Certificate getClientCertificate(X509Certificate[] certChain,
+ byte[] expectedSha256Fingerprint) {
+ if (certChain == null) {
+ return null;
+ }
+ try {
+ MessageDigest digester = MessageDigest.getInstance("SHA-256");
+ for (X509Certificate certificate : certChain) {
+ digester.reset();
+ byte[] fingerprint = digester.digest(certificate.getEncoded());
+ if (Arrays.equals(expectedSha256Fingerprint, fingerprint)) {
+ return certificate;
+ }
+ }
+ } catch (CertificateEncodingException | NoSuchAlgorithmException e) {
+ return null;
+ }
+
+ return null;
+ }
}