/* * Copyright (C) 2016 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 android.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.Signature; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; /** * Helper functions applicable to packages. * @hide */ public final class PackageUtils { private PackageUtils() { /* hide constructor */ } /** * Computes the SHA256 digests of a list of signatures. Items in the * resulting array of hashes correspond to the signatures in the * input array. * @param signatures The signatures. * @return The digest array. */ public static @NonNull String[] computeSignaturesSha256Digests( @NonNull Signature[] signatures) { final int signatureCount = signatures.length; final String[] digests = new String[signatureCount]; for (int i = 0; i < signatureCount; i++) { digests[i] = computeSha256Digest(signatures[i].toByteArray()); } return digests; } /** * Computes a SHA256 digest of the signatures' SHA256 digests. First, * individual hashes for each signature is derived in a hexademical * form, then these strings are sorted based the natural ordering, and * finally a hash is derived from these strings' bytes. * @param signatures The signatures. * @return The digest. */ public static @NonNull String computeSignaturesSha256Digest( @NonNull Signature[] signatures) { // Shortcut for optimization - most apps singed by a single cert if (signatures.length == 1) { return computeSha256Digest(signatures[0].toByteArray()); } // Make sure these are sorted to handle reversed certificates final String[] sha256Digests = computeSignaturesSha256Digests(signatures); return computeSignaturesSha256Digest(sha256Digests); } /** * Computes a SHA256 digest in of the signatures SHA256 digests. First, * the strings are sorted based the natural ordering, and then a hash is * derived from these strings' bytes. * @param sha256Digests Signature SHA256 hashes in hexademical form. * @return The digest. */ public static @NonNull String computeSignaturesSha256Digest( @NonNull String[] sha256Digests) { // Shortcut for optimization - most apps singed by a single cert if (sha256Digests.length == 1) { return sha256Digests[0]; } // Make sure these are sorted to handle reversed certificates Arrays.sort(sha256Digests); final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); for (String sha256Digest : sha256Digests) { try { bytes.write(sha256Digest.getBytes()); } catch (IOException e) { /* ignore - can't happen */ } } return computeSha256Digest(bytes.toByteArray()); } /** * Computes the SHA256 digest of some data. * @param data The data. * @return The digest or null if an error occurs. */ public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) { MessageDigest messageDigest; try { messageDigest = MessageDigest.getInstance("SHA256"); } catch (NoSuchAlgorithmException e) { /* can't happen */ return null; } messageDigest.update(data); return messageDigest.digest(); } /** * Computes the SHA256 digest of some data. * @param data The data. * @return The digest or null if an error occurs. */ public static @Nullable String computeSha256Digest(@NonNull byte[] data) { return ByteStringUtils.toHexString(computeSha256DigestBytes(data)); } }