jdk/src/java.base/share/classes/sun/security/util/ManifestEntryVerifier.java
changeset 25859 3317bb8137f4
parent 24969 afa6934dd8e8
child 30033 b9c86c17164a
equal deleted inserted replaced
25858:836adbf7a2cd 25859:3317bb8137f4
       
     1 /*
       
     2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package sun.security.util;
       
    27 
       
    28 import java.security.*;
       
    29 import java.io.*;
       
    30 import java.security.CodeSigner;
       
    31 import java.util.*;
       
    32 import java.util.jar.*;
       
    33 
       
    34 import java.util.Base64;
       
    35 
       
    36 import sun.security.jca.Providers;
       
    37 
       
    38 /**
       
    39  * This class is used to verify each entry in a jar file with its
       
    40  * manifest value.
       
    41  */
       
    42 
       
    43 public class ManifestEntryVerifier {
       
    44 
       
    45     private static final Debug debug = Debug.getInstance("jar");
       
    46 
       
    47     /**
       
    48      * Holder class to lazily load Sun provider. NOTE: if
       
    49      * Providers.getSunProvider returned a cached provider, we could avoid the
       
    50      * need for caching the provider with this holder class; we should try to
       
    51      * revisit this in JDK 8.
       
    52      */
       
    53     private static class SunProviderHolder {
       
    54         private static final Provider instance = Providers.getSunProvider();
       
    55     }
       
    56 
       
    57     /** the created digest objects */
       
    58     HashMap<String, MessageDigest> createdDigests;
       
    59 
       
    60     /** the digests in use for a given entry*/
       
    61     ArrayList<MessageDigest> digests;
       
    62 
       
    63     /** the manifest hashes for the digests in use */
       
    64     ArrayList<byte[]> manifestHashes;
       
    65 
       
    66     private String name = null;
       
    67     private Manifest man;
       
    68 
       
    69     private boolean skip = true;
       
    70 
       
    71     private JarEntry entry;
       
    72 
       
    73     private CodeSigner[] signers = null;
       
    74 
       
    75     /**
       
    76      * Create a new ManifestEntryVerifier object.
       
    77      */
       
    78     public ManifestEntryVerifier(Manifest man)
       
    79     {
       
    80         createdDigests = new HashMap<String, MessageDigest>(11);
       
    81         digests = new ArrayList<MessageDigest>();
       
    82         manifestHashes = new ArrayList<byte[]>();
       
    83         this.man = man;
       
    84     }
       
    85 
       
    86     /**
       
    87      * Find the hashes in the
       
    88      * manifest for this entry, save them, and set the MessageDigest
       
    89      * objects to calculate the hashes on the fly. If name is
       
    90      * null it signifies that update/verify should ignore this entry.
       
    91      */
       
    92     public void setEntry(String name, JarEntry entry)
       
    93         throws IOException
       
    94     {
       
    95         digests.clear();
       
    96         manifestHashes.clear();
       
    97         this.name = name;
       
    98         this.entry = entry;
       
    99 
       
   100         skip = true;
       
   101         signers = null;
       
   102 
       
   103         if (man == null || name == null) {
       
   104             return;
       
   105         }
       
   106 
       
   107         /* get the headers from the manifest for this entry */
       
   108         /* if there aren't any, we can't verify any digests for this entry */
       
   109 
       
   110         Attributes attr = man.getAttributes(name);
       
   111         if (attr == null) {
       
   112             // ugh. we should be able to remove this at some point.
       
   113             // there are broken jars floating around with ./name and /name
       
   114             // in the manifest, and "name" in the zip/jar file.
       
   115             attr = man.getAttributes("./"+name);
       
   116             if (attr == null) {
       
   117                 attr = man.getAttributes("/"+name);
       
   118                 if (attr == null)
       
   119                     return;
       
   120             }
       
   121         }
       
   122 
       
   123         for (Map.Entry<Object,Object> se : attr.entrySet()) {
       
   124             String key = se.getKey().toString();
       
   125 
       
   126             if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
       
   127                 // 7 is length of "-Digest"
       
   128                 String algorithm = key.substring(0, key.length()-7);
       
   129 
       
   130                 MessageDigest digest = createdDigests.get(algorithm);
       
   131 
       
   132                 if (digest == null) {
       
   133                     try {
       
   134 
       
   135                         digest = MessageDigest.getInstance
       
   136                                         (algorithm, SunProviderHolder.instance);
       
   137                         createdDigests.put(algorithm, digest);
       
   138                     } catch (NoSuchAlgorithmException nsae) {
       
   139                         // ignore
       
   140                     }
       
   141                 }
       
   142 
       
   143                 if (digest != null) {
       
   144                     skip = false;
       
   145                     digest.reset();
       
   146                     digests.add(digest);
       
   147                     manifestHashes.add(
       
   148                                 Base64.getMimeDecoder().decode((String)se.getValue()));
       
   149                 }
       
   150             }
       
   151         }
       
   152     }
       
   153 
       
   154     /**
       
   155      * update the digests for the digests we are interested in
       
   156      */
       
   157     public void update(byte buffer) {
       
   158         if (skip) return;
       
   159 
       
   160         for (int i=0; i < digests.size(); i++) {
       
   161             digests.get(i).update(buffer);
       
   162         }
       
   163     }
       
   164 
       
   165     /**
       
   166      * update the digests for the digests we are interested in
       
   167      */
       
   168     public void update(byte buffer[], int off, int len) {
       
   169         if (skip) return;
       
   170 
       
   171         for (int i=0; i < digests.size(); i++) {
       
   172             digests.get(i).update(buffer, off, len);
       
   173         }
       
   174     }
       
   175 
       
   176     /**
       
   177      * get the JarEntry for this object
       
   178      */
       
   179     public JarEntry getEntry()
       
   180     {
       
   181         return entry;
       
   182     }
       
   183 
       
   184     /**
       
   185      * go through all the digests, calculating the final digest
       
   186      * and comparing it to the one in the manifest. If this is
       
   187      * the first time we have verified this object, remove its
       
   188      * code signers from sigFileSigners and place in verifiedSigners.
       
   189      *
       
   190      *
       
   191      */
       
   192     public CodeSigner[] verify(Hashtable<String, CodeSigner[]> verifiedSigners,
       
   193                 Hashtable<String, CodeSigner[]> sigFileSigners)
       
   194         throws JarException
       
   195     {
       
   196         if (skip) {
       
   197             return null;
       
   198         }
       
   199 
       
   200         if (signers != null)
       
   201             return signers;
       
   202 
       
   203         for (int i=0; i < digests.size(); i++) {
       
   204 
       
   205             MessageDigest digest  = digests.get(i);
       
   206             byte [] manHash = manifestHashes.get(i);
       
   207             byte [] theHash = digest.digest();
       
   208 
       
   209             if (debug != null) {
       
   210                 debug.println("Manifest Entry: " +
       
   211                                    name + " digest=" + digest.getAlgorithm());
       
   212                 debug.println("  manifest " + toHex(manHash));
       
   213                 debug.println("  computed " + toHex(theHash));
       
   214                 debug.println();
       
   215             }
       
   216 
       
   217             if (!MessageDigest.isEqual(theHash, manHash))
       
   218                 throw new SecurityException(digest.getAlgorithm()+
       
   219                                             " digest error for "+name);
       
   220         }
       
   221 
       
   222         // take it out of sigFileSigners and put it in verifiedSigners...
       
   223         signers = sigFileSigners.remove(name);
       
   224         if (signers != null) {
       
   225             verifiedSigners.put(name, signers);
       
   226         }
       
   227         return signers;
       
   228     }
       
   229 
       
   230     // for the toHex function
       
   231     private static final char[] hexc =
       
   232             {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
       
   233     /**
       
   234      * convert a byte array to a hex string for debugging purposes
       
   235      * @param data the binary data to be converted to a hex string
       
   236      * @return an ASCII hex string
       
   237      */
       
   238 
       
   239     static String toHex(byte[] data) {
       
   240 
       
   241         StringBuilder sb = new StringBuilder(data.length*2);
       
   242 
       
   243         for (int i=0; i<data.length; i++) {
       
   244             sb.append(hexc[(data[i] >>4) & 0x0f]);
       
   245             sb.append(hexc[data[i] & 0x0f]);
       
   246         }
       
   247         return sb.toString();
       
   248     }
       
   249 
       
   250 }