16
16
17
17
package com .afwsamples .testdpc .policy ;
18
18
19
- import static android .os .UserManager .DISALLOW_INSTALL_UNKNOWN_SOURCES ;
20
-
21
- import static com .afwsamples .testdpc .common .preference .DpcPreferenceHelper .NO_CUSTOM_CONSTRIANT ;
22
-
23
19
import android .accessibilityservice .AccessibilityServiceInfo ;
24
20
import android .accounts .Account ;
25
21
import android .accounts .AccountManager ;
53
49
import android .os .UserManager ;
54
50
import android .provider .MediaStore ;
55
51
import android .provider .Settings ;
52
+ import android .security .AttestedKeyPair ;
56
53
import android .security .KeyChain ;
57
54
import android .security .KeyChainAliasCallback ;
55
+ import android .security .KeyChainException ;
56
+ import android .security .keystore .KeyGenParameterSpec ;
57
+ import android .security .keystore .KeyProperties ;
58
58
import android .service .notification .NotificationListenerService ;
59
+ import android .support .annotation .RequiresApi ;
59
60
import android .support .annotation .StringRes ;
60
61
import android .support .v14 .preference .SwitchPreference ;
61
62
import android .support .v4 .content .FileProvider ;
111
112
import com .afwsamples .testdpc .policy .networking .NetworkUsageStatsFragment ;
112
113
import com .afwsamples .testdpc .policy .resetpassword .ResetPasswordWithTokenFragment ;
113
114
import com .afwsamples .testdpc .policy .systemupdatepolicy .SystemUpdatePolicyFragment ;
115
+ import com .afwsamples .testdpc .policy .utils .CertificateUtils ;
114
116
import com .afwsamples .testdpc .policy .wifimanagement .WifiConfigCreationDialog ;
115
117
import com .afwsamples .testdpc .policy .wifimanagement .WifiEapTlsCreateDialogFragment ;
116
118
import com .afwsamples .testdpc .policy .wifimanagement .WifiModificationFragment ;
124
126
import com .afwsamples .testdpc .util .MainThreadExecutor ;
125
127
import com .afwsamples .testdpc .transferownership .PickTransferComponentFragment ;
126
128
129
+ import org .bouncycastle .operator .OperatorCreationException ;
130
+
127
131
import java .io .ByteArrayInputStream ;
128
132
import java .io .File ;
129
133
import java .io .FileNotFoundException ;
130
134
import java .io .IOException ;
131
135
import java .io .InputStream ;
136
+ import java .security .InvalidKeyException ;
132
137
import java .security .KeyStoreException ;
133
138
import java .security .NoSuchAlgorithmException ;
134
139
import java .security .PrivateKey ;
140
+ import java .security .PublicKey ;
141
+ import java .security .Signature ;
142
+ import java .security .SignatureException ;
135
143
import java .security .UnrecoverableKeyException ;
136
144
import java .security .cert .Certificate ;
137
145
import java .security .cert .CertificateException ;
151
159
import java .util .TimeZone ;
152
160
import java .util .stream .Collectors ;
153
161
162
+ import javax .security .auth .x500 .X500Principal ;
163
+
164
+ import static android .os .UserManager .DISALLOW_INSTALL_UNKNOWN_SOURCES ;
165
+ import static com .afwsamples .testdpc .common .preference .DpcPreferenceHelper .NO_CUSTOM_CONSTRIANT ;
166
+
154
167
/**
155
168
* Provides several device management functions.
156
169
*
@@ -265,6 +278,7 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag
265
278
= "enable_system_apps_by_package_name" ;
266
279
private static final String ENABLE_SYSTEM_APPS_KEY = "enable_system_apps" ;
267
280
private static final String INSTALL_EXISTING_PACKAGE_KEY = "install_existing_packages" ;
281
+ private static final String GENERATE_KEY_CERTIFICATE_KEY = "generate_key_and_certificate" ;
268
282
private static final String GET_CA_CERTIFICATES_KEY = "get_ca_certificates" ;
269
283
private static final String GET_DISABLE_ACCOUNT_MANAGEMENT_KEY
270
284
= "get_disable_account_management" ;
@@ -328,6 +342,7 @@ public class PolicyManagementFragment extends BaseSearchablePolicyPreferenceFrag
328
342
private static final String SUSPEND_APPS_KEY = "suspend_apps" ;
329
343
private static final String SYSTEM_UPDATE_POLICY_KEY = "system_update_policy" ;
330
344
private static final String SYSTEM_UPDATE_PENDING_KEY = "system_update_pending" ;
345
+ private static final String TEST_KEY_USABILITY_KEY = "test_key_usability" ;
331
346
332
347
private static final String UNHIDE_APPS_KEY = "unhide_apps" ;
333
348
private static final String UNSUSPEND_APPS_KEY = "unsuspend_apps" ;
@@ -552,7 +567,9 @@ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
552
567
findPreference (GENERIC_DELEGATION_KEY ).setOnPreferenceClickListener (this );
553
568
findPreference (APP_RESTRICTIONS_MANAGING_PACKAGE_KEY ).setOnPreferenceClickListener (this );
554
569
findPreference (INSTALL_KEY_CERTIFICATE_KEY ).setOnPreferenceClickListener (this );
570
+ findPreference (GENERATE_KEY_CERTIFICATE_KEY ).setOnPreferenceClickListener (this );
555
571
findPreference (REMOVE_KEY_CERTIFICATE_KEY ).setOnPreferenceClickListener (this );
572
+ findPreference (TEST_KEY_USABILITY_KEY ).setOnPreferenceClickListener (this );
556
573
findPreference (INSTALL_CA_CERTIFICATE_KEY ).setOnPreferenceClickListener (this );
557
574
findPreference (GET_CA_CERTIFICATES_KEY ).setOnPreferenceClickListener (this );
558
575
findPreference (REMOVE_ALL_CERTIFICATES_KEY ).setOnPreferenceClickListener (this );
@@ -866,6 +883,12 @@ public void onPositiveButtonClicked(String[] lockTaskArray) {
866
883
case REMOVE_KEY_CERTIFICATE_KEY :
867
884
choosePrivateKeyForRemoval ();
868
885
return true ;
886
+ case GENERATE_KEY_CERTIFICATE_KEY :
887
+ showPromptForGeneratedKeyAlias ("generated-rsa-testdpc-1" );
888
+ return true ;
889
+ case TEST_KEY_USABILITY_KEY :
890
+ testKeyCanBeUsedForSigning ();
891
+ return true ;
869
892
case INSTALL_CA_CERTIFICATE_KEY :
870
893
Util .showFileViewerForImportingCertificate (this ,
871
894
INSTALL_CA_CERTIFICATE_REQUEST_CODE );
@@ -1006,6 +1029,22 @@ public void onPositiveButtonClicked(String[] lockTaskArray) {
1006
1029
return false ;
1007
1030
}
1008
1031
1032
+ @ RequiresApi (api = Build .VERSION_CODES .M )
1033
+ private void testKeyCanBeUsedForSigning () {
1034
+ KeyChain .choosePrivateKeyAlias (getActivity (), new KeyChainAliasCallback () {
1035
+ @ Override
1036
+ public void alias (String alias ) {
1037
+ if (alias == null ) {
1038
+ // No value was chosen.
1039
+ showToast ("No key chosen." );
1040
+ return ;
1041
+ }
1042
+
1043
+ new SignAndVerifyTask ().execute (alias );
1044
+ }
1045
+ }, null , null , null , null );
1046
+ }
1047
+
1009
1048
@ TargetApi (Build .VERSION_CODES .O )
1010
1049
private void showPendingSystemUpdate () {
1011
1050
final SystemUpdateInfo updateInfo =
@@ -1228,6 +1267,131 @@ private void setStatusBarDisabled(boolean disable) {
1228
1267
}
1229
1268
}
1230
1269
1270
+ @ TargetApi (28 )
1271
+ private boolean installKeyPair (final PrivateKey key , final Certificate cert , final String alias ,
1272
+ boolean isUserSelectable ) {
1273
+ if (BuildCompat .isAtLeastP ()) {
1274
+ return mDevicePolicyManager .installKeyPair (
1275
+ mAdminComponentName , key , new Certificate []{cert }, alias , false ,
1276
+ isUserSelectable );
1277
+ } else {
1278
+ if (!isUserSelectable ) {
1279
+ throw new IllegalArgumentException (
1280
+ "Cannot set key as non-user-selectable prior to P" );
1281
+ }
1282
+ return mDevicePolicyManager .installKeyPair (mAdminComponentName , key , cert , alias );
1283
+ }
1284
+ }
1285
+
1286
+ class GenerateKeyAndCertificateTask extends AsyncTask <Void , Integer , Boolean > {
1287
+ final String mAlias ;
1288
+ final boolean mIsUserSelectable ;
1289
+
1290
+ GenerateKeyAndCertificateTask (String mAlias , boolean mIsUserSelectable ) {
1291
+ this .mAlias = mAlias ;
1292
+ this .mIsUserSelectable = mIsUserSelectable ;
1293
+ }
1294
+
1295
+ @ TargetApi (28 )
1296
+ @ Override
1297
+ protected Boolean doInBackground (Void ... voids ) {
1298
+ try {
1299
+ KeyGenParameterSpec keySpec = new KeyGenParameterSpec .Builder (mAlias ,
1300
+ KeyProperties .PURPOSE_SIGN | KeyProperties .PURPOSE_VERIFY )
1301
+ .setKeySize (2048 )
1302
+ .setDigests (KeyProperties .DIGEST_SHA256 )
1303
+ .setSignaturePaddings (KeyProperties .SIGNATURE_PADDING_RSA_PSS ,
1304
+ KeyProperties .SIGNATURE_PADDING_RSA_PKCS1 )
1305
+ .build ();
1306
+ AttestedKeyPair keyPair = mDevicePolicyManager .generateKeyPair (
1307
+ mAdminComponentName , "RSA" , keySpec , 0 );
1308
+
1309
+ if (keyPair == null ) {
1310
+ return false ;
1311
+ }
1312
+
1313
+ X500Principal subject = new X500Principal ("CN=TestDPC, O=Android, C=US" );
1314
+ // Self-signed certificate: Same subject and issuer.
1315
+ X509Certificate selfSigned = CertificateUtils .createCertificate (
1316
+ keyPair .getKeyPair (), subject , subject );
1317
+
1318
+ List <Certificate > certs = new ArrayList <Certificate >();
1319
+ certs .add (selfSigned );
1320
+
1321
+ return mDevicePolicyManager .setKeyPairCertificate (
1322
+ mAdminComponentName , mAlias , certs , mIsUserSelectable );
1323
+ } catch (CertificateException e ) {
1324
+ Log .e (TAG , "Failed to create certificate" , e );
1325
+ } catch (OperatorCreationException e ) {
1326
+ Log .e (TAG , "Failed to create certificate" , e );
1327
+ } catch (IOException e ) {
1328
+ Log .e (TAG , "Failed to encode certificate" , e );
1329
+ }
1330
+
1331
+ return false ;
1332
+ }
1333
+
1334
+ @ Override
1335
+ protected void onPostExecute (Boolean result ) {
1336
+ if (result .booleanValue ()) {
1337
+ showToast (R .string .key_generation_successful , mAlias );
1338
+ } else {
1339
+ showToast (R .string .key_generation_failed , mAlias );
1340
+ }
1341
+ }
1342
+ }
1343
+
1344
+ private void generateKeyPair (final String alias , boolean isUserSelectable ) {
1345
+ new GenerateKeyAndCertificateTask (alias , isUserSelectable ).execute ();
1346
+ }
1347
+
1348
+ class SignAndVerifyTask extends AsyncTask <String , Integer , String > {
1349
+ @ Override
1350
+ protected String doInBackground (String ... aliases ) {
1351
+ String alias = aliases [0 ];
1352
+ try {
1353
+ final String algorithmIdentifier = "SHA256withRSA" ;
1354
+ PrivateKey privateKey = KeyChain .getPrivateKey (getContext (), alias );
1355
+
1356
+ byte [] data = new String ("hello" ).getBytes ();
1357
+ Signature signer = Signature .getInstance (algorithmIdentifier );
1358
+ signer .initSign (privateKey );
1359
+ signer .update (data );
1360
+ byte [] signature = signer .sign ();
1361
+
1362
+ X509Certificate cert = KeyChain .getCertificateChain (getContext (), alias )[0 ];
1363
+ PublicKey publicKey = cert .getPublicKey ();
1364
+ Signature verifier = Signature .getInstance (algorithmIdentifier );
1365
+ verifier .initVerify (publicKey );
1366
+ verifier .update (data );
1367
+ if (verifier .verify (signature )) {
1368
+ return cert .getSubjectX500Principal ().getName ();
1369
+ }
1370
+ } catch (KeyChainException e ) {
1371
+ Log .e (TAG , "Error getting key" , e );
1372
+ } catch (InterruptedException e ) {
1373
+ Log .e (TAG , "Interrupted while getting the key" , e );
1374
+ } catch (NoSuchAlgorithmException e ) {
1375
+ e .printStackTrace ();
1376
+ } catch (SignatureException e ) {
1377
+ Log .e (TAG , "Failed signing with key" , e );
1378
+ } catch (InvalidKeyException e ) {
1379
+ Log .e (TAG , "Provided alias resolves to an invalid key" , e );
1380
+ }
1381
+ return null ;
1382
+ }
1383
+
1384
+ @ Override
1385
+ protected void onPostExecute (String result ) {
1386
+ if (result != null ) {
1387
+ showToast (R .string .key_usage_successful , result );
1388
+ } else {
1389
+ showToast (R .string .key_usage_failed );
1390
+ }
1391
+ }
1392
+ };
1393
+
1394
+
1231
1395
/**
1232
1396
* Dispatches an intent to capture image or video.
1233
1397
*/
@@ -2200,15 +2364,20 @@ private void showPromptForKeyCertificateAlias(final PrivateKey key,
2200
2364
input .selectAll ();
2201
2365
}
2202
2366
2367
+ final CheckBox userSelectableCheckbox = passwordInputView .findViewById (
2368
+ R .id .alias_user_selectable );
2369
+ userSelectableCheckbox .setEnabled (BuildCompat .isAtLeastP ());
2370
+ userSelectableCheckbox .setChecked (!BuildCompat .isAtLeastP ());
2371
+
2203
2372
new AlertDialog .Builder (getActivity ())
2204
2373
.setTitle (getString (R .string .certificate_alias_prompt_title ))
2205
2374
.setView (passwordInputView )
2206
2375
.setPositiveButton (android .R .string .ok , new DialogInterface .OnClickListener () {
2207
2376
@ Override
2208
2377
public void onClick (DialogInterface dialog , int which ) {
2209
2378
String alias = input .getText ().toString ();
2210
- if ( mDevicePolicyManager . installKeyPair ( mAdminComponentName , key ,
2211
- certificate , alias ) == true ) {
2379
+ boolean isUserSelectable = userSelectableCheckbox . isChecked ();
2380
+ if ( installKeyPair ( key , certificate , alias , isUserSelectable ) == true ) {
2212
2381
showToast (R .string .certificate_added , alias );
2213
2382
} else {
2214
2383
showToast (R .string .certificate_add_failed , alias );
@@ -2225,6 +2394,44 @@ public void onClick(DialogInterface dialog, int which) {
2225
2394
.show ();
2226
2395
}
2227
2396
2397
+ /**
2398
+ * Shows a prompt to ask for the certificate alias. A key will be generated for this alias.
2399
+ *
2400
+ * @param alias A name that represents the certificate in the profile.
2401
+ */
2402
+ private void showPromptForGeneratedKeyAlias (String alias ) {
2403
+ if (getActivity () == null || getActivity ().isFinishing ()) {
2404
+ return ;
2405
+ }
2406
+
2407
+ View aliasNamingView = getActivity ().getLayoutInflater ().inflate (
2408
+ R .layout .certificate_alias_prompt , null );
2409
+ final EditText input = (EditText ) aliasNamingView .findViewById (R .id .alias_input );
2410
+ if (!TextUtils .isEmpty (alias )) {
2411
+ input .setText (alias );
2412
+ input .selectAll ();
2413
+ }
2414
+
2415
+ final CheckBox userSelectableCheckbox = aliasNamingView .findViewById (
2416
+ R .id .alias_user_selectable );
2417
+ userSelectableCheckbox .setChecked (!BuildCompat .isAtLeastP ());
2418
+
2419
+ new AlertDialog .Builder (getActivity ())
2420
+ .setTitle (getString (R .string .certificate_alias_prompt_title ))
2421
+ .setView (aliasNamingView )
2422
+ .setPositiveButton (android .R .string .ok , new DialogInterface .OnClickListener () {
2423
+ @ Override
2424
+ public void onClick (DialogInterface dialog , int which ) {
2425
+ String alias = input .getText ().toString ();
2426
+ boolean isUserSelectable = userSelectableCheckbox .isChecked ();
2427
+ generateKeyPair (alias , isUserSelectable );
2428
+ }
2429
+ })
2430
+ .setNegativeButton (android .R .string .cancel , null )
2431
+ .show ();
2432
+ }
2433
+
2434
+
2228
2435
/**
2229
2436
* Selects a private/public key pair to uninstall, using the system dialog to choose
2230
2437
* an alias.
0 commit comments