1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.security; 18 19 import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE; 20 import static android.security.attestationverification.AttestationVerificationManager.PARAM_PUBLIC_KEY; 21 import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE; 22 import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS; 23 import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE; 24 import static android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY; 25 import static android.security.attestationverification.AttestationVerificationManager.localBindingTypeToString; 26 27 import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.VerifiedBootState.VERIFIED; 28 import static com.android.server.security.AndroidKeystoreAttestationVerificationAttributes.fromCertificate; 29 30 import static java.nio.charset.StandardCharsets.UTF_8; 31 32 import android.annotation.NonNull; 33 import android.content.Context; 34 import android.os.Build; 35 import android.os.Bundle; 36 import android.security.attestationverification.AttestationVerificationManager.LocalBindingType; 37 import android.util.Log; 38 import android.util.Slog; 39 40 import com.android.internal.R; 41 import com.android.internal.annotations.VisibleForTesting; 42 43 import org.json.JSONObject; 44 45 import java.io.ByteArrayInputStream; 46 import java.io.IOException; 47 import java.io.InputStream; 48 import java.net.URL; 49 import java.security.InvalidAlgorithmParameterException; 50 import java.security.cert.CertPath; 51 import java.security.cert.CertPathValidator; 52 import java.security.cert.CertPathValidatorException; 53 import java.security.cert.Certificate; 54 import java.security.cert.CertificateException; 55 import java.security.cert.CertificateFactory; 56 import java.security.cert.PKIXCertPathChecker; 57 import java.security.cert.PKIXParameters; 58 import java.security.cert.TrustAnchor; 59 import java.security.cert.X509Certificate; 60 import java.time.LocalDate; 61 import java.time.ZoneId; 62 import java.time.temporal.ChronoUnit; 63 import java.util.ArrayList; 64 import java.util.Arrays; 65 import java.util.Collection; 66 import java.util.Collections; 67 import java.util.HashSet; 68 import java.util.List; 69 import java.util.Objects; 70 import java.util.Set; 71 72 /** 73 * Verifies Android key attestation according to the 74 * {@link android.security.attestationverification.AttestationVerificationManager#PROFILE_PEER_DEVICE PROFILE_PEER_DEVICE} 75 * profile. 76 * 77 * <p> 78 * The profile is satisfied by checking all the following: 79 * <ul> 80 * <li> TrustAnchor match 81 * <li> Certificate validity 82 * <li> Android OS 10 or higher 83 * <li> Hardware backed key store 84 * <li> Verified boot locked 85 * <li> Remote Patch level must be within 1 year of local patch if local patch is less than 1 year 86 * old. 87 * </ul> 88 * 89 * <p> 90 * Trust anchors are vendor-defined by populating 91 * {@link R.array#vendor_required_attestation_certificates} string array (defenined in 92 * {@code frameworks/base/core/res/res/values/vendor_required_attestation_certificates.xml}). 93 */ 94 class AttestationVerificationPeerDeviceVerifier { 95 private static final String TAG = "AVF"; 96 private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE); 97 private static final int MAX_PATCH_AGE_MONTHS = 12; 98 99 /** 100 * Optional requirements bundle parameter key for {@code TYPE_PUBLIC_KEY} and 101 * {@code TYPE_CHALLENGE}. 102 * 103 * <p> 104 * This is NOT a part of the AVF API surface (neither public SDK nor internal to the 105 * system_server) and should really only be used by the CompanionDeviceManagerService (which 106 * duplicates the value rather than referencing it directly here). 107 */ 108 private static final String PARAM_OWNED_BY_SYSTEM = "android.key_owned_by_system"; 109 110 private static final String ANDROID_SYSTEM_PACKAGE_NAME = "AndroidSystem"; 111 private static final Set<String> ANDROID_SYSTEM_PACKAGE_NAME_SET = 112 Collections.singleton(ANDROID_SYSTEM_PACKAGE_NAME); 113 114 private final Context mContext; 115 private final Set<TrustAnchor> mTrustAnchors; 116 private final boolean mRevocationEnabled; 117 private final LocalDate mTestSystemDate; 118 private final LocalDate mTestLocalPatchDate; 119 private final CertificateFactory mCertificateFactory; 120 private final CertPathValidator mCertPathValidator; 121 AttestationVerificationPeerDeviceVerifier(@onNull Context context)122 AttestationVerificationPeerDeviceVerifier(@NonNull Context context) throws Exception { 123 mContext = Objects.requireNonNull(context); 124 mCertificateFactory = CertificateFactory.getInstance("X.509"); 125 mCertPathValidator = CertPathValidator.getInstance("PKIX"); 126 mTrustAnchors = getTrustAnchors(); 127 mRevocationEnabled = true; 128 mTestSystemDate = null; 129 mTestLocalPatchDate = null; 130 } 131 132 // Use ONLY for hermetic unit testing. 133 @VisibleForTesting AttestationVerificationPeerDeviceVerifier(@onNull Context context, Set<TrustAnchor> trustAnchors, boolean revocationEnabled, LocalDate systemDate, LocalDate localPatchDate)134 AttestationVerificationPeerDeviceVerifier(@NonNull Context context, 135 Set<TrustAnchor> trustAnchors, boolean revocationEnabled, 136 LocalDate systemDate, LocalDate localPatchDate) throws Exception { 137 mContext = Objects.requireNonNull(context); 138 mCertificateFactory = CertificateFactory.getInstance("X.509"); 139 mCertPathValidator = CertPathValidator.getInstance("PKIX"); 140 mTrustAnchors = trustAnchors; 141 mRevocationEnabled = revocationEnabled; 142 mTestSystemDate = systemDate; 143 mTestLocalPatchDate = localPatchDate; 144 } 145 146 /** 147 * Verifies attestation for public key or challenge local binding. 148 * <p> 149 * The attestations must be suitable for {@link java.security.cert.CertificateFactory} 150 * The certificates in the attestation provided must be DER-encoded and may be supplied in 151 * binary or printable (Base64) encoding. If the certificate is provided in Base64 encoding, 152 * it must be bounded at the beginning by {@code -----BEGIN CERTIFICATE-----}, and must be 153 * bounded at the end by {@code -----END CERTIFICATE-----}. 154 * 155 * @param localBindingType Only {@code TYPE_PUBLIC_KEY} and {@code TYPE_CHALLENGE} supported. 156 * @param requirements Only {@code PARAM_PUBLIC_KEY} and {@code PARAM_CHALLENGE} supported. 157 * @param attestation Certificates should be DER encoded with leaf certificate appended first. 158 */ verifyAttestation( @ocalBindingType int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation)159 int verifyAttestation( 160 @LocalBindingType int localBindingType, 161 @NonNull Bundle requirements, 162 @NonNull byte[] attestation) { 163 if (mCertificateFactory == null) { 164 debugVerboseLog("Unable to access CertificateFactory"); 165 return RESULT_FAILURE; 166 } 167 168 if (mCertPathValidator == null) { 169 debugVerboseLog("Unable to access CertPathValidator"); 170 return RESULT_FAILURE; 171 } 172 173 // Check if the provided local binding type is supported and if the provided requirements 174 // "match" the binding type. 175 if (!validateAttestationParameters(localBindingType, requirements)) { 176 return RESULT_FAILURE; 177 } 178 179 try { 180 // First: parse and validate the certificate chain. 181 final List<X509Certificate> certificateChain = getCertificates(attestation); 182 // (returns void, but throws CertificateException and other similar Exceptions) 183 validateCertificateChain(certificateChain); 184 185 final var leafCertificate = certificateChain.get(0); 186 final var attestationExtension = fromCertificate(leafCertificate); 187 188 // Second: verify if the attestation satisfies the "peer device" profile. 189 if (!checkAttestationForPeerDeviceProfile(attestationExtension)) { 190 return RESULT_FAILURE; 191 } 192 193 // Third: check if the attestation satisfies local binding requirements. 194 if (!checkLocalBindingRequirements( 195 leafCertificate, attestationExtension, localBindingType, requirements)) { 196 return RESULT_FAILURE; 197 } 198 199 return RESULT_SUCCESS; 200 } catch (CertificateException | CertPathValidatorException 201 | InvalidAlgorithmParameterException | IOException e) { 202 // Catch all non-RuntimeExpceptions (all of these are thrown by either getCertificates() 203 // or validateCertificateChain() or 204 // AndroidKeystoreAttestationVerificationAttributes.fromCertificate()) 205 debugVerboseLog("Unable to parse/validate Android Attestation certificate(s)", e); 206 return RESULT_FAILURE; 207 } catch (RuntimeException e) { 208 // Catch everyting else (RuntimeExpcetions), since we don't want to throw any exceptions 209 // out of this class/method. 210 debugVerboseLog("Unexpected error", e); 211 return RESULT_FAILURE; 212 } 213 } 214 215 @NonNull getCertificates(byte[] attestation)216 private List<X509Certificate> getCertificates(byte[] attestation) 217 throws CertificateException { 218 List<X509Certificate> certificates = new ArrayList<>(); 219 ByteArrayInputStream bis = new ByteArrayInputStream(attestation); 220 while (bis.available() > 0) { 221 certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis)); 222 } 223 224 return certificates; 225 } 226 227 /** 228 * Check if the {@code localBindingType} is supported and if the {@code requirements} contains 229 * the required parameter for the given {@code @LocalBindingType}. 230 */ validateAttestationParameters( @ocalBindingType int localBindingType, @NonNull Bundle requirements)231 private boolean validateAttestationParameters( 232 @LocalBindingType int localBindingType, @NonNull Bundle requirements) { 233 if (localBindingType != TYPE_PUBLIC_KEY && localBindingType != TYPE_CHALLENGE) { 234 debugVerboseLog("Binding type is not supported: " + localBindingType); 235 return false; 236 } 237 238 if (requirements.size() < 1) { 239 debugVerboseLog("At least 1 requirement is required."); 240 return false; 241 } 242 243 if (localBindingType == TYPE_PUBLIC_KEY && !requirements.containsKey(PARAM_PUBLIC_KEY)) { 244 debugVerboseLog("Requirements does not contain key: " + PARAM_PUBLIC_KEY); 245 return false; 246 } 247 248 if (localBindingType == TYPE_CHALLENGE && !requirements.containsKey(PARAM_CHALLENGE)) { 249 debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE); 250 return false; 251 } 252 253 return true; 254 } 255 validateCertificateChain(List<X509Certificate> certificates)256 private void validateCertificateChain(List<X509Certificate> certificates) 257 throws CertificateException, CertPathValidatorException, 258 InvalidAlgorithmParameterException { 259 if (certificates.size() < 2) { 260 debugVerboseLog("Certificate chain less than 2 in size."); 261 throw new CertificateException("Certificate chain less than 2 in size."); 262 } 263 264 CertPath certificatePath = mCertificateFactory.generateCertPath(certificates); 265 PKIXParameters validationParams = new PKIXParameters(mTrustAnchors); 266 if (mRevocationEnabled) { 267 // Checks Revocation Status List based on 268 // https://developer.android.com/training/articles/security-key-attestation#certificate_status 269 PKIXCertPathChecker checker = new AndroidRevocationStatusListChecker(); 270 validationParams.addCertPathChecker(checker); 271 } 272 // Do not use built-in revocation status checker. 273 validationParams.setRevocationEnabled(false); 274 mCertPathValidator.validate(certificatePath, validationParams); 275 } 276 getTrustAnchors()277 private Set<TrustAnchor> getTrustAnchors() throws CertPathValidatorException { 278 Set<TrustAnchor> modifiableSet = new HashSet<>(); 279 try { 280 for (String certString: getTrustAnchorResources()) { 281 modifiableSet.add( 282 new TrustAnchor((X509Certificate) mCertificateFactory.generateCertificate( 283 new ByteArrayInputStream(getCertificateBytes(certString))), null)); 284 } 285 } catch (CertificateException e) { 286 e.printStackTrace(); 287 throw new CertPathValidatorException("Invalid trust anchor certificate.", e); 288 } 289 return Collections.unmodifiableSet(modifiableSet); 290 } 291 getCertificateBytes(String certString)292 private byte[] getCertificateBytes(String certString) { 293 String formattedCertString = certString.replaceAll("\\s+", "\n"); 294 formattedCertString = formattedCertString.replaceAll( 295 "-BEGIN\\nCERTIFICATE-", "-BEGIN CERTIFICATE-"); 296 formattedCertString = formattedCertString.replaceAll( 297 "-END\\nCERTIFICATE-", "-END CERTIFICATE-"); 298 return formattedCertString.getBytes(UTF_8); 299 } 300 getTrustAnchorResources()301 private String[] getTrustAnchorResources() { 302 return mContext.getResources().getStringArray( 303 R.array.vendor_required_attestation_certificates); 304 } 305 checkLocalBindingRequirements( @onNull X509Certificate leafCertificate, @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, @LocalBindingType int localBindingType, @NonNull Bundle requirements)306 private boolean checkLocalBindingRequirements( 307 @NonNull X509Certificate leafCertificate, 308 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, 309 @LocalBindingType int localBindingType, 310 @NonNull Bundle requirements) { 311 // First: check non-optional (for the given local binding type) requirements. 312 switch (localBindingType) { 313 case TYPE_PUBLIC_KEY: 314 // Verify leaf public key matches provided public key. 315 final boolean publicKeyMatches = checkPublicKey( 316 leafCertificate, requirements.getByteArray(PARAM_PUBLIC_KEY)); 317 if (!publicKeyMatches) { 318 debugVerboseLog( 319 "Provided public key does not match leaf certificate public key."); 320 return false; 321 } 322 break; 323 324 case TYPE_CHALLENGE: 325 // Verify challenge matches provided challenge. 326 final boolean attestationChallengeMatches = checkAttestationChallenge( 327 attestationAttributes, requirements.getByteArray(PARAM_CHALLENGE)); 328 if (!attestationChallengeMatches) { 329 debugVerboseLog( 330 "Provided challenge does not match leaf certificate challenge."); 331 return false; 332 } 333 break; 334 335 default: 336 throw new IllegalArgumentException("Unsupported local binding type " 337 + localBindingTypeToString(localBindingType)); 338 } 339 340 // Second: check specified optional requirements. 341 if (requirements.containsKey(PARAM_OWNED_BY_SYSTEM)) { 342 if (requirements.getBoolean(PARAM_OWNED_BY_SYSTEM)) { 343 // Verify key is owned by the system. 344 final boolean ownedBySystem = checkOwnedBySystem( 345 leafCertificate, attestationAttributes); 346 if (!ownedBySystem) { 347 debugVerboseLog("Certificate public key is not owned by the AndroidSystem."); 348 return false; 349 } 350 } else { 351 throw new IllegalArgumentException("The value of the requirement key " 352 + PARAM_OWNED_BY_SYSTEM 353 + " cannot be false. You can remove the key if you don't want to verify " 354 + "it."); 355 } 356 } 357 358 return true; 359 } 360 checkAttestationForPeerDeviceProfile( @onNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes)361 private boolean checkAttestationForPeerDeviceProfile( 362 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes) { 363 // Checks for support of Keymaster 4. 364 if (attestationAttributes.getAttestationVersion() < 3) { 365 debugVerboseLog("Attestation version is not at least 3 (Keymaster 4)."); 366 return false; 367 } 368 369 // Checks for support of Keymaster 4. 370 if (attestationAttributes.getKeymasterVersion() < 4) { 371 debugVerboseLog("Keymaster version is not at least 4."); 372 return false; 373 } 374 375 // First two characters are Android OS version. 376 if (attestationAttributes.getKeyOsVersion() < 100000) { 377 debugVerboseLog("Android OS version is not 10+."); 378 return false; 379 } 380 381 if (!attestationAttributes.isAttestationHardwareBacked()) { 382 debugVerboseLog("Key is not HW backed."); 383 return false; 384 } 385 386 if (!attestationAttributes.isKeymasterHardwareBacked()) { 387 debugVerboseLog("Keymaster is not HW backed."); 388 return false; 389 } 390 391 if (attestationAttributes.getVerifiedBootState() != VERIFIED) { 392 debugVerboseLog("Boot state not Verified."); 393 return false; 394 } 395 396 try { 397 if (!attestationAttributes.isVerifiedBootLocked()) { 398 debugVerboseLog("Verified boot state is not locked."); 399 return false; 400 } 401 } catch (IllegalStateException e) { 402 debugVerboseLog("VerifiedBootLocked is not set.", e); 403 return false; 404 } 405 406 // Patch level integer YYYYMM is expected to be within 1 year of today. 407 if (!isValidPatchLevel(attestationAttributes.getKeyOsPatchLevel())) { 408 debugVerboseLog("OS patch level is not within valid range."); 409 return false; 410 } 411 412 // Patch level integer YYYYMMDD is expected to be within 1 year of today. 413 if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) { 414 debugVerboseLog("Boot patch level is not within valid range."); 415 return false; 416 } 417 418 if (!isValidPatchLevel(attestationAttributes.getKeyVendorPatchLevel())) { 419 debugVerboseLog("Vendor patch level is not within valid range."); 420 return false; 421 } 422 423 if (!isValidPatchLevel(attestationAttributes.getKeyBootPatchLevel())) { 424 debugVerboseLog("Boot patch level is not within valid range."); 425 return false; 426 } 427 428 return true; 429 } 430 checkPublicKey( @onNull Certificate certificate, @NonNull byte[] expectedPublicKey)431 private boolean checkPublicKey( 432 @NonNull Certificate certificate, @NonNull byte[] expectedPublicKey) { 433 final byte[] publicKey = certificate.getPublicKey().getEncoded(); 434 return Arrays.equals(publicKey, expectedPublicKey); 435 } 436 checkAttestationChallenge( @onNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, @NonNull byte[] expectedChallenge)437 private boolean checkAttestationChallenge( 438 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes, 439 @NonNull byte[] expectedChallenge) { 440 final byte[] challenge = attestationAttributes.getAttestationChallenge().toByteArray(); 441 return Arrays.equals(challenge, expectedChallenge); 442 } 443 checkOwnedBySystem(@onNull X509Certificate certificate, @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes)444 private boolean checkOwnedBySystem(@NonNull X509Certificate certificate, 445 @NonNull AndroidKeystoreAttestationVerificationAttributes attestationAttributes) { 446 final Set<String> ownerPackages = 447 attestationAttributes.getApplicationPackageNameVersion().keySet(); 448 if (!ANDROID_SYSTEM_PACKAGE_NAME_SET.equals(ownerPackages)) { 449 debugVerboseLog("Owner is not system, packages=" + ownerPackages); 450 return false; 451 } 452 453 return true; 454 } 455 456 /** 457 * Validates patchLevel passed is within range of the local device patch date if local patch is 458 * not over one year old. Since the time can be changed on device, just checking the patch date 459 * is not enough. Therefore, we also confirm the patch level for the remote and local device are 460 * similar. 461 */ isValidPatchLevel(int patchLevel)462 private boolean isValidPatchLevel(int patchLevel) { 463 LocalDate currentDate = mTestSystemDate != null 464 ? mTestSystemDate : LocalDate.now(ZoneId.systemDefault()); 465 466 // Convert local patch date to LocalDate. 467 LocalDate localPatchDate; 468 try { 469 if (mTestLocalPatchDate != null) { 470 localPatchDate = mTestLocalPatchDate; 471 } else { 472 localPatchDate = LocalDate.parse(Build.VERSION.SECURITY_PATCH); 473 } 474 } catch (Throwable t) { 475 debugVerboseLog("Build.VERSION.SECURITY_PATCH: " 476 + Build.VERSION.SECURITY_PATCH + " is not in format YYYY-MM-DD"); 477 return false; 478 } 479 480 // Check local patch date is not in last year of system clock. 481 if (ChronoUnit.MONTHS.between(localPatchDate, currentDate) > MAX_PATCH_AGE_MONTHS) { 482 return true; 483 } 484 485 // Convert remote patch dates to LocalDate. 486 String remoteDeviceDateStr = String.valueOf(patchLevel); 487 if (remoteDeviceDateStr.length() != 6 && remoteDeviceDateStr.length() != 8) { 488 debugVerboseLog("Patch level is not in format YYYYMM or YYYYMMDD"); 489 return false; 490 } 491 492 int patchYear = Integer.parseInt(remoteDeviceDateStr.substring(0, 4)); 493 int patchMonth = Integer.parseInt(remoteDeviceDateStr.substring(4, 6)); 494 LocalDate remotePatchDate = LocalDate.of(patchYear, patchMonth, 1); 495 496 // Check patch dates are within 1 year of each other 497 boolean IsRemotePatchWithinOneYearOfLocalPatch; 498 if (remotePatchDate.compareTo(localPatchDate) > 0) { 499 IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between( 500 localPatchDate, remotePatchDate) <= MAX_PATCH_AGE_MONTHS; 501 } else if (remotePatchDate.compareTo(localPatchDate) < 0) { 502 IsRemotePatchWithinOneYearOfLocalPatch = ChronoUnit.MONTHS.between( 503 remotePatchDate, localPatchDate) <= MAX_PATCH_AGE_MONTHS; 504 } else { 505 IsRemotePatchWithinOneYearOfLocalPatch = true; 506 } 507 508 return IsRemotePatchWithinOneYearOfLocalPatch; 509 } 510 511 /** 512 * Checks certificate revocation status. 513 * 514 * Queries status list from android.googleapis.com/attestation/status and checks for 515 * the existence of certificate's serial number. If serial number exists in map, then fail. 516 */ 517 private final class AndroidRevocationStatusListChecker extends PKIXCertPathChecker { 518 private static final String TOP_LEVEL_JSON_PROPERTY_KEY = "entries"; 519 private static final String STATUS_PROPERTY_KEY = "status"; 520 private static final String REASON_PROPERTY_KEY = "reason"; 521 private String mStatusUrl; 522 private JSONObject mJsonStatusMap; 523 524 @Override init(boolean forward)525 public void init(boolean forward) throws CertPathValidatorException { 526 mStatusUrl = getRevocationListUrl(); 527 if (mStatusUrl == null || mStatusUrl.isEmpty()) { 528 throw new CertPathValidatorException( 529 "R.string.vendor_required_attestation_revocation_list_url is empty."); 530 } 531 // TODO(b/221067843): Update to only pull status map on non critical path and if 532 // out of date (24hrs). 533 mJsonStatusMap = getStatusMap(mStatusUrl); 534 } 535 536 @Override isForwardCheckingSupported()537 public boolean isForwardCheckingSupported() { 538 return false; 539 } 540 541 @Override getSupportedExtensions()542 public Set<String> getSupportedExtensions() { 543 return null; 544 } 545 546 @Override check(Certificate cert, Collection<String> unresolvedCritExts)547 public void check(Certificate cert, Collection<String> unresolvedCritExts) 548 throws CertPathValidatorException { 549 X509Certificate x509Certificate = (X509Certificate) cert; 550 // The json key is the certificate's serial number converted to lowercase hex. 551 String serialNumber = x509Certificate.getSerialNumber().toString(16); 552 553 if (serialNumber == null) { 554 throw new CertPathValidatorException("Certificate serial number can not be null."); 555 } 556 557 if (mJsonStatusMap.has(serialNumber)) { 558 JSONObject revocationStatus; 559 String status; 560 String reason; 561 try { 562 revocationStatus = mJsonStatusMap.getJSONObject(serialNumber); 563 status = revocationStatus.getString(STATUS_PROPERTY_KEY); 564 reason = revocationStatus.getString(REASON_PROPERTY_KEY); 565 } catch (Throwable t) { 566 throw new CertPathValidatorException("Unable get properties for certificate " 567 + "with serial number " + serialNumber); 568 } 569 throw new CertPathValidatorException( 570 "Invalid certificate with serial number " + serialNumber 571 + " has status " + status 572 + " because reason " + reason); 573 } 574 } 575 getStatusMap(String stringUrl)576 private JSONObject getStatusMap(String stringUrl) throws CertPathValidatorException { 577 URL url; 578 try { 579 url = new URL(stringUrl); 580 } catch (Throwable t) { 581 throw new CertPathValidatorException( 582 "Unable to get revocation status from " + mStatusUrl, t); 583 } 584 585 try (InputStream inputStream = url.openStream()) { 586 JSONObject statusListJson = new JSONObject( 587 new String(inputStream.readAllBytes(), UTF_8)); 588 return statusListJson.getJSONObject(TOP_LEVEL_JSON_PROPERTY_KEY); 589 } catch (Throwable t) { 590 throw new CertPathValidatorException( 591 "Unable to parse revocation status from " + mStatusUrl, t); 592 } 593 } 594 getRevocationListUrl()595 private String getRevocationListUrl() { 596 return mContext.getResources().getString( 597 R.string.vendor_required_attestation_revocation_list_url); 598 } 599 } 600 debugVerboseLog(String str, Throwable t)601 private static void debugVerboseLog(String str, Throwable t) { 602 if (DEBUG) { 603 Slog.v(TAG, str, t); 604 } 605 } 606 debugVerboseLog(String str)607 private static void debugVerboseLog(String str) { 608 if (DEBUG) { 609 Slog.v(TAG, str); 610 } 611 } 612 } 613