Skip to content

Commit 9370510

Browse files
Pavel GrafovAndroid (Google) Code Review
authored andcommitted
Merge "Allow configuring WiFi with KeyChain keys" into ub-testdpc-sc
2 parents 5cda94d + 61f4c15 commit 9370510

File tree

3 files changed

+112
-57
lines changed

3 files changed

+112
-57
lines changed

app/src/main/java/com/afwsamples/testdpc/policy/wifimanagement/WifiEapTlsCreateDialogFragment.java

Lines changed: 103 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
package com.afwsamples.testdpc.policy.wifimanagement;
22

3+
import static com.afwsamples.testdpc.common.Util.SDK_INT;
4+
35
import android.app.Activity;
46
import android.app.AlertDialog;
57
import android.app.Dialog;
68
import android.app.DialogFragment;
9+
import android.app.admin.DevicePolicyManager;
710
import android.content.ActivityNotFoundException;
811
import android.content.DialogInterface;
912
import android.content.Intent;
1013
import android.database.Cursor;
1114
import android.net.Uri;
1215
import android.net.wifi.WifiConfiguration;
1316
import android.net.wifi.WifiEnterpriseConfig;
17+
import android.os.Build.VERSION_CODES;
1418
import android.os.Bundle;
1519
import android.provider.MediaStore;
20+
import android.security.KeyChain;
21+
import android.security.KeyChainAliasCallback;
1622
import android.text.TextUtils;
1723
import android.util.Log;
1824
import android.view.LayoutInflater;
1925
import android.view.View;
26+
import android.widget.Button;
2027
import android.widget.EditText;
2128
import android.widget.TextView;
2229
import android.widget.Toast;
@@ -28,7 +35,6 @@
2835
import java.io.InputStream;
2936
import java.security.KeyStoreException;
3037
import java.security.NoSuchAlgorithmException;
31-
import java.security.PrivateKey;
3238
import java.security.UnrecoverableKeyException;
3339
import java.security.cert.CertificateException;
3440
import java.security.cert.CertificateFactory;
@@ -45,9 +51,12 @@ public class WifiEapTlsCreateDialogFragment extends DialogFragment {
4551
private final static String ARG_CONFIG = "config";
4652
private static final String TAG = "wifi_eap_tls";
4753

54+
private DevicePolicyManager mDpm;
55+
4856
private WifiConfiguration mWifiConfiguration;
4957
private Uri mCaCertUri;
5058
private Uri mUserCertUri;
59+
private String mUserCertAlias;
5160

5261
private EditText mSsidEditText;
5362
private TextView mCaCertTextView;
@@ -66,6 +75,7 @@ public static WifiEapTlsCreateDialogFragment newInstance(WifiConfiguration confi
6675
@Override
6776
public void onCreate(Bundle savedInstanceState) {
6877
super.onCreate(savedInstanceState);
78+
mDpm = getActivity().getSystemService(DevicePolicyManager.class);
6979
mWifiConfiguration = getArguments().getParcelable(ARG_CONFIG);
7080
if (mWifiConfiguration == null) {
7181
mWifiConfiguration = new WifiConfiguration();
@@ -76,10 +86,19 @@ public void onCreate(Bundle savedInstanceState) {
7686
public Dialog onCreateDialog(Bundle savedInstanceState) {
7787
LayoutInflater inflater = LayoutInflater.from(getActivity());
7888
View rootView = inflater.inflate(R.layout.eap_tls_wifi_config_dialog, null);
89+
7990
rootView.findViewById(R.id.import_ca_cert).setOnClickListener(
8091
new ImportButtonOnClickListener(REQUEST_CA_CERT, "application/x-x509-ca-cert"));
8192
rootView.findViewById(R.id.import_user_cert).setOnClickListener(
8293
new ImportButtonOnClickListener(REQUEST_USER_CERT, "application/x-pkcs12"));
94+
95+
final Button selectUserCertButton = rootView.findViewById(R.id.select_user_cert);
96+
if (SDK_INT >= VERSION_CODES.S) {
97+
selectUserCertButton.setOnClickListener(this::onSelectClientCertClicked);
98+
} else {
99+
// KeyChain keys aren't supported.
100+
selectUserCertButton.setVisibility(View.GONE);
101+
}
83102
mCaCertTextView = (TextView) rootView.findViewById(R.id.selected_ca_cert);
84103
mUserCertTextView = (TextView) rootView.findViewById(R.id.selected_user_cert);
85104
mSsidEditText = (EditText) rootView.findViewById(R.id.ssid);
@@ -110,6 +129,20 @@ public void onClick(View view) {
110129
return dialog;
111130
}
112131

132+
private void onSelectClientCertClicked(View view) {
133+
KeyChain.choosePrivateKeyAlias(getActivity(), alias -> {
134+
if (alias == null) {
135+
// No value was chosen.
136+
return;
137+
}
138+
mUserCertAlias = alias;
139+
mUserCertUri = null;
140+
141+
getActivity().runOnUiThread(() ->
142+
updateSelectedCert(mUserCertTextView, /* uri= */ null, alias));
143+
}, /* keyTypes[] */ null, /* issuers[] */ null, /* uri */ null, /* alias */ null);
144+
}
145+
113146
private void populateUi() {
114147
if (mWifiConfiguration == null) {
115148
return;
@@ -119,8 +152,8 @@ private void populateUi() {
119152
}
120153
mIdentityEditText.setText(mWifiConfiguration.enterpriseConfig.getIdentity());
121154
// Both ca cert and client are not populated in the WifiConfiguration object.
122-
updateSelectedCert(mCaCertTextView, null);
123-
updateSelectedCert(mUserCertTextView, null);
155+
updateSelectedCert(mCaCertTextView, null, null);
156+
updateSelectedCert(mUserCertTextView, null, null);
124157
}
125158

126159
private boolean extractInputDataAndSave() {
@@ -131,31 +164,18 @@ private boolean extractInputDataAndSave() {
131164
} else {
132165
mSsidEditText.setError(null);
133166
}
134-
if (mCaCertUri == null) {
135-
showToast(R.string.error_missing_ca_cert);
136-
return false;
137-
}
138-
if (mUserCertUri == null) {
139-
showToast(R.string.error_missing_client_cert);
140-
return false;
141-
}
142-
X509Certificate caCert = parseX509Certificate(mCaCertUri);
143-
String certPassword = mCertPasswordEditText.getText().toString();
144-
CertificateUtil.PKCS12ParseInfo parseInfo = null;
145-
try {
146-
parseInfo = CertificateUtil.parsePKCS12Certificate(
147-
getActivity().getContentResolver(), mUserCertUri, certPassword);
148-
} catch (KeyStoreException | NoSuchAlgorithmException | IOException |
149-
CertificateException | UnrecoverableKeyException e) {
150-
Log.e(TAG, "Fail to parse the input certificate: ", e);
151-
}
152-
if (parseInfo == null) {
153-
showToast(R.string.error_missing_client_cert);
167+
168+
mWifiConfiguration.SSID = ssid;
169+
mWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
170+
mWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
171+
172+
mWifiConfiguration.enterpriseConfig = extractEnterpriseConfig();
173+
174+
if (mWifiConfiguration.enterpriseConfig == null) {
154175
return false;
155176
}
156-
String identity = mIdentityEditText.getText().toString();
157-
boolean success = saveWifiConfiguration(ssid, caCert, parseInfo.privateKey,
158-
parseInfo.certificate, identity);
177+
178+
boolean success = WifiConfigUtil.saveWifiConfiguration(getActivity(), mWifiConfiguration);
159179
if (success) {
160180
showToast(R.string.wifi_configs_header);
161181
return true;
@@ -165,20 +185,48 @@ private boolean extractInputDataAndSave() {
165185
return false;
166186
}
167187

168-
private boolean saveWifiConfiguration(String ssid, X509Certificate caCert,
169-
PrivateKey privateKey, X509Certificate userCert, String identity) {
170-
mWifiConfiguration.SSID = ssid;
171-
mWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
172-
mWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
173-
WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
174-
enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
175-
enterpriseConfig.setCaCertificate(caCert);
176-
enterpriseConfig.setClientKeyEntry(privateKey, userCert);
188+
private WifiEnterpriseConfig extractEnterpriseConfig() {
189+
WifiEnterpriseConfig config = new WifiEnterpriseConfig();
190+
config.setEapMethod(WifiEnterpriseConfig.Eap.TLS);
191+
String identity = mIdentityEditText.getText().toString();
192+
177193
if (!TextUtils.isEmpty(identity)) {
178-
enterpriseConfig.setIdentity(identity);
194+
config.setIdentity(identity);
195+
}
196+
197+
if (mCaCertUri == null) {
198+
showToast(R.string.error_missing_ca_cert);
199+
return null;
179200
}
180-
mWifiConfiguration.enterpriseConfig = enterpriseConfig;
181-
return WifiConfigUtil.saveWifiConfiguration(getActivity(), mWifiConfiguration);
201+
config.setCaCertificate(parseX509Certificate(mCaCertUri));
202+
203+
if (mUserCertUri != null) {
204+
final String certPassword = mCertPasswordEditText.getText().toString();
205+
CertificateUtil.PKCS12ParseInfo parseInfo = null;
206+
try {
207+
parseInfo = CertificateUtil.parsePKCS12Certificate(
208+
getActivity().getContentResolver(), mUserCertUri, certPassword);
209+
} catch (KeyStoreException | NoSuchAlgorithmException | IOException |
210+
CertificateException | UnrecoverableKeyException e) {
211+
Log.e(TAG, "Fail to parse the input certificate: ", e);
212+
}
213+
if (parseInfo == null) {
214+
showToast(R.string.error_missing_client_cert);
215+
return null;
216+
}
217+
config.setClientKeyEntry(parseInfo.privateKey, parseInfo.certificate);
218+
} else if (mUserCertAlias != null) {
219+
if (!mDpm.grantKeyPairToWifiAuth(mUserCertAlias)) {
220+
showToast(R.string.error_cannot_grant_to_wifi);
221+
return null;
222+
}
223+
config.setClientKeyPairAlias(mUserCertAlias);
224+
} else {
225+
showToast(R.string.error_missing_client_cert);
226+
return null;
227+
}
228+
229+
return config;
182230
}
183231

184232
/**
@@ -217,28 +265,26 @@ public void onClick(View view) {
217265
}
218266
}
219267

220-
private void updateSelectedCert(TextView textView, Uri uri) {
221-
String displayName = null;
222-
if (uri == null) {
223-
displayName = getString(R.string.selected_certificate_none);
224-
} else {
268+
private void updateSelectedCert(TextView textView, Uri uri, String alias) {
269+
final String selectedText;
270+
if (uri != null) {
271+
String displayName = null;
225272
final String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME};
226-
Cursor cursor = getActivity().getContentResolver().query(uri, projection,
227-
null, null, null);
228-
if (cursor != null) {
229-
try {
230-
if (cursor.moveToFirst()) {
231-
displayName = cursor.getString(0);
232-
}
233-
} finally {
234-
cursor.close();
273+
try (Cursor cursor = getActivity().getContentResolver().query(
274+
uri, projection, null, null, null)) {
275+
if (cursor != null && cursor.moveToFirst()) {
276+
displayName = cursor.getString(0);
235277
}
236278
}
237-
if (TextUtils.isEmpty(getString(R.string.wifi_unknown_cert))) {
279+
if (TextUtils.isEmpty(displayName)) {
238280
displayName = getString(R.string.wifi_unknown_cert);
239281
}
282+
selectedText = getString(R.string.selected_certificate, displayName);
283+
} else if (alias != null) {
284+
selectedText = getString(R.string.selected_keychain_certificate, alias);
285+
} else {
286+
selectedText = getString(R.string.selected_certificate_none);
240287
}
241-
String selectedText = getString(R.string.selected_certificate, displayName);
242288
textView.setText(selectedText);
243289
}
244290

@@ -252,11 +298,12 @@ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
252298
switch (requestCode) {
253299
case REQUEST_CA_CERT:
254300
mCaCertUri = intent.getData();
255-
updateSelectedCert(mCaCertTextView, mCaCertUri);
301+
updateSelectedCert(mCaCertTextView, mCaCertUri, /* alias= */ null);
256302
break;
257303
case REQUEST_USER_CERT:
258304
mUserCertUri = intent.getData();
259-
updateSelectedCert(mUserCertTextView, mUserCertUri);
305+
mUserCertAlias = null;
306+
updateSelectedCert(mUserCertTextView, mUserCertUri, /* alias= */ null);
260307
break;
261308
}
262309
}

app/src/main/res/layout/eap_tls_wifi_config_dialog.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@
7676
android:layout_width="wrap_content"
7777
android:layout_height="wrap_content"
7878
android:text="@string/import_user_certificate"/>
79+
<Button android:id="@+id/select_user_cert"
80+
style="@style/networking_item_content"
81+
android:layout_width="wrap_content"
82+
android:layout_height="wrap_content"
83+
android:text="@string/select_user_certificate"/>
7984
<TextView
8085
android:id="@+id/selected_user_cert"
8186
style="@style/networking_item_label"

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,11 +344,14 @@
344344
<string name="wifi_client_cert_password">Client certificate password (if any)</string>
345345
<string name="import_ca_certificate">Import CA certificate</string>
346346
<string name="import_user_certificate">Import user certificate</string>
347+
<string name="select_user_certificate">Select user certificate</string>
347348
<string name="selected_certificate">Selected certificate: <xliff:g example="server certificate" id="certificate_alias">%1$s</xliff:g></string>
348-
<string name="selected_certificate_none">None</string>
349+
<string name="selected_keychain_certificate">Selected KeyChain alias: <xliff:g example="somealias" id="certificate_alias">%1$s</xliff:g></string>
350+
<string name="selected_certificate_none">No certificate selected</string>
349351
<string name="error_missing_ssid">Missing SSID</string>
350352
<string name="error_missing_ca_cert">Missing CA cert</string>
351353
<string name="error_missing_client_cert">Missing client cert or invalid cert password</string>
354+
<string name="error_cannot_grant_to_wifi">Failed to allow Wi-Fi auth with client key pair</string>
352355
<string name="wifi_eap_tls_dialog_message">EAP_TLS with X.509 CA certificate and PKCS12 client certificate</string>
353356
<string name="wifi_unknown_cert">Unknown cert</string>
354357

0 commit comments

Comments
 (0)