jdk/src/share/classes/sun/security/util/SignatureFileVerifier.java
changeset 2 90ce3da70b43
child 4152 bc36a9f01ac6
equal deleted inserted replaced
0:fd16c54261b3 2:90ce3da70b43
       
     1 /*
       
     2  * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.security.util;
       
    27 
       
    28 import java.security.CodeSigner;
       
    29 import java.security.cert.CertPath;
       
    30 import java.security.cert.X509Certificate;
       
    31 import java.security.cert.CertificateException;
       
    32 import java.security.cert.CertificateFactory;
       
    33 import java.security.*;
       
    34 import java.io.*;
       
    35 import java.util.*;
       
    36 import java.util.jar.*;
       
    37 import java.io.ByteArrayOutputStream;
       
    38 
       
    39 import sun.security.pkcs.*;
       
    40 import sun.security.timestamp.TimestampToken;
       
    41 import sun.misc.BASE64Decoder;
       
    42 
       
    43 import sun.security.jca.Providers;
       
    44 
       
    45 public class SignatureFileVerifier {
       
    46 
       
    47     /* Are we debugging ? */
       
    48     private static final Debug debug = Debug.getInstance("jar");
       
    49 
       
    50     /* cache of CodeSigner objects */
       
    51     private ArrayList<CodeSigner[]> signerCache;
       
    52 
       
    53     private static final String ATTR_DIGEST =
       
    54         ("-DIGEST-" + ManifestDigester.MF_MAIN_ATTRS).toUpperCase
       
    55         (Locale.ENGLISH);
       
    56 
       
    57     /** the PKCS7 block for this .DSA/.RSA file */
       
    58     private PKCS7 block;
       
    59 
       
    60     /** the raw bytes of the .SF file */
       
    61     private byte sfBytes[];
       
    62 
       
    63     /** the name of the signature block file, uppercased and without
       
    64      *  the extension (.DSA/.RSA)
       
    65      */
       
    66     private String name;
       
    67 
       
    68     /** the ManifestDigester */
       
    69     private ManifestDigester md;
       
    70 
       
    71     /** cache of created MessageDigest objects */
       
    72     private HashMap<String, MessageDigest> createdDigests;
       
    73 
       
    74     /* workaround for parsing Netscape jars  */
       
    75     private boolean workaround = false;
       
    76 
       
    77     /* for generating certpath objects */
       
    78     private CertificateFactory certificateFactory = null;
       
    79 
       
    80     /**
       
    81      * Create the named SignatureFileVerifier.
       
    82      *
       
    83      * @param name the name of the signature block file (.DSA/.RSA)
       
    84      *
       
    85      * @param rawBytes the raw bytes of the signature block file
       
    86      */
       
    87     public SignatureFileVerifier(ArrayList<CodeSigner[]> signerCache,
       
    88                                  ManifestDigester md,
       
    89                                  String name,
       
    90                                  byte rawBytes[])
       
    91         throws IOException, CertificateException
       
    92     {
       
    93         // new PKCS7() calls CertificateFactory.getInstance()
       
    94         // need to use local providers here, see Providers class
       
    95         Object obj = null;
       
    96         try {
       
    97             obj = Providers.startJarVerification();
       
    98             block = new PKCS7(rawBytes);
       
    99             sfBytes = block.getContentInfo().getData();
       
   100             certificateFactory = CertificateFactory.getInstance("X509");
       
   101         } finally {
       
   102             Providers.stopJarVerification(obj);
       
   103         }
       
   104         this.name = name.substring(0, name.lastIndexOf("."))
       
   105                                                    .toUpperCase(Locale.ENGLISH);
       
   106         this.md = md;
       
   107         this.signerCache = signerCache;
       
   108     }
       
   109 
       
   110     /**
       
   111      * returns true if we need the .SF file
       
   112      */
       
   113     public boolean needSignatureFileBytes()
       
   114     {
       
   115 
       
   116         return sfBytes == null;
       
   117     }
       
   118 
       
   119 
       
   120     /**
       
   121      * returns true if we need this .SF file.
       
   122      *
       
   123      * @param name the name of the .SF file without the extension
       
   124      *
       
   125      */
       
   126     public boolean needSignatureFile(String name)
       
   127     {
       
   128         return this.name.equalsIgnoreCase(name);
       
   129     }
       
   130 
       
   131     /**
       
   132      * used to set the raw bytes of the .SF file when it
       
   133      * is external to the signature block file.
       
   134      */
       
   135     public void setSignatureFile(byte sfBytes[])
       
   136     {
       
   137         this.sfBytes = sfBytes;
       
   138     }
       
   139 
       
   140     /**
       
   141      * Utility method used by JarVerifier and JarSigner
       
   142      * to determine the signature file names and PKCS7 block
       
   143      * files names that are supported
       
   144      *
       
   145      * @param s file name
       
   146      * @return true if the input file name is a supported
       
   147      *          Signature File or PKCS7 block file name
       
   148      */
       
   149     public static boolean isBlockOrSF(String s) {
       
   150         // we currently only support DSA and RSA PKCS7 blocks
       
   151         if (s.endsWith(".SF") || s.endsWith(".DSA") || s.endsWith(".RSA")) {
       
   152             return true;
       
   153         }
       
   154         return false;
       
   155     }
       
   156 
       
   157     /** get digest from cache */
       
   158 
       
   159     private MessageDigest getDigest(String algorithm)
       
   160     {
       
   161         if (createdDigests == null)
       
   162             createdDigests = new HashMap<String, MessageDigest>();
       
   163 
       
   164         MessageDigest digest = createdDigests.get(algorithm);
       
   165 
       
   166         if (digest == null) {
       
   167             try {
       
   168                 digest = MessageDigest.getInstance(algorithm);
       
   169                 createdDigests.put(algorithm, digest);
       
   170             } catch (NoSuchAlgorithmException nsae) {
       
   171                 // ignore
       
   172             }
       
   173         }
       
   174         return digest;
       
   175     }
       
   176 
       
   177     /**
       
   178      * process the signature block file. Goes through the .SF file
       
   179      * and adds code signers for each section where the .SF section
       
   180      * hash was verified against the Manifest section.
       
   181      *
       
   182      *
       
   183      */
       
   184     public void process(Hashtable<String, CodeSigner[]> signers)
       
   185         throws IOException, SignatureException, NoSuchAlgorithmException,
       
   186             JarException, CertificateException
       
   187     {
       
   188         // calls Signature.getInstance() and MessageDigest.getInstance()
       
   189         // need to use local providers here, see Providers class
       
   190         Object obj = null;
       
   191         try {
       
   192             obj = Providers.startJarVerification();
       
   193             processImpl(signers);
       
   194         } finally {
       
   195             Providers.stopJarVerification(obj);
       
   196         }
       
   197 
       
   198     }
       
   199 
       
   200     private void processImpl(Hashtable<String, CodeSigner[]> signers)
       
   201         throws IOException, SignatureException, NoSuchAlgorithmException,
       
   202             JarException, CertificateException
       
   203     {
       
   204         Manifest sf = new Manifest();
       
   205         sf.read(new ByteArrayInputStream(sfBytes));
       
   206 
       
   207         String version =
       
   208             sf.getMainAttributes().getValue(Attributes.Name.SIGNATURE_VERSION);
       
   209 
       
   210         if ((version == null) || !(version.equalsIgnoreCase("1.0"))) {
       
   211             // XXX: should this be an exception?
       
   212             // for now we just ignore this signature file
       
   213             return;
       
   214         }
       
   215 
       
   216         SignerInfo[] infos = block.verify(sfBytes);
       
   217 
       
   218         if (infos == null) {
       
   219             throw new SecurityException("cannot verify signature block file " +
       
   220                                         name);
       
   221         }
       
   222 
       
   223         BASE64Decoder decoder = new BASE64Decoder();
       
   224 
       
   225         CodeSigner[] newSigners = getSigners(infos, block);
       
   226 
       
   227         // make sure we have something to do all this work for...
       
   228         if (newSigners == null)
       
   229             return;
       
   230 
       
   231         Iterator<Map.Entry<String,Attributes>> entries =
       
   232                                 sf.getEntries().entrySet().iterator();
       
   233 
       
   234         // see if we can verify the whole manifest first
       
   235         boolean manifestSigned = verifyManifestHash(sf, md, decoder);
       
   236 
       
   237         // verify manifest main attributes
       
   238         if (!manifestSigned && !verifyManifestMainAttrs(sf, md, decoder)) {
       
   239             throw new SecurityException
       
   240                 ("Invalid signature file digest for Manifest main attributes");
       
   241         }
       
   242 
       
   243         // go through each section in the signature file
       
   244         while(entries.hasNext()) {
       
   245 
       
   246             Map.Entry<String,Attributes> e = entries.next();
       
   247             String name = e.getKey();
       
   248 
       
   249             if (manifestSigned ||
       
   250                 (verifySection(e.getValue(), name, md, decoder))) {
       
   251 
       
   252                 if (name.startsWith("./"))
       
   253                     name = name.substring(2);
       
   254 
       
   255                 if (name.startsWith("/"))
       
   256                     name = name.substring(1);
       
   257 
       
   258                 updateSigners(newSigners, signers, name);
       
   259 
       
   260                 if (debug != null) {
       
   261                     debug.println("processSignature signed name = "+name);
       
   262                 }
       
   263 
       
   264             } else if (debug != null) {
       
   265                 debug.println("processSignature unsigned name = "+name);
       
   266             }
       
   267         }
       
   268     }
       
   269 
       
   270     /**
       
   271      * See if the whole manifest was signed.
       
   272      */
       
   273     private boolean verifyManifestHash(Manifest sf,
       
   274                                        ManifestDigester md,
       
   275                                        BASE64Decoder decoder)
       
   276          throws IOException
       
   277     {
       
   278         Attributes mattr = sf.getMainAttributes();
       
   279         boolean manifestSigned = false;
       
   280 
       
   281         // go through all the attributes and process *-Digest-Manifest entries
       
   282         for (Map.Entry<Object,Object> se : mattr.entrySet()) {
       
   283 
       
   284             String key = se.getKey().toString();
       
   285 
       
   286             if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) {
       
   287                 // 16 is length of "-Digest-Manifest"
       
   288                 String algorithm = key.substring(0, key.length()-16);
       
   289 
       
   290                 MessageDigest digest = getDigest(algorithm);
       
   291                 if (digest != null) {
       
   292                     byte[] computedHash = md.manifestDigest(digest);
       
   293                     byte[] expectedHash =
       
   294                         decoder.decodeBuffer((String)se.getValue());
       
   295 
       
   296                     if (debug != null) {
       
   297                      debug.println("Signature File: Manifest digest " +
       
   298                                           digest.getAlgorithm());
       
   299                      debug.println( "  sigfile  " + toHex(expectedHash));
       
   300                      debug.println( "  computed " + toHex(computedHash));
       
   301                      debug.println();
       
   302                     }
       
   303 
       
   304                     if (MessageDigest.isEqual(computedHash,
       
   305                                               expectedHash)) {
       
   306                         manifestSigned = true;
       
   307                     } else {
       
   308                         //XXX: we will continue and verify each section
       
   309                     }
       
   310                 }
       
   311             }
       
   312         }
       
   313         return manifestSigned;
       
   314     }
       
   315 
       
   316     private boolean verifyManifestMainAttrs(Manifest sf,
       
   317                                         ManifestDigester md,
       
   318                                         BASE64Decoder decoder)
       
   319          throws IOException
       
   320     {
       
   321         Attributes mattr = sf.getMainAttributes();
       
   322         boolean attrsVerified = true;
       
   323 
       
   324         // go through all the attributes and process
       
   325         // digest entries for the manifest main attributes
       
   326         for (Map.Entry<Object,Object> se : mattr.entrySet()) {
       
   327             String key = se.getKey().toString();
       
   328 
       
   329             if (key.toUpperCase(Locale.ENGLISH).endsWith(ATTR_DIGEST)) {
       
   330                 String algorithm =
       
   331                         key.substring(0, key.length() - ATTR_DIGEST.length());
       
   332 
       
   333                 MessageDigest digest = getDigest(algorithm);
       
   334                 if (digest != null) {
       
   335                     ManifestDigester.Entry mde =
       
   336                         md.get(ManifestDigester.MF_MAIN_ATTRS, false);
       
   337                     byte[] computedHash = mde.digest(digest);
       
   338                     byte[] expectedHash =
       
   339                         decoder.decodeBuffer((String)se.getValue());
       
   340 
       
   341                     if (debug != null) {
       
   342                      debug.println("Signature File: " +
       
   343                                         "Manifest Main Attributes digest " +
       
   344                                         digest.getAlgorithm());
       
   345                      debug.println( "  sigfile  " + toHex(expectedHash));
       
   346                      debug.println( "  computed " + toHex(computedHash));
       
   347                      debug.println();
       
   348                     }
       
   349 
       
   350                     if (MessageDigest.isEqual(computedHash,
       
   351                                               expectedHash)) {
       
   352                         // good
       
   353                     } else {
       
   354                         // we will *not* continue and verify each section
       
   355                         attrsVerified = false;
       
   356                         if (debug != null) {
       
   357                             debug.println("Verification of " +
       
   358                                         "Manifest main attributes failed");
       
   359                             debug.println();
       
   360                         }
       
   361                         break;
       
   362                     }
       
   363                 }
       
   364             }
       
   365         }
       
   366 
       
   367         // this method returns 'true' if either:
       
   368         //      . manifest main attributes were not signed, or
       
   369         //      . manifest main attributes were signed and verified
       
   370         return attrsVerified;
       
   371     }
       
   372 
       
   373     /**
       
   374      * given the .SF digest header, and the data from the
       
   375      * section in the manifest, see if the hashes match.
       
   376      * if not, throw a SecurityException.
       
   377      *
       
   378      * @return true if all the -Digest headers verified
       
   379      * @exception SecurityException if the hash was not equal
       
   380      */
       
   381 
       
   382     private boolean verifySection(Attributes sfAttr,
       
   383                                   String name,
       
   384                                   ManifestDigester md,
       
   385                                   BASE64Decoder decoder)
       
   386          throws IOException
       
   387     {
       
   388         boolean oneDigestVerified = false;
       
   389         ManifestDigester.Entry mde = md.get(name,block.isOldStyle());
       
   390 
       
   391         if (mde == null) {
       
   392             throw new SecurityException(
       
   393                   "no manifiest section for signature file entry "+name);
       
   394         }
       
   395 
       
   396         if (sfAttr != null) {
       
   397 
       
   398             //sun.misc.HexDumpEncoder hex = new sun.misc.HexDumpEncoder();
       
   399             //hex.encodeBuffer(data, System.out);
       
   400 
       
   401             // go through all the attributes and process *-Digest entries
       
   402             for (Map.Entry<Object,Object> se : sfAttr.entrySet()) {
       
   403                 String key = se.getKey().toString();
       
   404 
       
   405                 if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
       
   406                     // 7 is length of "-Digest"
       
   407                     String algorithm = key.substring(0, key.length()-7);
       
   408 
       
   409                     MessageDigest digest = getDigest(algorithm);
       
   410 
       
   411                     if (digest != null) {
       
   412                         boolean ok = false;
       
   413 
       
   414                         byte[] expected =
       
   415                             decoder.decodeBuffer((String)se.getValue());
       
   416                         byte[] computed;
       
   417                         if (workaround) {
       
   418                             computed = mde.digestWorkaround(digest);
       
   419                         } else {
       
   420                             computed = mde.digest(digest);
       
   421                         }
       
   422 
       
   423                         if (debug != null) {
       
   424                           debug.println("Signature Block File: " +
       
   425                                    name + " digest=" + digest.getAlgorithm());
       
   426                           debug.println("  expected " + toHex(expected));
       
   427                           debug.println("  computed " + toHex(computed));
       
   428                           debug.println();
       
   429                         }
       
   430 
       
   431                         if (MessageDigest.isEqual(computed, expected)) {
       
   432                             oneDigestVerified = true;
       
   433                             ok = true;
       
   434                         } else {
       
   435                             // attempt to fallback to the workaround
       
   436                             if (!workaround) {
       
   437                                computed = mde.digestWorkaround(digest);
       
   438                                if (MessageDigest.isEqual(computed, expected)) {
       
   439                                    if (debug != null) {
       
   440                                        debug.println("  re-computed " + toHex(computed));
       
   441                                        debug.println();
       
   442                                    }
       
   443                                    workaround = true;
       
   444                                    oneDigestVerified = true;
       
   445                                    ok = true;
       
   446                                }
       
   447                             }
       
   448                         }
       
   449                         if (!ok){
       
   450                             throw new SecurityException("invalid " +
       
   451                                        digest.getAlgorithm() +
       
   452                                        " signature file digest for " + name);
       
   453                         }
       
   454                     }
       
   455                 }
       
   456             }
       
   457         }
       
   458         return oneDigestVerified;
       
   459     }
       
   460 
       
   461     /**
       
   462      * Given the PKCS7 block and SignerInfo[], create an array of
       
   463      * CodeSigner objects. We do this only *once* for a given
       
   464      * signature block file.
       
   465      */
       
   466     private CodeSigner[] getSigners(SignerInfo infos[], PKCS7 block)
       
   467         throws IOException, NoSuchAlgorithmException, SignatureException,
       
   468             CertificateException {
       
   469 
       
   470         ArrayList<CodeSigner> signers = null;
       
   471 
       
   472         for (int i = 0; i < infos.length; i++) {
       
   473 
       
   474             SignerInfo info = infos[i];
       
   475             ArrayList<X509Certificate> chain = info.getCertificateChain(block);
       
   476             CertPath certChain = certificateFactory.generateCertPath(chain);
       
   477             if (signers == null) {
       
   478                 signers = new ArrayList<CodeSigner>();
       
   479             }
       
   480             // Append the new code signer
       
   481             signers.add(new CodeSigner(certChain, getTimestamp(info)));
       
   482 
       
   483             if (debug != null) {
       
   484                 debug.println("Signature Block Certificate: " +
       
   485                     chain.get(0));
       
   486             }
       
   487         }
       
   488 
       
   489         if (signers != null) {
       
   490             return signers.toArray(new CodeSigner[signers.size()]);
       
   491         } else {
       
   492             return null;
       
   493         }
       
   494     }
       
   495 
       
   496     /*
       
   497      * Examines a signature timestamp token to generate a timestamp object.
       
   498      *
       
   499      * Examines the signer's unsigned attributes for a
       
   500      * <tt>signatureTimestampToken</tt> attribute. If present,
       
   501      * then it is parsed to extract the date and time at which the
       
   502      * timestamp was generated.
       
   503      *
       
   504      * @param info A signer information element of a PKCS 7 block.
       
   505      *
       
   506      * @return A timestamp token or null if none is present.
       
   507      * @throws IOException if an error is encountered while parsing the
       
   508      *         PKCS7 data.
       
   509      * @throws NoSuchAlgorithmException if an error is encountered while
       
   510      *         verifying the PKCS7 object.
       
   511      * @throws SignatureException if an error is encountered while
       
   512      *         verifying the PKCS7 object.
       
   513      * @throws CertificateException if an error is encountered while generating
       
   514      *         the TSA's certpath.
       
   515      */
       
   516     private Timestamp getTimestamp(SignerInfo info)
       
   517         throws IOException, NoSuchAlgorithmException, SignatureException,
       
   518             CertificateException {
       
   519 
       
   520         Timestamp timestamp = null;
       
   521 
       
   522         // Extract the signer's unsigned attributes
       
   523         PKCS9Attributes unsignedAttrs = info.getUnauthenticatedAttributes();
       
   524         if (unsignedAttrs != null) {
       
   525             PKCS9Attribute timestampTokenAttr =
       
   526                 unsignedAttrs.getAttribute("signatureTimestampToken");
       
   527             if (timestampTokenAttr != null) {
       
   528                 PKCS7 timestampToken =
       
   529                     new PKCS7((byte[])timestampTokenAttr.getValue());
       
   530                 // Extract the content (an encoded timestamp token info)
       
   531                 byte[] encodedTimestampTokenInfo =
       
   532                     timestampToken.getContentInfo().getData();
       
   533                 // Extract the signer (the Timestamping Authority)
       
   534                 // while verifying the content
       
   535                 SignerInfo[] tsa =
       
   536                     timestampToken.verify(encodedTimestampTokenInfo);
       
   537                 // Expect only one signer
       
   538                 ArrayList<X509Certificate> chain =
       
   539                                 tsa[0].getCertificateChain(timestampToken);
       
   540                 CertPath tsaChain = certificateFactory.generateCertPath(chain);
       
   541                 // Create a timestamp token info object
       
   542                 TimestampToken timestampTokenInfo =
       
   543                     new TimestampToken(encodedTimestampTokenInfo);
       
   544                 // Create a timestamp object
       
   545                 timestamp =
       
   546                     new Timestamp(timestampTokenInfo.getDate(), tsaChain);
       
   547             }
       
   548         }
       
   549         return timestamp;
       
   550     }
       
   551 
       
   552     // for the toHex function
       
   553     private static final char[] hexc =
       
   554             {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
       
   555     /**
       
   556      * convert a byte array to a hex string for debugging purposes
       
   557      * @param data the binary data to be converted to a hex string
       
   558      * @return an ASCII hex string
       
   559      */
       
   560 
       
   561     static String toHex(byte[] data) {
       
   562 
       
   563         StringBuffer sb = new StringBuffer(data.length*2);
       
   564 
       
   565         for (int i=0; i<data.length; i++) {
       
   566             sb.append(hexc[(data[i] >>4) & 0x0f]);
       
   567             sb.append(hexc[data[i] & 0x0f]);
       
   568         }
       
   569         return sb.toString();
       
   570     }
       
   571 
       
   572     // returns true if set contains signer
       
   573     static boolean contains(CodeSigner[] set, CodeSigner signer)
       
   574     {
       
   575         for (int i = 0; i < set.length; i++) {
       
   576             if (set[i].equals(signer))
       
   577                 return true;
       
   578         }
       
   579         return false;
       
   580     }
       
   581 
       
   582     // returns true if subset is a subset of set
       
   583     static boolean isSubSet(CodeSigner[] subset, CodeSigner[] set)
       
   584     {
       
   585         // check for the same object
       
   586         if (set == subset)
       
   587             return true;
       
   588 
       
   589         boolean match;
       
   590         for (int i = 0; i < subset.length; i++) {
       
   591             if (!contains(set, subset[i]))
       
   592                 return false;
       
   593         }
       
   594         return true;
       
   595     }
       
   596 
       
   597     /**
       
   598      * returns true if signer contains exactly the same code signers as
       
   599      * oldSigner and newSigner, false otherwise. oldSigner
       
   600      * is allowed to be null.
       
   601      */
       
   602     static boolean matches(CodeSigner[] signers, CodeSigner[] oldSigners,
       
   603         CodeSigner[] newSigners) {
       
   604 
       
   605         // special case
       
   606         if ((oldSigners == null) && (signers == newSigners))
       
   607             return true;
       
   608 
       
   609         boolean match;
       
   610 
       
   611         // make sure all oldSigners are in signers
       
   612         if ((oldSigners != null) && !isSubSet(oldSigners, signers))
       
   613             return false;
       
   614 
       
   615         // make sure all newSigners are in signers
       
   616         if (!isSubSet(newSigners, signers)) {
       
   617             return false;
       
   618         }
       
   619 
       
   620         // now make sure all the code signers in signers are
       
   621         // also in oldSigners or newSigners
       
   622 
       
   623         for (int i = 0; i < signers.length; i++) {
       
   624             boolean found =
       
   625                 ((oldSigners != null) && contains(oldSigners, signers[i])) ||
       
   626                 contains(newSigners, signers[i]);
       
   627             if (!found)
       
   628                 return false;
       
   629         }
       
   630         return true;
       
   631     }
       
   632 
       
   633     void updateSigners(CodeSigner[] newSigners,
       
   634         Hashtable<String, CodeSigner[]> signers, String name) {
       
   635 
       
   636         CodeSigner[] oldSigners = signers.get(name);
       
   637 
       
   638         // search through the cache for a match, go in reverse order
       
   639         // as we are more likely to find a match with the last one
       
   640         // added to the cache
       
   641 
       
   642         CodeSigner[] cachedSigners;
       
   643         for (int i = signerCache.size() - 1; i != -1; i--) {
       
   644             cachedSigners = signerCache.get(i);
       
   645             if (matches(cachedSigners, oldSigners, newSigners)) {
       
   646                 signers.put(name, cachedSigners);
       
   647                 return;
       
   648             }
       
   649         }
       
   650 
       
   651         if (oldSigners == null) {
       
   652             cachedSigners = newSigners;
       
   653         } else {
       
   654             cachedSigners =
       
   655                 new CodeSigner[oldSigners.length + newSigners.length];
       
   656             System.arraycopy(oldSigners, 0, cachedSigners, 0,
       
   657                 oldSigners.length);
       
   658             System.arraycopy(newSigners, 0, cachedSigners, oldSigners.length,
       
   659                 newSigners.length);
       
   660         }
       
   661         signerCache.add(cachedSigners);
       
   662         signers.put(name, cachedSigners);
       
   663     }
       
   664 }