1 /* 2 * Copyright (C) 2016 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 android.util; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.pm.Signature; 22 import android.text.TextUtils; 23 24 import libcore.util.HexEncoding; 25 26 import java.io.ByteArrayOutputStream; 27 import java.io.IOException; 28 import java.security.MessageDigest; 29 import java.security.NoSuchAlgorithmException; 30 import java.util.Arrays; 31 32 /** 33 * Helper functions applicable to packages. 34 * @hide 35 */ 36 public final class PackageUtils { 37 PackageUtils()38 private PackageUtils() { 39 /* hide constructor */ 40 } 41 42 /** 43 * @see #computeSignaturesSha256Digests(Signature[], String) 44 */ computeSignaturesSha256Digests( @onNull Signature[] signatures)45 public static @NonNull String[] computeSignaturesSha256Digests( 46 @NonNull Signature[] signatures) { 47 return computeSignaturesSha256Digests(signatures, null); 48 } 49 50 /** 51 * Computes the SHA256 digests of a list of signatures. Items in the 52 * resulting array of hashes correspond to the signatures in the 53 * input array. 54 * @param signatures The signatures. 55 * @param separator Separator between each pair of characters, such as a colon, or null to omit. 56 * @return The digest array. 57 */ computeSignaturesSha256Digests( @onNull Signature[] signatures, @Nullable String separator)58 public static @NonNull String[] computeSignaturesSha256Digests( 59 @NonNull Signature[] signatures, @Nullable String separator) { 60 final int signatureCount = signatures.length; 61 final String[] digests = new String[signatureCount]; 62 for (int i = 0; i < signatureCount; i++) { 63 digests[i] = computeSha256Digest(signatures[i].toByteArray(), separator); 64 } 65 return digests; 66 } 67 /** 68 * Computes a SHA256 digest of the signatures' SHA256 digests. First, 69 * individual hashes for each signature is derived in a hexademical 70 * form, then these strings are sorted based the natural ordering, and 71 * finally a hash is derived from these strings' bytes. 72 * @param signatures The signatures. 73 * @return The digest. 74 */ computeSignaturesSha256Digest( @onNull Signature[] signatures)75 public static @NonNull String computeSignaturesSha256Digest( 76 @NonNull Signature[] signatures) { 77 // Shortcut for optimization - most apps singed by a single cert 78 if (signatures.length == 1) { 79 return computeSha256Digest(signatures[0].toByteArray(), null); 80 } 81 82 // Make sure these are sorted to handle reversed certificates 83 final String[] sha256Digests = computeSignaturesSha256Digests(signatures, null); 84 return computeSignaturesSha256Digest(sha256Digests); 85 } 86 87 /** 88 * Computes a SHA256 digest in of the signatures SHA256 digests. First, 89 * the strings are sorted based the natural ordering, and then a hash is 90 * derived from these strings' bytes. 91 * @param sha256Digests Signature SHA256 hashes in hexademical form. 92 * @return The digest. 93 */ computeSignaturesSha256Digest( @onNull String[] sha256Digests)94 public static @NonNull String computeSignaturesSha256Digest( 95 @NonNull String[] sha256Digests) { 96 // Shortcut for optimization - most apps singed by a single cert 97 if (sha256Digests.length == 1) { 98 return sha256Digests[0]; 99 } 100 101 // Make sure these are sorted to handle reversed certificates 102 Arrays.sort(sha256Digests); 103 104 final ByteArrayOutputStream bytes = new ByteArrayOutputStream(); 105 for (String sha256Digest : sha256Digests) { 106 try { 107 bytes.write(sha256Digest.getBytes()); 108 } catch (IOException e) { 109 /* ignore - can't happen */ 110 } 111 } 112 return computeSha256Digest(bytes.toByteArray(), null); 113 } 114 115 /** 116 * Computes the SHA256 digest of some data. 117 * @param data The data. 118 * @return The digest or null if an error occurs. 119 */ computeSha256DigestBytes(@onNull byte[] data)120 public static @Nullable byte[] computeSha256DigestBytes(@NonNull byte[] data) { 121 MessageDigest messageDigest; 122 try { 123 messageDigest = MessageDigest.getInstance("SHA256"); 124 } catch (NoSuchAlgorithmException e) { 125 /* can't happen */ 126 return null; 127 } 128 129 messageDigest.update(data); 130 131 return messageDigest.digest(); 132 } 133 134 /** 135 * @see #computeSha256Digest(byte[], String) 136 */ computeSha256Digest(@onNull byte[] data)137 public static @Nullable String computeSha256Digest(@NonNull byte[] data) { 138 return computeSha256Digest(data, null); 139 } 140 /** 141 * Computes the SHA256 digest of some data. 142 * @param data The data. 143 * @param separator Separator between each pair of characters, such as a colon, or null to omit. 144 * @return The digest or null if an error occurs. 145 */ computeSha256Digest(@onNull byte[] data, @Nullable String separator)146 public static @Nullable String computeSha256Digest(@NonNull byte[] data, 147 @Nullable String separator) { 148 byte[] sha256DigestBytes = computeSha256DigestBytes(data); 149 if (sha256DigestBytes == null) { 150 return null; 151 } 152 153 if (separator == null) { 154 return HexEncoding.encodeToString(sha256DigestBytes, true /* uppercase */); 155 } 156 157 int length = sha256DigestBytes.length; 158 String[] pieces = new String[length]; 159 for (int index = 0; index < length; index++) { 160 pieces[index] = HexEncoding.encodeToString(sha256DigestBytes[index], true); 161 } 162 163 return TextUtils.join(separator, pieces); 164 } 165 } 166