package com.afwsamples.testdpc.policy.wifimanagement; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiEnterpriseConfig; import android.os.Bundle; import android.provider.MediaStore; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import com.afwsamples.testdpc.R; import com.afwsamples.testdpc.common.CertificateUtil; import java.io.IOException; import java.io.InputStream; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; /** * Dialog for adding/editing EAP-TLS Wifi. * We currently only support CA cert in x.509 format and client cert in PKCS12 format. */ public class WifiEapTlsCreateDialogFragment extends DialogFragment { private final static int REQUEST_CA_CERT = 1; private final static int REQUEST_USER_CERT = 2; private final static String ARG_CONFIG = "config"; private static final String TAG = "wifi_eap_tls"; private WifiConfiguration mWifiConfiguration; private Uri mCaCertUri; private Uri mUserCertUri; private EditText mSsidEditText; private TextView mCaCertTextView; private TextView mUserCertTextView; private EditText mCertPasswordEditText; private EditText mIdentityEditText; public static WifiEapTlsCreateDialogFragment newInstance(WifiConfiguration config) { Bundle arguments = new Bundle(); arguments.putParcelable(ARG_CONFIG, config); WifiEapTlsCreateDialogFragment fragment = new WifiEapTlsCreateDialogFragment(); fragment.setArguments(arguments); return fragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWifiConfiguration = getArguments().getParcelable(ARG_CONFIG); if (mWifiConfiguration == null) { mWifiConfiguration = new WifiConfiguration(); } } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { LayoutInflater inflater = LayoutInflater.from(getActivity()); View rootView = inflater.inflate(R.layout.eap_tls_wifi_config_dialog, null); rootView.findViewById(R.id.import_ca_cert).setOnClickListener( new ImportButtonOnClickListener(REQUEST_CA_CERT, "application/x-x509-ca-cert")); rootView.findViewById(R.id.import_user_cert).setOnClickListener( new ImportButtonOnClickListener(REQUEST_USER_CERT, "application/x-pkcs12")); mCaCertTextView = (TextView) rootView.findViewById(R.id.selected_ca_cert); mUserCertTextView = (TextView) rootView.findViewById(R.id.selected_user_cert); mSsidEditText = (EditText) rootView.findViewById(R.id.ssid); mCertPasswordEditText = (EditText) rootView.findViewById(R.id.wifi_client_cert_password); mIdentityEditText = (EditText) rootView.findViewById(R.id.wifi_identity); populateUi(); final AlertDialog dialog = new AlertDialog.Builder(getActivity()) .setTitle(R.string.create_eap_tls_wifi_configuration) .setView(rootView) .setPositiveButton(R.string.wifi_save, null) .setNegativeButton(R.string.wifi_cancel, null) .create(); dialog.setOnShowListener(new DialogInterface.OnShowListener() { @Override public void onShow(DialogInterface dialogInterface) { dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { // Only dismiss the dialog when we saved the config. if (extractInputDataAndSave()) { dialog.dismiss(); } } }); } }); return dialog; } private void populateUi() { if (mWifiConfiguration == null) { return; } if (!TextUtils.isEmpty(mWifiConfiguration.SSID)) { mSsidEditText.setText(mWifiConfiguration.SSID.replace("\"", "")); } mIdentityEditText.setText(mWifiConfiguration.enterpriseConfig.getIdentity()); // Both ca cert and client are not populated in the WifiConfiguration object. updateSelectedCert(mCaCertTextView, null); updateSelectedCert(mUserCertTextView, null); } private boolean extractInputDataAndSave() { String ssid = mSsidEditText.getText().toString(); if (TextUtils.isEmpty(ssid)) { mSsidEditText.setError(getString(R.string.error_missing_ssid)); return false; } else { mSsidEditText.setError(null); } if (mCaCertUri == null) { showToast(R.string.error_missing_ca_cert); return false; } if (mUserCertUri == null) { showToast(R.string.error_missing_client_cert); return false; } X509Certificate caCert = parseX509Certificate(mCaCertUri); String certPassword = mCertPasswordEditText.getText().toString(); CertificateUtil.PKCS12ParseInfo parseInfo = null; try { parseInfo = CertificateUtil.parsePKCS12Certificate( getActivity().getContentResolver(), mUserCertUri, certPassword); } catch (KeyStoreException | NoSuchAlgorithmException | IOException | CertificateException | UnrecoverableKeyException e) { Log.e(TAG, "Fail to parse the input certificate: ", e); } if (parseInfo == null) { showToast(R.string.error_missing_client_cert); return false; } String identity = mIdentityEditText.getText().toString(); boolean success = saveWifiConfiguration(ssid, caCert, parseInfo.privateKey, parseInfo.certificate, identity); if (success) { showToast(R.string.wifi_configs_header); return true; } else { showToast(R.string.wifi_config_fail); } return false; } private boolean saveWifiConfiguration(String ssid, X509Certificate caCert, PrivateKey privateKey, X509Certificate userCert, String identity) { mWifiConfiguration.SSID = ssid; mWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); mWifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.TLS); enterpriseConfig.setCaCertificate(caCert); enterpriseConfig.setClientKeyEntry(privateKey, userCert); if (!TextUtils.isEmpty(identity)) { enterpriseConfig.setIdentity(identity); } mWifiConfiguration.enterpriseConfig = enterpriseConfig; return WifiConfigUtil.saveWifiConfiguration(getActivity(), mWifiConfiguration); } /** * @param uri of the x509 certificate * @return the X509Certificate object */ private X509Certificate parseX509Certificate(Uri uri) { try { CertificateFactory factory = CertificateFactory.getInstance("X.509"); InputStream inputStream = getActivity().getContentResolver().openInputStream(uri); return (X509Certificate) factory.generateCertificate(inputStream); } catch (IOException | CertificateException ex) { Log.e(TAG, "parseX509Certificate: ", ex); return null; } } private class ImportButtonOnClickListener implements View.OnClickListener { private int mRequestCode; private String mMimeType; public ImportButtonOnClickListener(int requestCode, String mimeType) { mRequestCode = requestCode; mMimeType = mimeType; } @Override public void onClick(View view) { Intent certIntent = new Intent(Intent.ACTION_GET_CONTENT); certIntent.setTypeAndNormalize(mMimeType); try { startActivityForResult(certIntent, mRequestCode); } catch (ActivityNotFoundException e) { Log.e(TAG, "no file picker: ", e); } } } private void updateSelectedCert(TextView textView, Uri uri) { String displayName = null; if (uri == null) { displayName = getString(R.string.selected_certificate_none); } else { final String[] projection = {MediaStore.MediaColumns.DISPLAY_NAME}; Cursor cursor = getActivity().getContentResolver().query(uri, projection, null, null, null); if (cursor != null) { try { if (cursor.moveToFirst()) { displayName = cursor.getString(0); } } finally { cursor.close(); } } if (TextUtils.isEmpty(getString(R.string.wifi_unknown_cert))) { displayName = getString(R.string.wifi_unknown_cert); } } String selectedText = getString(R.string.selected_certificate, displayName); textView.setText(selectedText); } private void showToast(int message) { Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show(); } @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if (resultCode == Activity.RESULT_OK) { switch (requestCode) { case REQUEST_CA_CERT: mCaCertUri = intent.getData(); updateSelectedCert(mCaCertTextView, mCaCertUri); break; case REQUEST_USER_CERT: mUserCertUri = intent.getData(); updateSelectedCert(mUserCertTextView, mUserCertUri); break; } } } }