1 /* 2 * Copyright (C) 2023 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.appsearch.util; 18 19 import android.content.Context; 20 import android.content.pm.PackageInfo; 21 import android.content.pm.PackageManager; 22 import android.content.pm.PackageManager.NameNotFoundException; 23 import android.content.pm.Signature; 24 import android.os.Build; 25 26 import java.security.MessageDigest; 27 import java.security.NoSuchAlgorithmException; 28 import java.util.Arrays; 29 30 /** 31 * Utility to verify if the package is signed with correct certificates. 32 * 33 * @hide 34 */ 35 public final class PackageManagerUtil { 36 37 /** 38 * Verifies if the callingPackage has correct matching certificate. 39 * 40 * <p>For Pre-P devices, this matches with a single byte-array corresponding to the oldest 41 * available signature. For P+ devices, it used existing PackageManager's hasSigningCertificate 42 * implementation that takes rotation history in account. 43 * 44 * @param context Context of the calling app. 45 * @param packageName package whose signing certificates to check 46 * @param sha256cert sha256 of the signing certificate for which to search 47 * @return true if this package was or is signed by exactly the certificate with SHA-256 as 48 * {@code sha256cert} 49 */ hasSigningCertificate( Context context, String packageName, byte[] sha256cert)50 public static boolean hasSigningCertificate( 51 Context context, String packageName, byte[] sha256cert) { 52 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 53 return hasSigningCertificateBelowP(context, packageName, sha256cert); 54 } 55 56 return context.getPackageManager() 57 .hasSigningCertificate(packageName, sha256cert, PackageManager.CERT_INPUT_SHA256); 58 } 59 hasSigningCertificateBelowP( Context context, String packageName, byte[] sha256cert)60 private static boolean hasSigningCertificateBelowP( 61 Context context, String packageName, byte[] sha256cert) { 62 PackageInfo packageInfo; 63 try { 64 packageInfo = 65 context.getPackageManager() 66 .getPackageInfo(packageName, PackageManager.GET_SIGNATURES); 67 } catch (NameNotFoundException e) { 68 throw new IllegalArgumentException("Given package does not exist on device!"); 69 } 70 if (packageInfo == null) { 71 return false; 72 } 73 74 // Verification of an android application requires set-equals matching, to avoid known 75 // security vulnerabilities a certificate hash will only be matched when exactly one 76 // certificate is present. See http://issuetracker.google.com/36992561 for more information. 77 try { 78 Signature[] signatures = packageInfo.signatures; 79 if (signatures != null && signatures.length == 1) { 80 byte[] certificate = 81 MessageDigest.getInstance(/* algorithm= */ "SHA-256") 82 .digest(signatures[0].toByteArray()); 83 return Arrays.equals(certificate, sha256cert); 84 } 85 } catch (NoSuchAlgorithmException e) { 86 throw new IllegalArgumentException("Provided SHA-256 implementation is invalid!"); 87 } 88 return false; 89 } 90 PackageManagerUtil()91 private PackageManagerUtil() {} 92 } 93