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