Skip to content

Commit 26ac167

Browse files
TreeHugger RobotAndroid (Google) Code Review
authored andcommitted
Merge "TestDPC: Support Device ID attestation" into ub-testdpc-pic
2 parents f11afc5 + 2039ded commit 26ac167

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.
@@ -1055,7 +1048,10 @@ public void alias(String alias) {
10551048
return;
10561049
}
10571050

1058-
new SignAndVerifyTask().execute(alias);
1051+
new SignAndVerifyTask(
1052+
getContext(), (int msgId, Object... args) -> {
1053+
showToast(msgId, args);
1054+
}).execute(alias);
10591055
}
10601056
}, null, null, null, null);
10611057
}
@@ -1293,115 +1289,14 @@ private boolean installKeyPair(final PrivateKey key, final Certificate cert, fin
12931289
}
12941290
}
12951291

1296-
class GenerateKeyAndCertificateTask extends AsyncTask<Void, Integer, Boolean> {
1297-
final String mAlias;
1298-
final boolean mIsUserSelectable;
1299-
1300-
GenerateKeyAndCertificateTask(String mAlias, boolean mIsUserSelectable) {
1301-
this.mAlias = mAlias;
1302-
this.mIsUserSelectable = mIsUserSelectable;
1303-
}
1304-
1305-
@TargetApi(28)
1306-
@Override
1307-
protected Boolean doInBackground(Void... voids) {
1308-
try {
1309-
KeyGenParameterSpec keySpec = new KeyGenParameterSpec.Builder(mAlias,
1310-
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
1311-
.setKeySize(2048)
1312-
.setDigests(KeyProperties.DIGEST_SHA256)
1313-
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS,
1314-
KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
1315-
.build();
1316-
AttestedKeyPair keyPair = mDevicePolicyManager.generateKeyPair(
1317-
mAdminComponentName, "RSA", keySpec, 0);
1318-
1319-
if (keyPair == null) {
1320-
return false;
1321-
}
1322-
1323-
X500Principal subject = new X500Principal("CN=TestDPC, O=Android, C=US");
1324-
// Self-signed certificate: Same subject and issuer.
1325-
X509Certificate selfSigned = CertificateUtils.createCertificate(
1326-
keyPair.getKeyPair(), subject, subject);
1327-
1328-
List<Certificate> certs = new ArrayList<Certificate>();
1329-
certs.add(selfSigned);
1330-
1331-
return mDevicePolicyManager.setKeyPairCertificate(
1332-
mAdminComponentName, mAlias, certs, mIsUserSelectable);
1333-
} catch (CertificateException e) {
1334-
Log.e(TAG, "Failed to create certificate", e);
1335-
} catch (OperatorCreationException e) {
1336-
Log.e(TAG, "Failed to create certificate", e);
1337-
} catch (IOException e) {
1338-
Log.e(TAG, "Failed to encode certificate", e);
1339-
}
1340-
1341-
return false;
1342-
}
1343-
1344-
@Override
1345-
protected void onPostExecute(Boolean result) {
1346-
if (result.booleanValue()) {
1347-
showToast(R.string.key_generation_successful, mAlias);
1348-
} else {
1349-
showToast(R.string.key_generation_failed, mAlias);
1350-
}
1351-
}
1352-
}
1353-
1354-
private void generateKeyPair(final String alias, boolean isUserSelectable) {
1355-
new GenerateKeyAndCertificateTask(alias, isUserSelectable).execute();
1292+
private void generateKeyPair(final String alias, boolean isUserSelectable,
1293+
byte[] attestationChallenge,
1294+
int idAttestationFlags) {
1295+
new GenerateKeyAndCertificateTask(
1296+
alias, isUserSelectable, attestationChallenge, idAttestationFlags,
1297+
getActivity()).execute();
13561298
}
13571299

1358-
class SignAndVerifyTask extends AsyncTask<String, Integer, String> {
1359-
@Override
1360-
protected String doInBackground(String... aliases) {
1361-
String alias = aliases[0];
1362-
try {
1363-
final String algorithmIdentifier = "SHA256withRSA";
1364-
PrivateKey privateKey = KeyChain.getPrivateKey(getContext(), alias);
1365-
1366-
byte[] data = new String("hello").getBytes();
1367-
Signature signer = Signature.getInstance(algorithmIdentifier);
1368-
signer.initSign(privateKey);
1369-
signer.update(data);
1370-
byte[] signature = signer.sign();
1371-
1372-
X509Certificate cert = KeyChain.getCertificateChain(getContext(), alias)[0];
1373-
PublicKey publicKey = cert.getPublicKey();
1374-
Signature verifier = Signature.getInstance(algorithmIdentifier);
1375-
verifier.initVerify(publicKey);
1376-
verifier.update(data);
1377-
if (verifier.verify(signature)) {
1378-
return cert.getSubjectX500Principal().getName();
1379-
}
1380-
} catch (KeyChainException e) {
1381-
Log.e(TAG, "Error getting key", e);
1382-
} catch (InterruptedException e) {
1383-
Log.e(TAG, "Interrupted while getting the key", e);
1384-
} catch (NoSuchAlgorithmException e) {
1385-
e.printStackTrace();
1386-
} catch (SignatureException e) {
1387-
Log.e(TAG, "Failed signing with key", e);
1388-
} catch (InvalidKeyException e) {
1389-
Log.e(TAG, "Provided alias resolves to an invalid key", e);
1390-
}
1391-
return null;
1392-
}
1393-
1394-
@Override
1395-
protected void onPostExecute(String result) {
1396-
if (result != null) {
1397-
showToast(R.string.key_usage_successful, result);
1398-
} else {
1399-
showToast(R.string.key_usage_failed);
1400-
}
1401-
}
1402-
};
1403-
1404-
14051300
/**
14061301
* Dispatches an intent to capture image or video.
14071302
*/
@@ -2413,7 +2308,7 @@ private void showPromptForGeneratedKeyAlias(String alias) {
24132308
}
24142309

24152310
View aliasNamingView = getActivity().getLayoutInflater().inflate(
2416-
R.layout.certificate_alias_prompt, null);
2311+
R.layout.key_generation_prompt, null);
24172312
final EditText input = (EditText) aliasNamingView.findViewById(R.id.alias_input);
24182313
if (!TextUtils.isEmpty(alias)) {
24192314
input.setText(alias);
@@ -2424,6 +2319,19 @@ private void showPromptForGeneratedKeyAlias(String alias) {
24242319
R.id.alias_user_selectable);
24252320
userSelectableCheckbox.setChecked(!BuildCompat.isAtLeastP());
24262321

2322+
// Attestation check-boxes
2323+
final CheckBox includeAttestationChallengeCheckbox = aliasNamingView.findViewById(
2324+
R.id.include_key_attestation_challenge);
2325+
final CheckBox deviceBrandAttestationCheckbox = aliasNamingView.findViewById(
2326+
R.id.include_device_brand_attestation);
2327+
final CheckBox deviceSerialAttestationCheckbox = aliasNamingView.findViewById(
2328+
R.id.include_device_serial_in_attestation);
2329+
final CheckBox deviceImeiAttestationCheckbox = aliasNamingView.findViewById(
2330+
R.id.include_device_imei_in_attestation);
2331+
final CheckBox deviceMeidAttestationCheckbox = aliasNamingView.findViewById(
2332+
R.id.include_device_meid_in_attestation);
2333+
2334+
24272335
new AlertDialog.Builder(getActivity())
24282336
.setTitle(getString(R.string.certificate_alias_prompt_title))
24292337
.setView(aliasNamingView)
@@ -2432,7 +2340,28 @@ private void showPromptForGeneratedKeyAlias(String alias) {
24322340
public void onClick(DialogInterface dialog, int which) {
24332341
String alias = input.getText().toString();
24342342
boolean isUserSelectable = userSelectableCheckbox.isChecked();
2435-
generateKeyPair(alias, isUserSelectable);
2343+
2344+
byte[] attestationChallenge = null;
2345+
if (includeAttestationChallengeCheckbox.isChecked()) {
2346+
attestationChallenge = new byte[] {0x61, 0x62, 0x63};
2347+
}
2348+
2349+
int idAttestationFlags = 0;
2350+
if (deviceBrandAttestationCheckbox.isChecked()) {
2351+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_BASE_INFO;
2352+
}
2353+
if (deviceSerialAttestationCheckbox.isChecked()) {
2354+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_SERIAL;
2355+
}
2356+
if (deviceImeiAttestationCheckbox.isChecked()) {
2357+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_IMEI;
2358+
}
2359+
if (deviceMeidAttestationCheckbox.isChecked()) {
2360+
idAttestationFlags |= DevicePolicyManager.ID_TYPE_MEID;
2361+
}
2362+
2363+
generateKeyPair(alias, isUserSelectable, attestationChallenge,
2364+
idAttestationFlags);
24362365
}
24372366
})
24382367
.setNegativeButton(android.R.string.cancel, null)

0 commit comments

Comments
 (0)