Merge "Add credential management app to platform"
This commit is contained in:
committed by
Android (Google) Code Review
commit
0a6b674c91
@@ -40978,6 +40978,19 @@ package android.se.omapi {
|
||||
|
||||
package android.security {
|
||||
|
||||
public final class AppUriAuthenticationPolicy implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method @NonNull public java.util.Map<java.lang.String,java.util.Map<android.net.Uri,java.lang.String>> getAppAndUriMappings();
|
||||
method public void writeToParcel(@NonNull android.os.Parcel, int);
|
||||
field @NonNull public static final android.os.Parcelable.Creator<android.security.AppUriAuthenticationPolicy> CREATOR;
|
||||
}
|
||||
|
||||
public static final class AppUriAuthenticationPolicy.Builder {
|
||||
ctor public AppUriAuthenticationPolicy.Builder();
|
||||
method @NonNull public android.security.AppUriAuthenticationPolicy.Builder addAppAndUriMapping(@NonNull String, @NonNull android.net.Uri, @NonNull String);
|
||||
method @NonNull public android.security.AppUriAuthenticationPolicy build();
|
||||
}
|
||||
|
||||
public final class AttestedKeyPair {
|
||||
ctor public AttestedKeyPair(@Nullable java.security.KeyPair, @NonNull java.util.List<java.security.cert.Certificate>);
|
||||
method @NonNull public java.util.List<java.security.cert.Certificate> getAttestationRecord();
|
||||
@@ -41025,6 +41038,7 @@ package android.security {
|
||||
method public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable String, int, @Nullable String);
|
||||
method public static void choosePrivateKeyAlias(@NonNull android.app.Activity, @NonNull android.security.KeyChainAliasCallback, @Nullable String[], @Nullable java.security.Principal[], @Nullable android.net.Uri, @Nullable String);
|
||||
method @NonNull public static android.content.Intent createInstallIntent();
|
||||
method @NonNull public static android.content.Intent createManageCredentialsIntent(@NonNull android.security.AppUriAuthenticationPolicy);
|
||||
method @Nullable @WorkerThread public static java.security.cert.X509Certificate[] getCertificateChain(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
|
||||
method @Nullable @WorkerThread public static java.security.PrivateKey getPrivateKey(@NonNull android.content.Context, @NonNull String) throws java.lang.InterruptedException, android.security.KeyChainException;
|
||||
method @Deprecated public static boolean isBoundKeyAlgorithm(@NonNull String);
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Xml;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public final class CredentialManagementAppTest {
|
||||
|
||||
private static final String TEST_PACKAGE_NAME_1 = "com.android.test";
|
||||
private static final String TEST_PACKAGE_NAME_2 = "com.android.test2";
|
||||
private static final Uri TEST_URI_1 = Uri.parse("test.com");
|
||||
private static final Uri TEST_URI_2 = Uri.parse("test2.com");
|
||||
private static final String TEST_ALIAS_1 = "testAlias";
|
||||
private static final String TEST_ALIAS_2 = "testAlias2";
|
||||
|
||||
private static final String PACKAGE_NAME = "com.android.cred.mng.pkg";
|
||||
private static final AppUriAuthenticationPolicy AUTHENTICATION_POLICY =
|
||||
new AppUriAuthenticationPolicy.Builder()
|
||||
.addAppAndUriMapping(TEST_PACKAGE_NAME_1, TEST_URI_1, TEST_ALIAS_1)
|
||||
.build();
|
||||
private static final CredentialManagementApp CREDENTIAL_MANAGEMENT_APP =
|
||||
new CredentialManagementApp(PACKAGE_NAME, AUTHENTICATION_POLICY);
|
||||
|
||||
private static final String TAG_CREDENTIAL_MANAGEMENT_APP = "credential-management-app";
|
||||
|
||||
@Test
|
||||
public void credentialManagementApp_getters() {
|
||||
CredentialManagementApp credentialManagementApp =
|
||||
new CredentialManagementApp(PACKAGE_NAME, AUTHENTICATION_POLICY);
|
||||
|
||||
assertThat(credentialManagementApp.getPackageName(), is(PACKAGE_NAME));
|
||||
assertThat(credentialManagementApp.getAuthenticationPolicy(), is(AUTHENTICATION_POLICY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setAuthenticationPolicy_updatesAuthenticationPolicy() {
|
||||
CredentialManagementApp credentialManagementApp =
|
||||
new CredentialManagementApp(PACKAGE_NAME, AUTHENTICATION_POLICY);
|
||||
AppUriAuthenticationPolicy updatedAuthenticationPolicy =
|
||||
new AppUriAuthenticationPolicy.Builder().addAppAndUriMapping(
|
||||
TEST_PACKAGE_NAME_2, TEST_URI_2, TEST_ALIAS_2).build();
|
||||
|
||||
credentialManagementApp.setAuthenticationPolicy(updatedAuthenticationPolicy);
|
||||
|
||||
assertThat(credentialManagementApp.getAuthenticationPolicy(),
|
||||
is(updatedAuthenticationPolicy));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_nullPackageName_throwException() {
|
||||
try {
|
||||
new CredentialManagementApp(/* packageName= */ null, AUTHENTICATION_POLICY);
|
||||
fail("Shall not take null inputs");
|
||||
} catch (NullPointerException expected) {
|
||||
// Expected behavior, nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructor_nullAuthenticationPolicy_throwException() {
|
||||
try {
|
||||
new CredentialManagementApp(PACKAGE_NAME, /* authenticationPolicy= */ null);
|
||||
fail("Shall not take null inputs");
|
||||
} catch (NullPointerException expected) {
|
||||
// Expected behavior, nothing to do.
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void writeToXmlAndReadFromXml() throws IOException, XmlPullParserException {
|
||||
File xmlFile = writeToXml(CREDENTIAL_MANAGEMENT_APP);
|
||||
|
||||
CredentialManagementApp loadedCredentialManagementApp = readFromXml(xmlFile);
|
||||
|
||||
assertCredentialManagementAppsEqual(loadedCredentialManagementApp,
|
||||
CREDENTIAL_MANAGEMENT_APP);
|
||||
}
|
||||
|
||||
private File writeToXml(CredentialManagementApp credentialManagementApp) throws IOException {
|
||||
File file = File.createTempFile("temp", "credmng");
|
||||
final FileOutputStream out = new FileOutputStream(file);
|
||||
XmlSerializer xml = Xml.newSerializer();
|
||||
xml.setOutput(out, StandardCharsets.UTF_8.name());
|
||||
xml.startDocument(null, true);
|
||||
xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
|
||||
xml.startTag(null, TAG_CREDENTIAL_MANAGEMENT_APP);
|
||||
credentialManagementApp.writeToXml(xml);
|
||||
xml.endTag(null, TAG_CREDENTIAL_MANAGEMENT_APP);
|
||||
xml.endDocument();
|
||||
out.close();
|
||||
return file;
|
||||
}
|
||||
|
||||
private CredentialManagementApp readFromXml(File file)
|
||||
throws IOException, XmlPullParserException {
|
||||
CredentialManagementApp credentialManagementApp = null;
|
||||
final XmlPullParser parser = Xml.newPullParser();
|
||||
final FileInputStream in = new FileInputStream(file);
|
||||
parser.setInput(in, StandardCharsets.UTF_8.name());
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.START_TAG
|
||||
&& type != XmlPullParser.END_DOCUMENT) {
|
||||
}
|
||||
String tag = parser.getName();
|
||||
if (TAG_CREDENTIAL_MANAGEMENT_APP.equals(tag)) {
|
||||
credentialManagementApp = CredentialManagementApp.readFromXml(parser);
|
||||
}
|
||||
return credentialManagementApp;
|
||||
}
|
||||
|
||||
private void assertCredentialManagementAppsEqual(CredentialManagementApp actual,
|
||||
CredentialManagementApp expected) {
|
||||
assertThat(actual.getPackageName(), is(expected.getPackageName()));
|
||||
assertAuthenticationPoliciesEqual(actual.getAuthenticationPolicy(),
|
||||
expected.getAuthenticationPolicy());
|
||||
}
|
||||
|
||||
private void assertAuthenticationPoliciesEqual(AppUriAuthenticationPolicy actual,
|
||||
AppUriAuthenticationPolicy expected) {
|
||||
Iterator<Map.Entry<String, Map<Uri, String>>> actualIter =
|
||||
actual.getAppAndUriMappings().entrySet().iterator();
|
||||
Iterator<Map.Entry<String, Map<Uri, String>>> expectedIter =
|
||||
expected.getAppAndUriMappings().entrySet().iterator();
|
||||
|
||||
assertThat(actual.getAppAndUriMappings().size(),
|
||||
is(expected.getAppAndUriMappings().size()));
|
||||
while (actualIter.hasNext()) {
|
||||
Map.Entry<String, Map<Uri, String>> actualAppToUri = actualIter.next();
|
||||
Map.Entry<String, Map<Uri, String>> expectedAppToUri = expectedIter.next();
|
||||
assertThat(actualAppToUri.getKey(), is(expectedAppToUri.getKey()));
|
||||
assertUrisToAliasesEqual(actualAppToUri.getValue(), expectedAppToUri.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void assertUrisToAliasesEqual(Map<Uri, String> actual, Map<Uri, String> expected) {
|
||||
Iterator<Map.Entry<Uri, String>> actualIter = actual.entrySet().iterator();
|
||||
Iterator<Map.Entry<Uri, String>> expectedIter = expected.entrySet().iterator();
|
||||
|
||||
assertThat(actual.size(), is(expected.size()));
|
||||
while (actualIter.hasNext()) {
|
||||
Map.Entry<Uri, String> actualUriToAlias = actualIter.next();
|
||||
Map.Entry<Uri, String> expectedUriToAlias = expectedIter.next();
|
||||
assertThat(actualUriToAlias.getKey(), is(expectedUriToAlias.getKey()));
|
||||
assertThat(actualUriToAlias.getValue(), is(expectedUriToAlias.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
parcelable AppUriAuthenticationPolicy;
|
||||
226
keystore/java/android/security/AppUriAuthenticationPolicy.java
Normal file
226
keystore/java/android/security/AppUriAuthenticationPolicy.java
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The app-URI authentication policy is set by the credential management app. This policy determines
|
||||
* which alias for a private key and certificate pair should be used for authentication.
|
||||
* <p>
|
||||
* The authentication policy should be added as a parameter when calling
|
||||
* {@link KeyChain#createManageCredentialsIntent}.
|
||||
* <p>
|
||||
* Example:
|
||||
* <pre>{@code
|
||||
* AppUriAuthenticationPolicy authenticationPolicy = new AppUriAuthenticationPolicy.Builder()
|
||||
* .addAppAndUriMapping("com.test.pkg", testUri, "testAlias")
|
||||
* .addAppAndUriMapping("com.test2.pkg", testUri1, "testAlias2")
|
||||
* .addAppAndUriMapping("com.test2.pkg", testUri2, "testAlias2")
|
||||
* .build();
|
||||
* Intent requestIntent = KeyChain.createManageCredentialsIntent(authenticationPolicy);
|
||||
* }</pre>
|
||||
* <p>
|
||||
*/
|
||||
public final class AppUriAuthenticationPolicy implements Parcelable {
|
||||
|
||||
private static final String KEY_AUTHENTICATION_POLICY_APP_TO_URIS =
|
||||
"authentication_policy_app_to_uris";
|
||||
private static final String KEY_AUTHENTICATION_POLICY_APP = "policy_app";
|
||||
|
||||
/**
|
||||
* The mappings from an app and list of URIs to a list of aliases, which will be used for
|
||||
* authentication.
|
||||
* <p>
|
||||
* appPackageName -> uri -> alias
|
||||
*/
|
||||
@NonNull
|
||||
private final Map<String, UrisToAliases> mAppToUris;
|
||||
|
||||
private AppUriAuthenticationPolicy(@NonNull Map<String, UrisToAliases> appToUris) {
|
||||
Objects.requireNonNull(appToUris);
|
||||
this.mAppToUris = appToUris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder class for {@link AppUriAuthenticationPolicy} objects.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private Map<String, UrisToAliases> mPackageNameToUris;
|
||||
|
||||
/**
|
||||
* Initialize a new Builder to construct an {@link AppUriAuthenticationPolicy}.
|
||||
*/
|
||||
public Builder() {
|
||||
mPackageNameToUris = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds mappings from an app and URI to an alias, which will be used for authentication.
|
||||
* <p>
|
||||
* If this method is called with a package name and URI that was previously added, the
|
||||
* previous alias will be overwritten.
|
||||
*
|
||||
* @param appPackageName The app's package name to authenticate the user to.
|
||||
* @param uri The URI to authenticate the user to.
|
||||
* @param alias The alias which will be used for authentication.
|
||||
*
|
||||
* @return the same Builder instance.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addAppAndUriMapping(@NonNull String appPackageName, @NonNull Uri uri,
|
||||
@NonNull String alias) {
|
||||
Objects.requireNonNull(appPackageName);
|
||||
Objects.requireNonNull(uri);
|
||||
Objects.requireNonNull(alias);
|
||||
UrisToAliases urisToAliases =
|
||||
mPackageNameToUris.getOrDefault(appPackageName, new UrisToAliases());
|
||||
urisToAliases.addUriToAlias(uri, alias);
|
||||
mPackageNameToUris.put(appPackageName, urisToAliases);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds mappings from an app and list of URIs to a list of aliases, which will be used for
|
||||
* authentication.
|
||||
* <p>
|
||||
* appPackageName -> uri -> alias
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Builder addAppAndUriMapping(@NonNull String appPackageName,
|
||||
@NonNull UrisToAliases urisToAliases) {
|
||||
Objects.requireNonNull(appPackageName);
|
||||
Objects.requireNonNull(urisToAliases);
|
||||
mPackageNameToUris.put(appPackageName, urisToAliases);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines all of the attributes that have been set on the {@link Builder}
|
||||
*
|
||||
* @return a new {@link AppUriAuthenticationPolicy} object.
|
||||
*/
|
||||
@NonNull
|
||||
public AppUriAuthenticationPolicy build() {
|
||||
return new AppUriAuthenticationPolicy(mPackageNameToUris);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeMap(mAppToUris);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Parcelable.Creator<AppUriAuthenticationPolicy> CREATOR =
|
||||
new Parcelable.Creator<AppUriAuthenticationPolicy>() {
|
||||
@Override
|
||||
public AppUriAuthenticationPolicy createFromParcel(Parcel in) {
|
||||
Map<String, UrisToAliases> appToUris = new HashMap<>();
|
||||
in.readMap(appToUris, UrisToAliases.class.getClassLoader());
|
||||
return new AppUriAuthenticationPolicy(appToUris);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppUriAuthenticationPolicy[] newArray(int size) {
|
||||
return new AppUriAuthenticationPolicy[size];
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AppUriAuthenticationPolicy{"
|
||||
+ "mPackageNameToUris=" + mAppToUris
|
||||
+ '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the authentication policy mapping, which determines which alias for a private key
|
||||
* and certificate pair should be used for authentication.
|
||||
* <p>
|
||||
* appPackageName -> uri -> alias
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, Map<Uri, String>> getAppAndUriMappings() {
|
||||
Map<String, Map<Uri, String>> appAndUris = new HashMap<>();
|
||||
for (Map.Entry<String, UrisToAliases> entry : mAppToUris.entrySet()) {
|
||||
appAndUris.put(entry.getKey(), entry.getValue().getUrisToAliases());
|
||||
}
|
||||
return appAndUris;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a previously saved {@link AppUriAuthenticationPolicy} from XML.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public static AppUriAuthenticationPolicy readFromXml(@NonNull XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
AppUriAuthenticationPolicy.Builder builder = new AppUriAuthenticationPolicy.Builder();
|
||||
int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
if (!parser.getName().equals(KEY_AUTHENTICATION_POLICY_APP_TO_URIS)) {
|
||||
continue;
|
||||
}
|
||||
String app = parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_APP);
|
||||
UrisToAliases urisToAliases = UrisToAliases.readFromXml(parser);
|
||||
builder.addAppAndUriMapping(app, urisToAliases);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the {@link AppUriAuthenticationPolicy} to XML.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
for (Map.Entry<String, UrisToAliases> appsToUris : mAppToUris.entrySet()) {
|
||||
out.startTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS);
|
||||
out.attribute(null, KEY_AUTHENTICATION_POLICY_APP, appsToUris.getKey());
|
||||
appsToUris.getValue().writeToXml(out);
|
||||
out.endTag(null, KEY_AUTHENTICATION_POLICY_APP_TO_URIS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
123
keystore/java/android/security/CredentialManagementApp.java
Normal file
123
keystore/java/android/security/CredentialManagementApp.java
Normal file
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.util.Log;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* The credential management app has the ability to manage the user's KeyChain credentials on
|
||||
* unmanaged devices. {@link KeyChain#createManageCredentialsIntent} should be used by an app to
|
||||
* request to become the credential management app. The user must approve this request before the
|
||||
* app can manage the user's credentials.
|
||||
* <p>
|
||||
* Note: there can only be one credential management on the device. If another app requests to
|
||||
* become the credential management app and the user approves, then the existing credential
|
||||
* management app will no longer be able to manage credentials.
|
||||
* <p>
|
||||
* The requesting credential management app should include its authentication policy in the
|
||||
* requesting intent. The authentication policy declares which certificates should be used for a
|
||||
* given list of apps and URIs.
|
||||
*
|
||||
* @hide
|
||||
* @see AppUriAuthenticationPolicy
|
||||
*/
|
||||
public class CredentialManagementApp {
|
||||
|
||||
private static final String TAG = "CredentialManagementApp";
|
||||
private static final String KEY_PACKAGE_NAME = "package_name";
|
||||
|
||||
/**
|
||||
* The credential management app's package name
|
||||
*/
|
||||
@NonNull
|
||||
private final String mPackageName;
|
||||
|
||||
/**
|
||||
* The mappings from an app and list of URIs to a list of aliases, which will be used for
|
||||
* authentication.
|
||||
* <p>
|
||||
* appPackageName -> uri -> alias
|
||||
*/
|
||||
@NonNull
|
||||
private AppUriAuthenticationPolicy mAuthenticationPolicy;
|
||||
|
||||
public CredentialManagementApp(@NonNull String packageName,
|
||||
@NonNull AppUriAuthenticationPolicy authenticationPolicy) {
|
||||
Objects.requireNonNull(packageName);
|
||||
Objects.requireNonNull(authenticationPolicy);
|
||||
mPackageName = packageName;
|
||||
mAuthenticationPolicy = authenticationPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the package name of the credential management app.
|
||||
*/
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the authentication policy of the credential management app.
|
||||
*/
|
||||
@NonNull
|
||||
public AppUriAuthenticationPolicy getAuthenticationPolicy() {
|
||||
return mAuthenticationPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the authentication policy of the credential management app.
|
||||
*/
|
||||
public void setAuthenticationPolicy(@Nullable AppUriAuthenticationPolicy authenticationPolicy) {
|
||||
Objects.requireNonNull(authenticationPolicy);
|
||||
mAuthenticationPolicy = authenticationPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a previously saved {@link CredentialManagementApp} from XML.
|
||||
*/
|
||||
@Nullable
|
||||
public static CredentialManagementApp readFromXml(@NonNull XmlPullParser parser) {
|
||||
try {
|
||||
String packageName = parser.getAttributeValue(null, KEY_PACKAGE_NAME);
|
||||
AppUriAuthenticationPolicy policy = AppUriAuthenticationPolicy.readFromXml(parser);
|
||||
return new CredentialManagementApp(packageName, policy);
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
Log.w(TAG, "Reading from xml failed", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the {@link CredentialManagementApp} to XML.
|
||||
*/
|
||||
public void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
out.attribute(null, KEY_PACKAGE_NAME, mPackageName);
|
||||
if (mAuthenticationPolicy != null) {
|
||||
mAuthenticationPolicy.writeToXml(out);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,8 @@ public class Credentials {
|
||||
|
||||
public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER";
|
||||
|
||||
public static final String ACTION_MANAGE_CREDENTIALS = "android.security.MANAGE_CREDENTIALS";
|
||||
|
||||
/**
|
||||
* Key prefix for CA certificates.
|
||||
*
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package android.security;
|
||||
|
||||
import static android.security.Credentials.ACTION_MANAGE_CREDENTIALS;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SdkConstant;
|
||||
@@ -121,6 +123,11 @@ public final class KeyChain {
|
||||
*/
|
||||
private static final String CERT_INSTALLER_PACKAGE = "com.android.certinstaller";
|
||||
|
||||
/**
|
||||
* Package name for Settings.
|
||||
*/
|
||||
private static final String SETTINGS_PACKAGE = "com.android.settings";
|
||||
|
||||
/**
|
||||
* Extra for use with {@link #ACTION_CHOOSER}
|
||||
* @hide Also used by KeyChainActivity implementation
|
||||
@@ -201,6 +208,20 @@ public final class KeyChain {
|
||||
// Compatible with old android.security.Credentials.PKCS12
|
||||
public static final String EXTRA_PKCS12 = "PKCS12";
|
||||
|
||||
/**
|
||||
* Extra used by {@link #createManageCredentialsIntent(AppUriAuthenticationPolicy)} to specify
|
||||
* the authentication policy of the credential management app.
|
||||
*
|
||||
* <p>The authentication policy declares which alias for a private key and certificate pair
|
||||
* should be used for authentication, given a list of apps and URIs.
|
||||
*
|
||||
* <p>The extra value should be a {@link AppUriAuthenticationPolicy}.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final String EXTRA_AUTHENTICATION_POLICY =
|
||||
"android.security.extra.AUTHENTICATION_POLICY";
|
||||
|
||||
/**
|
||||
* Broadcast Action: Indicates the trusted storage has changed. Sent when
|
||||
* one of this happens:
|
||||
@@ -385,6 +406,23 @@ public final class KeyChain {
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@code Intent} that should be used by an app to request to manage the user's
|
||||
* credentials. This is limited to unmanaged devices. The authentication policy must be
|
||||
* provided to be able to make this request successfully.
|
||||
*
|
||||
* @param policy The authentication policy determines which alias for a private key and
|
||||
* certificate pair should be used for authentication.
|
||||
*/
|
||||
@NonNull
|
||||
public static Intent createManageCredentialsIntent(@NonNull AppUriAuthenticationPolicy policy) {
|
||||
Intent intent = new Intent(ACTION_MANAGE_CREDENTIALS);
|
||||
intent.setComponent(ComponentName.createRelative(SETTINGS_PACKAGE,
|
||||
".security.RequestManageCredentials"));
|
||||
intent.putExtra(EXTRA_AUTHENTICATION_POLICY, policy);
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launches an {@code Activity} for the user to select the alias
|
||||
* for a private key and certificate pair for authentication. The
|
||||
|
||||
138
keystore/java/android/security/UrisToAliases.java
Normal file
138
keystore/java/android/security/UrisToAliases.java
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.net.Uri;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The mapping from URI to alias, which determines the alias to use when the user visits a URI.
|
||||
* This mapping is part of the {@link AppUriAuthenticationPolicy}, which specifies which app this
|
||||
* mapping should be used for.
|
||||
*
|
||||
* @hide
|
||||
* @see AppUriAuthenticationPolicy
|
||||
*/
|
||||
public final class UrisToAliases implements Parcelable {
|
||||
|
||||
private static final String KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS =
|
||||
"authentication_policy_uri_to_alias";
|
||||
private static final String KEY_AUTHENTICATION_POLICY_URI = "policy_uri";
|
||||
private static final String KEY_AUTHENTICATION_POLICY_ALIAS = "policy_alias";
|
||||
|
||||
/**
|
||||
* The mappings from URIs to aliases, which will be used for authentication.
|
||||
*/
|
||||
@NonNull
|
||||
private final Map<Uri, String> mUrisToAliases;
|
||||
|
||||
public UrisToAliases() {
|
||||
this.mUrisToAliases = new HashMap<>();
|
||||
}
|
||||
|
||||
private UrisToAliases(@NonNull Map<Uri, String> urisToAliases) {
|
||||
this.mUrisToAliases = urisToAliases;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static final Creator<UrisToAliases> CREATOR = new Creator<UrisToAliases>() {
|
||||
@Override
|
||||
public UrisToAliases createFromParcel(Parcel in) {
|
||||
Map<Uri, String> urisToAliases = new HashMap<>();
|
||||
in.readMap(urisToAliases, String.class.getClassLoader());
|
||||
return new UrisToAliases(urisToAliases);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UrisToAliases[] newArray(int size) {
|
||||
return new UrisToAliases[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the mapping from URIs to aliases.
|
||||
*/
|
||||
@NonNull
|
||||
public Map<Uri, String> getUrisToAliases() {
|
||||
return Collections.unmodifiableMap(mUrisToAliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds mapping from an URI to an alias.
|
||||
*/
|
||||
public void addUriToAlias(@NonNull Uri uri, @NonNull String alias) {
|
||||
mUrisToAliases.put(uri, alias);
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore a previously saved {@link UrisToAliases} from XML.
|
||||
*/
|
||||
@Nullable
|
||||
public static UrisToAliases readFromXml(@NonNull XmlPullParser parser)
|
||||
throws IOException, XmlPullParserException {
|
||||
Map<Uri, String> urisToAliases = new HashMap<>();
|
||||
int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
if (!parser.getName().equals(KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS)) {
|
||||
continue;
|
||||
}
|
||||
Uri uri = Uri.parse(parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_URI));
|
||||
String alias = parser.getAttributeValue(null, KEY_AUTHENTICATION_POLICY_ALIAS);
|
||||
urisToAliases.put(uri, alias);
|
||||
}
|
||||
return new UrisToAliases(urisToAliases);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the {@link UrisToAliases} to XML.
|
||||
*/
|
||||
public void writeToXml(@NonNull XmlSerializer out) throws IOException {
|
||||
for (Map.Entry<Uri, String> urisToAliases : mUrisToAliases.entrySet()) {
|
||||
out.startTag(null, KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS);
|
||||
out.attribute(null, KEY_AUTHENTICATION_POLICY_URI, urisToAliases.getKey().toString());
|
||||
out.attribute(null, KEY_AUTHENTICATION_POLICY_ALIAS, urisToAliases.getValue());
|
||||
out.endTag(null, KEY_AUTHENTICATION_POLICY_URI_TO_ALIAS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(@NonNull Parcel dest, int flags) {
|
||||
dest.writeMap(mUrisToAliases);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user