1 /* Copyright 2019, The Android Open Source Project, Inc. 2 * 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 package com.google.android.attestation; 17 18 import com.google.gson.Gson; 19 import com.google.gson.JsonObject; 20 import com.google.gson.JsonParser; 21 import java.io.ByteArrayInputStream; 22 import java.io.FileReader; 23 import java.io.IOException; 24 import java.io.InputStreamReader; 25 import java.io.Reader; 26 import java.math.BigInteger; 27 import java.net.MalformedURLException; 28 import java.net.URL; 29 import java.nio.file.Files; 30 import java.util.HashMap; 31 32 33 /** 34 * Utils for fetching and decoding attestation certificate status. 35 */ 36 public class CertificateRevocationStatus { 37 38 private static final String STATUS_URL = "https://android.googleapis.com/attestation/status"; 39 public final Status status; 40 public final Reason reason; 41 public final String comment; 42 public final String expires; 43 fetchAllEntries()44 public static HashMap<String, CertificateRevocationStatus> fetchAllEntries() throws IOException { 45 URL url = new URL(STATUS_URL); 46 InputStreamReader statusListReader = new InputStreamReader(url.openStream()); 47 return getEntryToStatusMap(statusListReader); 48 } 49 loadAllEntriesFromFile(String filePath)50 public static HashMap<String, CertificateRevocationStatus> loadAllEntriesFromFile(String filePath) 51 throws IOException { 52 FileReader reader = new FileReader(filePath); 53 return getEntryToStatusMap(reader); 54 } 55 getEntryToStatusMap( Reader statusListReader)56 private static HashMap<String, CertificateRevocationStatus> getEntryToStatusMap( 57 Reader statusListReader) { 58 JsonObject entries = 59 new JsonParser().parse(statusListReader).getAsJsonObject().getAsJsonObject("entries"); 60 61 HashMap<String, CertificateRevocationStatus> serialNumberToStatus = new HashMap<>(); 62 for (String serialNumber : entries.keySet()) { 63 serialNumberToStatus.put( 64 serialNumber, 65 new Gson().fromJson(entries.get(serialNumber), CertificateRevocationStatus.class)); 66 } 67 68 return serialNumberToStatus; 69 } 70 loadStatusFromFile(BigInteger serialNumber, String filePath)71 public static CertificateRevocationStatus loadStatusFromFile(BigInteger serialNumber, 72 String filePath) 73 throws IOException { 74 return loadStatusFromFile(serialNumber.toString(16), filePath); 75 } 76 loadStatusFromFile(String serialNumber, String filePath)77 public static CertificateRevocationStatus loadStatusFromFile(String serialNumber, String filePath) 78 throws IOException { 79 FileReader reader = new FileReader(filePath); 80 return decodeStatus(serialNumber, reader); 81 } 82 83 fetchStatus(BigInteger serialNumber)84 public static CertificateRevocationStatus fetchStatus(BigInteger serialNumber) 85 throws IOException { 86 return fetchStatus(serialNumber.toString(16)); 87 } 88 fetchStatus(String serialNumber)89 public static CertificateRevocationStatus fetchStatus(String serialNumber) throws IOException { 90 URL url; 91 try { 92 url = new URL(STATUS_URL); 93 } catch (MalformedURLException e) { 94 throw new IllegalStateException(e); 95 } 96 97 InputStreamReader statusListReader = new InputStreamReader(url.openStream()); 98 99 return decodeStatus(serialNumber, statusListReader); 100 101 } 102 decodeStatus(String serialNumber, Reader statusListReader)103 private static CertificateRevocationStatus decodeStatus(String serialNumber, 104 Reader statusListReader) { 105 if (serialNumber == null) { 106 throw new IllegalArgumentException("serialNumber cannot be null"); 107 } 108 serialNumber = serialNumber.toLowerCase(); 109 110 JsonObject entries = new JsonParser().parse(statusListReader) 111 .getAsJsonObject() 112 .getAsJsonObject("entries"); 113 114 if (!entries.has(serialNumber)) { 115 return null; 116 } 117 118 return new Gson().fromJson(entries.get(serialNumber), CertificateRevocationStatus.class); 119 } 120 121 public enum Status { 122 REVOKED, SUSPENDED 123 } 124 125 public enum Reason { 126 UNSPECIFIED, KEY_COMPROMISE, CA_COMPROMISE, SUPERSEDED, SOFTWARE_FLAW 127 } 128 CertificateRevocationStatus()129 public CertificateRevocationStatus() { 130 status = Status.REVOKED; 131 reason = Reason.UNSPECIFIED; 132 comment = null; 133 expires = null; 134 } 135 } 136