Merge "Add KeyChainService credential management app APIs"

This commit is contained in:
Rubin Xu
2021-02-14 17:06:48 +00:00
committed by Gerrit Code Review
6 changed files with 543 additions and 0 deletions

View File

@@ -35970,6 +35970,19 @@ package android.se.omapi {
package android.security { 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 { public final class AttestedKeyPair {
ctor public AttestedKeyPair(@Nullable java.security.KeyPair, @NonNull java.util.List<java.security.cert.Certificate>); 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(); method @NonNull public java.util.List<java.security.cert.Certificate> getAttestationRecord();

View File

@@ -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;

View File

@@ -0,0 +1,241 @@
/*
* 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.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
* 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);
}
}
/**
* Get the set of aliases found in the policy.
*
* @hide
*/
public Set<String> getAliases() {
Set<String> aliases = new HashSet<>();
for (UrisToAliases appsToUris : mAppToUris.values()) {
aliases.addAll(appsToUris.getUrisToAliases().values());
}
return aliases;
}
}

View 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);
}
}
}

View File

@@ -18,6 +18,8 @@ package android.security;
import android.content.pm.StringParceledListSlice; import android.content.pm.StringParceledListSlice;
import android.security.keymaster.KeymasterCertificateChain; import android.security.keymaster.KeymasterCertificateChain;
import android.security.keystore.ParcelableKeyGenParameterSpec; import android.security.keystore.ParcelableKeyGenParameterSpec;
import android.security.AppUriAuthenticationPolicy;
import android.net.Uri;
/** /**
* Caller is required to ensure that {@link KeyStore#unlock * Caller is required to ensure that {@link KeyStore#unlock
@@ -56,6 +58,13 @@ interface IKeyChainService {
boolean containsCaAlias(String alias); boolean containsCaAlias(String alias);
byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem); byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem);
List<String> getCaCertificateChainAliases(String rootAlias, boolean includeDeletedSystem); List<String> getCaCertificateChainAliases(String rootAlias, boolean includeDeletedSystem);
void setCredentialManagementApp(String packageName, in AppUriAuthenticationPolicy policy);
void updateCredentialManagementAppPolicy(in AppUriAuthenticationPolicy policy);
boolean hasCredentialManagementApp();
String getCredentialManagementAppPackageName();
AppUriAuthenticationPolicy getCredentialManagementAppPolicy();
String getPredefinedAliasForPackageAndUri(String packageName, in Uri uri);
void removeCredentialManagementApp();
// APIs used by KeyChainActivity // APIs used by KeyChainActivity
void setGrant(int uid, String alias, boolean value); void setGrant(int uid, String alias, boolean value);

View 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);
}
}