Skip to content

Commit 2039ded

Browse files
committed
TestDPC: Support Device ID attestation
Support inclusion of an attestation record and requesting Device ID attestation when generating a key using TestDPC. If the generated key has attestation record and includes device identifiers, these are displayed in a dialog. Bug: 63388672,70246374 Test: Manually Change-Id: I8545cf7dbe9d70df25ef068e16b14c80f453ce98
1 parent 5138fe2 commit 2039ded

15 files changed

+1884
-125
lines changed

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ android {
2222
targetSdkVersion 28
2323
versionCode versionMajor * 1000 + versionMinor * 100 + versionBuild
2424
versionName "${versionMajor}.${versionMinor}.${versionBuild}"
25+
multiDexEnabled true
2526
}
2627

2728
buildTypes {
@@ -75,6 +76,7 @@ android {
7576
}
7677

7778
dependencies {
79+
compile 'com.android.support:multidex:1.0.1'
7880
compile 'com.android.support:preference-v14:27.0.2'
7981
compile 'com.android.support:recyclerview-v7:27.0.2'
8082
compile 'com.android.support:support-v13:27.0.2'
@@ -83,4 +85,5 @@ dependencies {
8385
compile(name: 'setup-wizard-lib-platform-release', ext: 'aar')
8486
compile 'org.bouncycastle:bcpkix-jdk15on:1.56'
8587
compile 'org.bouncycastle:bcprov-jdk15on:1.56'
88+
compile 'com.google.guava:guava:23.6-android'
8689
}

app/src/main/java/com/afwsamples/testdpc/policy/PolicyManagementFragment.java

Lines changed: 53 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616

1717
package com.afwsamples.testdpc.policy;
1818

19-
import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
20-
import static com.afwsamples.testdpc.common.preference.DpcPreferenceHelper.NO_CUSTOM_CONSTRIANT;
21-
2219
import android.accessibilityservice.AccessibilityServiceInfo;
2320
import android.accounts.Account;
2421
import android.accounts.AccountManager;
@@ -52,12 +49,8 @@
5249
import android.os.UserManager;
5350
import android.provider.MediaStore;
5451
import android.provider.Settings;
55-
import android.security.AttestedKeyPair;
5652
import android.security.KeyChain;
5753
import android.security.KeyChainAliasCallback;
58-
import android.security.KeyChainException;
59-
import android.security.keystore.KeyGenParameterSpec;
60-
import android.security.keystore.KeyProperties;
6154
import android.service.notification.NotificationListenerService;
6255
import android.support.annotation.Nullable;
6356
import android.support.annotation.RequiresApi;
@@ -85,6 +78,7 @@
8578
import android.widget.RadioButton;
8679
import android.widget.RadioGroup;
8780
import android.widget.Toast;
81+
8882
import com.afwsamples.testdpc.AddAccountActivity;
8983
import com.afwsamples.testdpc.BuildConfig;
9084
import com.afwsamples.testdpc.CrossProfileAppsFragment;
@@ -108,14 +102,15 @@
108102
import com.afwsamples.testdpc.policy.certificate.DelegatedCertInstallerFragment;
109103
import com.afwsamples.testdpc.policy.keyguard.LockScreenPolicyFragment;
110104
import com.afwsamples.testdpc.policy.keyguard.PasswordConstraintsFragment;
105+
import com.afwsamples.testdpc.policy.keymanagement.GenerateKeyAndCertificateTask;
106+
import com.afwsamples.testdpc.policy.keymanagement.SignAndVerifyTask;
111107
import com.afwsamples.testdpc.policy.locktask.KioskModeActivity;
112108
import com.afwsamples.testdpc.policy.locktask.LockTaskAppInfoArrayAdapter;
113109
import com.afwsamples.testdpc.policy.locktask.SetLockTaskFeaturesFragment;
114110
import com.afwsamples.testdpc.policy.networking.AlwaysOnVpnFragment;
115111
import com.afwsamples.testdpc.policy.networking.NetworkUsageStatsFragment;
116112
import com.afwsamples.testdpc.policy.resetpassword.ResetPasswordWithTokenFragment;
117113
import com.afwsamples.testdpc.policy.systemupdatepolicy.SystemUpdatePolicyFragment;
118-
import com.afwsamples.testdpc.policy.utils.CertificateUtils;
119114
import com.afwsamples.testdpc.policy.wifimanagement.WifiConfigCreationDialog;
120115
import com.afwsamples.testdpc.policy.wifimanagement.WifiEapTlsCreateDialogFragment;
121116
import com.afwsamples.testdpc.policy.wifimanagement.WifiModificationFragment;
@@ -128,18 +123,15 @@
128123
import com.afwsamples.testdpc.safetynet.SafetyNetFragment;
129124
import com.afwsamples.testdpc.transferownership.PickTransferComponentFragment;
130125
import com.afwsamples.testdpc.util.MainThreadExecutor;
126+
131127
import java.io.ByteArrayInputStream;
132128
import java.io.File;
133129
import java.io.FileNotFoundException;
134130
import java.io.IOException;
135131
import java.io.InputStream;
136-
import java.security.InvalidKeyException;
137132
import java.security.KeyStoreException;
138133
import java.security.NoSuchAlgorithmException;
139134
import java.security.PrivateKey;
140-
import java.security.PublicKey;
141-
import java.security.Signature;
142-
import java.security.SignatureException;
143135
import java.security.UnrecoverableKeyException;
144136
import java.security.cert.Certificate;
145137
import java.security.cert.CertificateException;
@@ -158,8 +150,9 @@
158150
import java.util.Set;
159151
import java.util.TimeZone;
160152
import java.util.stream.Collectors;
161-
import javax.security.auth.x500.X500Principal;
162-
import org.bouncycastle.operator.OperatorCreationException;
153+
154+
import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
155+
import static com.afwsamples.testdpc.common.preference.DpcPreferenceHelper.NO_CUSTOM_CONSTRIANT;
163156

164157
/**
165158
* Provides several device management functions.
@@ -1060,7 +1053,10 @@ public void alias(String alias) {
10601053
return;
10611054
}
10621055

1063-
new SignAndVerifyTask().execute(alias);
1056+
new SignAndVerifyTask(
1057+
getContext(), (int msgId, Object... args) -> {
1058+
showToast(msgId, args);
1059+
}).execute(alias);
10641060
}
10651061
}, null, null, null, null);
10661062
}
@@ -1313,115 +1309,14 @@ private boolean installKeyPair(final PrivateKey key, final Certificate cert, fin
13131309
}
13141310
}
13151311

1316-
class GenerateKeyAndCertificateTask extends AsyncTask<Void, Integer, Boolean> {
1317-
final String mAlias;
1318-
final boolean mIsUserSelectable;
1319-
1320-
GenerateKeyAndCertificateTask(String mAlias, boolean mIsUserSelectable) {
1321-
this.mAlias = mAlias;
1322-
this.mIsUserSelectable = mIsUserSelectable;
1323-
}
1324-
1325-
@TargetApi(28)
1326-
@Override
1327-
protected Boolean doInBackground(Void... voids) {
1328-
try {
1329-
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(mAlias,
1330-
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
1331-
.setKeySize(2048)
1332-
.setDigests(KeyProperties.DIGEST_SHA256)
1333-
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
1334-
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
1335-
.build();
1336-
AttestedKeyPair keyPair = mDevicePolicyManager.generateKeyPair(
1337-
mAdminComponentName, "RSA", keySpec, 0);
1338-
1339-
if (keyPair == null) {
1340-
return false;
1341-
}
1342-
1343-
X500Principal subject = new X500Principal("CN=TestDPC, O=Android, C=US");
1344-
// Self-signed certificate: Same subject and issuer.
1345-
X509Certificate selfSigned = CertificateUtils.createCertificate(
1346-
keyPair.getKeyPair(), subject, subject);
1347-
1348-
List<Certificate> certs = new ArrayList<Certificate>();
1349-
certs.add(selfSigned);
1350-
1351-
return mDevicePolicyManager.setKeyPairCertificate(
1352-
mAdminComponentName, mAlias, certs, mIsUserSelectable);
1353-
} catch (CertificateException e) {
1354-
Log.e(TAG, "Failed to create certificate", e);
1355-
} catch (OperatorCreationException e) {
1356-
Log.e(TAG, "Failed to create certificate", e);
1357-
} catch (IOException e) {
1358-
Log.e(TAG, "Failed to encode certificate", e);
1359-
}
1360-
1361-
return false;
1362-
}
1363-
1364-
@Override
1365-
protected void onPostExecute(Boolean result) {
1366-
if (result.booleanValue()) {
1367-
showToast(R.string.key_generation_successful, mAlias);
1368-
} else {
1369-
showToast(R.string.key_generation_failed, mAlias);
1370-
}
1371-
}
1372-
}
1373-
1374-
private void generateKeyPair(final String alias, boolean isUserSelectable) {
1375-
new GenerateKeyAndCertificateTask(alias, isUserSelectable).execute();
1312+
private void generateKeyPair(final String alias, boolean isUserSelectable,
1313+
byte[] attestationChallenge,
1314+
int idAttestationFlags) {
1315+
new GenerateKeyAndCertificateTask(
1316+
alias, isUserSelectable, attestationChallenge, idAttestationFlags,
1317+
getActivity()).execute();
13761318
}
13771319

1378-
class SignAndVerifyTask extends AsyncTask<String, Integer, String> {
1379-
@Override
1380-
protected String doInBackground(String... aliases) {
1381-
String alias = aliases[0];
1382-
try {
1383-
final String algorithmIdentifier = "SHA256withRSA";
1384-
PrivateKey privateKey = KeyChain.getPrivateKey(getContext(), alias);
1385-
1386-
byte[] data = new String("hello").getBytes();
1387-
Signature signer = Signature.getInstance(algorithmIdentifier);
1388-
signer.initSign(privateKey);
1389-
signer.update(data);
1390-
byte[] signature = signer.sign();
1391-
1392-
X509Certificate cert = KeyChain.getCertificateChain(getContext(), alias)[0];
1393-
PublicKey publicKey = cert.getPublicKey();
1394-
Signature verifier = Signature.getInstance(algorithmIdentifier);
1395-
verifier.initVerify(publicKey);
1396-
verifier.update(data);
1397-
if (verifier.verify(signature)) {
1398-
return cert.getSubjectX500Principal().getName();
1399-
}
1400-
} catch (KeyChainException e) {
1401-
Log.e(TAG, "Error getting key", e);
1402-
} catch (InterruptedException e) {
1403-
Log.e(TAG, "Interrupted while getting the key", e);
1404-
} catch (NoSuchAlgorithmException e) {
1405-
e.printStackTrace();
1406-
} catch (SignatureException e) {
1407-
Log.e(TAG, "Failed signing with key", e);
1408-
} catch (InvalidKeyException e) {
1409-
Log.e(TAG, "Provided alias resolves to an invalid key", e);
1410-
}
1411-
return null;
1412-
}
1413-
1414-
@Override
1415-
protected void onPostExecute(String result) {
1416-
if (result != null) {
1417-
showToast(R.string.key_usage_successful, result);
1418-
} else {
1419-
showToast(R.string.key_usage_failed);
1420-
}
1421-
}
1422-
};
1423-
1424-
14251320
/**
14261321
* Dispatches an intent to capture image or video.
14271322
*/
@@ -2443,7 +2338,7 @@ private void showPromptForGeneratedKeyAlias(String alias) {
24432338
}
24442339

24452340
View aliasNamingView = getActivity().getLayoutInflater().inflate(
2446-
R.layout.certificate_alias_prompt, null);
2341+
R.layout.key_generation_prompt, null);
24472342
final EditText input = (EditText) aliasNamingView.findViewById(R.id.alias_input);
24482343
if (!TextUtils.isEmpty(alias)) {
24492344
input.setText(alias);
@@ -2454,6 +2349,19 @@ private void showPromptForGeneratedKeyAlias(String alias) {
24542349
R.id.alias_user_selectable);
24552350
userSelectableCheckbox.setChecked(!BuildCompat.isAtLeastP());
24562351

2352+
// Attestation check-boxes
2353+
final CheckBox includeAttestationChallengeCheckbox = aliasNamingView.findViewById(
2354+
R.id.include_key_attestation_challenge);
2355+
final CheckBox deviceBrandAttestationCheckbox = aliasNamingView.findViewById(
2356+
R.id.include_device_brand_attestation);
2357+
final CheckBox deviceSerialAttestationCheckbox = aliasNamingView.findViewById(
2358+
R.id.include_device_serial_in_attestation);
2359+
final CheckBox deviceImeiAttestationCheckbox = aliasNamingView.findViewById(
2360+
R.id.include_device_imei_in_attestation);
2361+
final CheckBox deviceMeidAttestationCheckbox = aliasNamingView.findViewById(
2362+
R.id.include_device_meid_in_attestation);
2363+
2364+
24572365
new AlertDialog.Builder(getActivity())
24582366
.setTitle(getString(R.string.certificate_alias_prompt_title))
24592367
.setView(aliasNamingView)
@@ -2462,7 +2370,28 @@ private void showPromptForGeneratedKeyAlias(String alias) {
24622370
public void onClick(DialogInterface dialog, int which) {
24632371
String alias = input.getText().toString();
24642372
boolean isUserSelectable = userSelectableCheckbox.isChecked();
2465-
generateKeyPair(alias, isUserSelectable);
2373+
2374+
byte[] attestationChallenge = null;
2375+
if (includeAttestationChallengeCheckbox.isChecked()) {
2376+
attestationChallenge = new byte[] {0x61, 0x62, 0x63};
2377+
}
2378+
2379+
int idAttestationFlags = 0;
2380+
if (deviceBrandAttestationCheckbox.isChecked()) {
2381+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_BASE_INFO;
2382+
}
2383+
if (deviceSerialAttestationCheckbox.isChecked()) {
2384+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_SERIAL;
2385+
}
2386+
if (deviceImeiAttestationCheckbox.isChecked()) {
2387+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_IMEI;
2388+
}
2389+
if (deviceMeidAttestationCheckbox.isChecked()) {
2390+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_MEID;
2391+
}
2392+
2393+
generateKeyPair(alias, isUserSelectable, attestationChallenge,
2394+
idAttestationFlags);
24662395
}
24672396
})
24682397
.setNegativeButton(android.R.string.cancel, null)

0 commit comments

Comments
 (0)