/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.statementservice.retriever; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; /** * Utility library for computing certificate fingerprints. Also includes fields name used by * Statement JSON string. */ public final class Utils { private Utils() {} /** * Field name for namespace. */ public static final String NAMESPACE_FIELD = "namespace"; /** * Supported asset namespaces. */ public static final String NAMESPACE_WEB = "web"; public static final String NAMESPACE_ANDROID_APP = "android_app"; /** * Field names in a web asset descriptor. */ public static final String WEB_ASSET_FIELD_SITE = "site"; /** * Field names in a Android app asset descriptor. */ public static final String ANDROID_APP_ASSET_FIELD_PACKAGE_NAME = "package_name"; public static final String ANDROID_APP_ASSET_FIELD_CERT_FPS = "sha256_cert_fingerprints"; /** * Field names in a statement. */ public static final String ASSET_DESCRIPTOR_FIELD_RELATION = "relation"; public static final String ASSET_DESCRIPTOR_FIELD_TARGET = "target"; public static final String DELEGATE_FIELD_DELEGATE = "include"; private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; /** * Joins a list of strings, by placing separator between each string. For example, * {@code joinStrings("; ", Arrays.asList(new String[]{"a", "b", "c"}))} returns * "{@code a; b; c}". */ public static String joinStrings(String separator, List strings) { switch(strings.size()) { case 0: return ""; case 1: return strings.get(0); default: StringBuilder joiner = new StringBuilder(); boolean first = true; for (String field : strings) { if (first) { first = false; } else { joiner.append(separator); } joiner.append(field); } return joiner.toString(); } } /** * Returns the normalized sha-256 fingerprints of a given package according to the Android * package manager. */ public static List getCertFingerprintsFromPackageManager(String packageName, Context context) throws NameNotFoundException { Signature[] signatures = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures; ArrayList result = new ArrayList(signatures.length); for (Signature sig : signatures) { result.add(computeNormalizedSha256Fingerprint(sig.toByteArray())); } return result; } /** * Computes the hash of the byte array using the specified algorithm, returning a hex string * with a colon between each byte. */ public static String computeNormalizedSha256Fingerprint(byte[] signature) { MessageDigest digester; try { digester = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new AssertionError("No SHA-256 implementation found."); } digester.update(signature); return byteArrayToHexString(digester.digest()); } /** * Returns true if there is at least one common string between the two lists of string. */ public static boolean hasCommonString(List list1, List list2) { HashSet set2 = new HashSet<>(list2); for (String string : list1) { if (set2.contains(string)) { return true; } } return false; } /** * Converts the byte array to an lowercase hexadecimal digits String with a colon character (:) * between each byte. */ private static String byteArrayToHexString(byte[] array) { if (array.length == 0) { return ""; } char[] buf = new char[array.length * 3 - 1]; int bufIndex = 0; for (int i = 0; i < array.length; i++) { byte b = array[i]; if (i > 0) { buf[bufIndex++] = ':'; } buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F]; buf[bufIndex++] = HEX_DIGITS[b & 0x0F]; } return new String(buf); } }