1 /* 2 * Copyright (C) 2008 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.content.pm; 18 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 22 import com.android.internal.util.ArrayUtils; 23 24 import java.io.ByteArrayInputStream; 25 import java.lang.ref.SoftReference; 26 import java.security.PublicKey; 27 import java.security.cert.Certificate; 28 import java.security.cert.CertificateException; 29 import java.security.cert.CertificateFactory; 30 import java.util.Arrays; 31 32 /** 33 * Opaque, immutable representation of a signature associated with an 34 * application package. 35 */ 36 public class Signature implements Parcelable { 37 private final byte[] mSignature; 38 private int mHashCode; 39 private boolean mHaveHashCode; 40 private SoftReference<String> mStringRef; 41 42 /** 43 * Create Signature from an existing raw byte array. 44 */ Signature(byte[] signature)45 public Signature(byte[] signature) { 46 mSignature = signature.clone(); 47 } 48 parseHexDigit(int nibble)49 private static final int parseHexDigit(int nibble) { 50 if ('0' <= nibble && nibble <= '9') { 51 return nibble - '0'; 52 } else if ('a' <= nibble && nibble <= 'f') { 53 return nibble - 'a' + 10; 54 } else if ('A' <= nibble && nibble <= 'F') { 55 return nibble - 'A' + 10; 56 } else { 57 throw new IllegalArgumentException("Invalid character " + nibble + " in hex string"); 58 } 59 } 60 61 /** 62 * Create Signature from a text representation previously returned by 63 * {@link #toChars} or {@link #toCharsString()}. Signatures are expected to 64 * be a hex-encoded ASCII string. 65 * 66 * @param text hex-encoded string representing the signature 67 * @throws IllegalArgumentException when signature is odd-length 68 */ Signature(String text)69 public Signature(String text) { 70 final byte[] input = text.getBytes(); 71 final int N = input.length; 72 73 if (N % 2 != 0) { 74 throw new IllegalArgumentException("text size " + N + " is not even"); 75 } 76 77 final byte[] sig = new byte[N / 2]; 78 int sigIndex = 0; 79 80 for (int i = 0; i < N;) { 81 final int hi = parseHexDigit(input[i++]); 82 final int lo = parseHexDigit(input[i++]); 83 sig[sigIndex++] = (byte) ((hi << 4) | lo); 84 } 85 86 mSignature = sig; 87 } 88 89 /** 90 * Encode the Signature as ASCII text. 91 */ toChars()92 public char[] toChars() { 93 return toChars(null, null); 94 } 95 96 /** 97 * Encode the Signature as ASCII text in to an existing array. 98 * 99 * @param existingArray Existing char array or null. 100 * @param outLen Output parameter for the number of characters written in 101 * to the array. 102 * @return Returns either <var>existingArray</var> if it was large enough 103 * to hold the ASCII representation, or a newly created char[] array if 104 * needed. 105 */ toChars(char[] existingArray, int[] outLen)106 public char[] toChars(char[] existingArray, int[] outLen) { 107 byte[] sig = mSignature; 108 final int N = sig.length; 109 final int N2 = N*2; 110 char[] text = existingArray == null || N2 > existingArray.length 111 ? new char[N2] : existingArray; 112 for (int j=0; j<N; j++) { 113 byte v = sig[j]; 114 int d = (v>>4)&0xf; 115 text[j*2] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d)); 116 d = v&0xf; 117 text[j*2+1] = (char)(d >= 10 ? ('a' + d - 10) : ('0' + d)); 118 } 119 if (outLen != null) outLen[0] = N; 120 return text; 121 } 122 123 /** 124 * Return the result of {@link #toChars()} as a String. 125 */ toCharsString()126 public String toCharsString() { 127 String str = mStringRef == null ? null : mStringRef.get(); 128 if (str != null) { 129 return str; 130 } 131 str = new String(toChars()); 132 mStringRef = new SoftReference<String>(str); 133 return str; 134 } 135 136 /** 137 * @return the contents of this signature as a byte array. 138 */ toByteArray()139 public byte[] toByteArray() { 140 byte[] bytes = new byte[mSignature.length]; 141 System.arraycopy(mSignature, 0, bytes, 0, mSignature.length); 142 return bytes; 143 } 144 145 /** 146 * Returns the public key for this signature. 147 * 148 * @throws CertificateException when Signature isn't a valid X.509 149 * certificate; shouldn't happen. 150 * @hide 151 */ getPublicKey()152 public PublicKey getPublicKey() throws CertificateException { 153 final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); 154 final ByteArrayInputStream bais = new ByteArrayInputStream(mSignature); 155 final Certificate cert = certFactory.generateCertificate(bais); 156 return cert.getPublicKey(); 157 } 158 159 @Override equals(Object obj)160 public boolean equals(Object obj) { 161 try { 162 if (obj != null) { 163 Signature other = (Signature)obj; 164 return this == other || Arrays.equals(mSignature, other.mSignature); 165 } 166 } catch (ClassCastException e) { 167 } 168 return false; 169 } 170 171 @Override hashCode()172 public int hashCode() { 173 if (mHaveHashCode) { 174 return mHashCode; 175 } 176 mHashCode = Arrays.hashCode(mSignature); 177 mHaveHashCode = true; 178 return mHashCode; 179 } 180 describeContents()181 public int describeContents() { 182 return 0; 183 } 184 writeToParcel(Parcel dest, int parcelableFlags)185 public void writeToParcel(Parcel dest, int parcelableFlags) { 186 dest.writeByteArray(mSignature); 187 } 188 189 public static final Parcelable.Creator<Signature> CREATOR 190 = new Parcelable.Creator<Signature>() { 191 public Signature createFromParcel(Parcel source) { 192 return new Signature(source); 193 } 194 195 public Signature[] newArray(int size) { 196 return new Signature[size]; 197 } 198 }; 199 Signature(Parcel source)200 private Signature(Parcel source) { 201 mSignature = source.createByteArray(); 202 } 203 204 /** 205 * Test if given {@link Signature} sets are exactly equal. 206 * 207 * @hide 208 */ areExactMatch(Signature[] a, Signature[] b)209 public static boolean areExactMatch(Signature[] a, Signature[] b) { 210 return ArrayUtils.containsAll(a, b) && ArrayUtils.containsAll(b, a); 211 } 212 } 213