Credential storage triggers unlock when keystore is locked.
If the phone is in the unlocked state and keystore is locked, storing credentials asks for a password that does not exist to the user. Replace this workflow with a key guard confirmation, asking the user to unlock the screen in the same way they would normally unlock their phone. Bug: 68298609 Test: adb push sample_credentials.p12 /sdcard/ Test: adb shell su 1000 service call android.security.keystore 9 i32 0 Test: adb shell am start -a android.credentials.INSTALL --user 10 Test: adb shell su 1000 service call android.security.keystore 9 i32 10 Change-Id: I8a3068a5d7de508fb417016acdf41b1712a2e7cc
This commit is contained in:
@@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2011 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.
|
||||
-->
|
||||
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="15dip">
|
||||
|
||||
<TextView android:id="@+id/hint"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10sp"/>
|
||||
|
||||
<TextView android:id="@+id/error"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="10sp"
|
||||
android:textColor="@color/red"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView android:id="@+id/old_password_prompt"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/credentials_old_password"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<EditText android:id="@+id/old_password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:password="true"
|
||||
android:singleLine="true"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@@ -5613,27 +5613,12 @@
|
||||
<string name="credential_for_vpn_and_apps">Installed for VPN and apps</string>
|
||||
<!-- Sub-heading for a user credential installed to be used as part of a Wi-Fi configuration. [CHAR LIMIT=NONE]. -->
|
||||
<string name="credential_for_wifi">Installed for Wi-Fi</string>
|
||||
|
||||
<!-- Title of dialog to enable credential storage [CHAR LIMIT=30] -->
|
||||
<string name="credentials_unlock"></string>
|
||||
<!-- Description of dialog to enable credential storage [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_unlock_hint">Type the password for credential storage.</string>
|
||||
<!-- Description of the input box for the old password [CHAR LIMIT=30] -->
|
||||
<string name="credentials_old_password">Current password:</string>
|
||||
<!-- Description of dialog to reset credential storage [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_reset_hint">Remove all the contents?</string>
|
||||
<!-- Error message [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_wrong_password">Incorrect password.</string>
|
||||
<!-- Error message [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_reset_warning">Incorrect password. You have one more chance before credential storage is erased.</string>
|
||||
<!-- Error message [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_reset_warning_plural">Incorrect password. You have <xliff:g id="number" example="5">%1$d</xliff:g> more chances before credential storage is erased.</string>
|
||||
<!-- Toast message [CHAR LIMIT=30] -->
|
||||
<string name="credentials_erased">Credential storage is erased.</string>
|
||||
<!-- Toast message [CHAR LIMIT=30] when credential storage containing private keys and certificates could not be erased (opposite of string credentials_erased) -->
|
||||
<string name="credentials_not_erased">Credential storage couldn\u2019t be erased.</string>
|
||||
<!-- Toast message [CHAR LIMIT=30] -->
|
||||
<string name="credentials_enabled">Credential storage is enabled.</string>
|
||||
<!-- This string is in a dialog, and the dialog shows up on a device that's managed by a user's company. It lets the user know that they need to have a secure lock screen (PIN, password, or pattern) before they can use credential storage [CHAR LIMIT=NONE] -->
|
||||
<string name="credentials_configure_lock_screen_hint">Before you can use credential storage, your device need to have a secure lock screen</string>
|
||||
<!-- This string is for the content of the button that leads user to lock screen settings [CHAR LIMIT=20] -->
|
||||
|
@@ -35,25 +35,17 @@ import android.security.Credentials;
|
||||
import android.security.KeyChain;
|
||||
import android.security.KeyChain.KeyChainConnection;
|
||||
import android.security.KeyStore;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.widget.LockPatternUtils;
|
||||
import com.android.org.bouncycastle.asn1.ASN1InputStream;
|
||||
import com.android.org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
import com.android.settings.security.ConfigureKeyGuardDialog;
|
||||
import com.android.settings.vpn2.VpnUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import sun.security.util.ObjectIdentifier;
|
||||
import sun.security.x509.AlgorithmId;
|
||||
|
||||
@@ -78,8 +70,8 @@ import sun.security.x509.AlgorithmId;
|
||||
*
|
||||
* KeyStore: LOCKED
|
||||
* KeyGuard: OFF/ON
|
||||
* Action: old unlock dialog
|
||||
* Notes: assume old password, need to use it to unlock.
|
||||
* Action: confirm key guard
|
||||
* Notes: request normal unlock to unlock the keystore.
|
||||
* if unlock, ensure key guard before install.
|
||||
* if reset, treat as UNINITALIZED/OFF
|
||||
*
|
||||
@@ -115,14 +107,6 @@ public final class CredentialStorage extends Activity {
|
||||
*/
|
||||
private Bundle mInstallBundle;
|
||||
|
||||
/**
|
||||
* After unsuccessful KeyStore.unlock, the number of unlock
|
||||
* attempts remaining before the KeyStore will reset itself.
|
||||
*
|
||||
* Reset to -1 on successful unlock or reset.
|
||||
*/
|
||||
private int mRetriesRemaining = -1;
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
@@ -166,11 +150,12 @@ public final class CredentialStorage extends Activity {
|
||||
return;
|
||||
}
|
||||
case LOCKED: {
|
||||
new UnlockDialog();
|
||||
// Force key guard confirmation
|
||||
confirmKeyGuard(CONFIRM_KEY_GUARD_REQUEST);
|
||||
return;
|
||||
}
|
||||
case UNLOCKED: {
|
||||
if (!checkKeyGuardQuality()) {
|
||||
if (isActivePasswordQualityInsufficient()) {
|
||||
final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
|
||||
dialog.show(getFragmentManager(), ConfigureKeyGuardDialog.TAG);
|
||||
return;
|
||||
@@ -189,7 +174,7 @@ public final class CredentialStorage extends Activity {
|
||||
* case after unlocking with an old-style password).
|
||||
*/
|
||||
private void ensureKeyGuard() {
|
||||
if (!checkKeyGuardQuality()) {
|
||||
if (isActivePasswordQualityInsufficient()) {
|
||||
// key guard not setup, doing so will initialize keystore
|
||||
final ConfigureKeyGuardDialog dialog = new ConfigureKeyGuardDialog();
|
||||
dialog.show(getFragmentManager(), ConfigureKeyGuardDialog.TAG);
|
||||
@@ -205,9 +190,9 @@ public final class CredentialStorage extends Activity {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the currently set key guard matches our minimum quality requirements.
|
||||
* Returns true if the currently set key guard violates our minimum quality requirements.
|
||||
*/
|
||||
private boolean checkKeyGuardQuality() {
|
||||
private boolean isActivePasswordQualityInsufficient() {
|
||||
final int credentialOwner =
|
||||
UserManager.get(this).getCredentialOwnerProfile(UserHandle.myUserId());
|
||||
final int quality = new LockPatternUtils(this).getActivePasswordQuality(credentialOwner);
|
||||
@@ -457,11 +442,8 @@ public final class CredentialStorage extends Activity {
|
||||
|
||||
final UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
|
||||
final UserInfo parentInfo = userManager.getProfileParent(launchedFromUserId);
|
||||
if (parentInfo == null || parentInfo.id != UserHandle.myUserId()) {
|
||||
// Caller is not running in a profile of this user
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// Caller is running in a profile of this user
|
||||
return ((parentInfo != null) && (parentInfo.id == UserHandle.myUserId()));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -469,19 +451,15 @@ public final class CredentialStorage extends Activity {
|
||||
*/
|
||||
private boolean confirmKeyGuard(int requestCode) {
|
||||
final Resources res = getResources();
|
||||
boolean launched = new ChooseLockSettingsHelper(this)
|
||||
return new ChooseLockSettingsHelper(this)
|
||||
.launchConfirmationActivity(requestCode,
|
||||
res.getText(R.string.credentials_title), true);
|
||||
return launched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
/**
|
||||
* Receive key guard password initiated by confirmKeyGuard.
|
||||
*/
|
||||
// Receive key guard password initiated by confirmKeyGuard.
|
||||
if (requestCode == CONFIRM_KEY_GUARD_REQUEST) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
final String password = data.getStringExtra(ChooseLockSettingsHelper.EXTRA_KEY_PASSWORD);
|
||||
@@ -503,102 +481,4 @@ public final class CredentialStorage extends Activity {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt for unlock with old-style password.
|
||||
*
|
||||
* On successful unlock, ensure migration to key guard before continuing.
|
||||
* On unsuccessful unlock, retry by calling handleUnlockOrInstall.
|
||||
*/
|
||||
private class UnlockDialog implements TextWatcher,
|
||||
DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
|
||||
private boolean mUnlockConfirmed;
|
||||
|
||||
private final Button mButton;
|
||||
private final TextView mOldPassword;
|
||||
private final TextView mError;
|
||||
|
||||
private UnlockDialog() {
|
||||
final View view = View.inflate(
|
||||
CredentialStorage.this, R.layout.credentials_dialog, null);
|
||||
|
||||
final CharSequence text;
|
||||
if (mRetriesRemaining == -1) {
|
||||
text = getResources().getText(R.string.credentials_unlock_hint);
|
||||
} else if (mRetriesRemaining > 3) {
|
||||
text = getResources().getText(R.string.credentials_wrong_password);
|
||||
} else if (mRetriesRemaining == 1) {
|
||||
text = getResources().getText(R.string.credentials_reset_warning);
|
||||
} else {
|
||||
text = getString(R.string.credentials_reset_warning_plural, mRetriesRemaining);
|
||||
}
|
||||
|
||||
((TextView) view.findViewById(R.id.hint)).setText(text);
|
||||
mOldPassword = (TextView) view.findViewById(R.id.old_password);
|
||||
mOldPassword.setVisibility(View.VISIBLE);
|
||||
mOldPassword.addTextChangedListener(this);
|
||||
mError = (TextView) view.findViewById(R.id.error);
|
||||
|
||||
final AlertDialog dialog = new AlertDialog.Builder(CredentialStorage.this)
|
||||
.setView(view)
|
||||
.setTitle(R.string.credentials_unlock)
|
||||
.setPositiveButton(android.R.string.ok, this)
|
||||
.setNegativeButton(android.R.string.cancel, this)
|
||||
.create();
|
||||
dialog.setOnDismissListener(this);
|
||||
dialog.show();
|
||||
mButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||
mButton.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
mButton.setEnabled(mOldPassword == null || mOldPassword.getText().length() > 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int button) {
|
||||
mUnlockConfirmed = (button == DialogInterface.BUTTON_POSITIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
if (mUnlockConfirmed) {
|
||||
mUnlockConfirmed = false;
|
||||
mError.setVisibility(View.VISIBLE);
|
||||
mKeyStore.unlock(mOldPassword.getText().toString());
|
||||
final int error = mKeyStore.getLastError();
|
||||
if (error == KeyStore.NO_ERROR) {
|
||||
mRetriesRemaining = -1;
|
||||
Toast.makeText(CredentialStorage.this,
|
||||
R.string.credentials_enabled,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
// aha, now we are unlocked, switch to key guard.
|
||||
// we'll end up back in onResume to install
|
||||
ensureKeyGuard();
|
||||
} else if (error == KeyStore.UNINITIALIZED) {
|
||||
mRetriesRemaining = -1;
|
||||
Toast.makeText(CredentialStorage.this,
|
||||
R.string.credentials_erased,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
// we are reset, we can now set new password with key guard
|
||||
handleUnlockOrInstall();
|
||||
} else if (error >= KeyStore.WRONG_PASSWORD) {
|
||||
// we need to try again
|
||||
mRetriesRemaining = error - KeyStore.WRONG_PASSWORD + 1;
|
||||
handleUnlockOrInstall();
|
||||
}
|
||||
return;
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user