OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [tools/] [gnu/] [classpath/] [tools/] [jarsigner/] [JarVerifier.java] - Blame information for rev 779

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 779 jeremybenn
/* JarVerifier.java -- The verification handler of the gjarsigner tool
2
   Copyright (C) 2006, 2007 Free Software Foundation, Inc.
3
 
4
This file is part of GNU Classpath.
5
 
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
9
any later version.
10
 
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
General Public License for more details.
15
 
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING.  If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
02110-1301 USA.
20
 
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library.  Thus, the terms and
23
conditions of the GNU General Public License cover the whole
24
combination.
25
 
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module.  An independent module is a module which is not derived from
33
or based on this library.  If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so.  If you do not wish to do so, delete this
36
exception statement from your version. */
37
 
38
 
39
package gnu.classpath.tools.jarsigner;
40
 
41
import gnu.classpath.Configuration;
42
import gnu.java.security.OID;
43
import gnu.java.security.Registry;
44
import gnu.java.security.pkcs.PKCS7SignedData;
45
import gnu.java.security.pkcs.SignerInfo;
46
import gnu.java.security.sig.ISignature;
47
import gnu.java.security.sig.ISignatureCodec;
48
import gnu.java.security.sig.dss.DSSSignature;
49
import gnu.java.security.sig.dss.DSSSignatureX509Codec;
50
import gnu.java.security.sig.rsa.RSAPKCS1V1_5Signature;
51
import gnu.java.security.sig.rsa.RSAPKCS1V1_5SignatureX509Codec;
52
import gnu.java.security.util.Util;
53
import gnu.java.util.jar.JarUtils;
54
 
55
import java.io.IOException;
56
import java.io.InputStream;
57
import java.security.PublicKey;
58
import java.security.cert.CRLException;
59
import java.security.cert.Certificate;
60
import java.security.cert.CertificateException;
61
import java.util.ArrayList;
62
import java.util.Enumeration;
63
import java.util.HashMap;
64
import java.util.List;
65
import java.util.Map;
66
import java.util.Set;
67
import java.util.Map.Entry;
68
import java.util.jar.Attributes;
69
import java.util.jar.JarEntry;
70
import java.util.jar.JarFile;
71
import java.util.logging.Logger;
72
import java.util.zip.ZipException;
73
 
74
/**
75
 * The JAR verification handler of the <code>gjarsigner</code> tool.
76
 */
77
public class JarVerifier
78
{
79
  private static final Logger log = Logger.getLogger(JarVerifier.class.getName());
80
  /** The owner tool of this handler. */
81
  private Main main;
82
  private HashUtils util = new HashUtils();
83
  /** The JAR file to verify. */
84
  private JarFile jarFile;
85
  /** Map of jar entry names to their hash. */
86
  private Map<String, String> entryHashes = new HashMap<String, String>();
87
 
88
  JarVerifier(Main main)
89
  {
90
    super();
91
 
92
    this.main = main;
93
  }
94
 
95
  void start() throws Exception
96
  {
97
    if (Configuration.DEBUG)
98
      log.entering(this.getClass().getName(), "start"); //$NON-NLS-1$
99
    String jarFileName = main.getJarFileName();
100
    jarFile = new JarFile(jarFileName);
101
 
102
    // 1. find all signature (.SF) files
103
    List<String> sfFiles = new ArrayList<String>();
104
    for (Enumeration e = jarFile.entries(); e.hasMoreElements(); )
105
      {
106
        JarEntry je = (JarEntry) e.nextElement();
107
        String jeName = je.getName();
108
        if (! (jeName.startsWith(JarUtils.META_INF)
109
            && jeName.endsWith(JarUtils.SF_SUFFIX)))
110
          continue;
111
 
112
        // only interested in .SF files in, and not deeper than, META-INF
113
        String[] jeNameParts = jeName.split("/"); //$NON-NLS-1$
114
        if (jeNameParts.length != 2)
115
          continue;
116
 
117
        String sfName = jeNameParts[1];
118
        String sigFileName = sfName.substring(0, sfName.length() - 3);
119
        sfFiles.add(sigFileName);
120
      }
121
 
122
    // 2. verify each one
123
    if (sfFiles.isEmpty())
124
      System.out.println(Messages.getString("JarVerifier.2")); //$NON-NLS-1$
125
    else
126
      {
127
        int limit = sfFiles.size();
128
        int count = 0;
129
        for (String alias : sfFiles)
130
          if (verifySF(alias))
131
            if (verifySFEntries(alias))
132
              count++;
133
 
134
        if (count == 0)
135
          System.out.println(Messages.getString("JarVerifier.3")); //$NON-NLS-1$
136
        else if (count != limit)
137
          System.out.println(Messages.getFormattedString("JarVerifier.4", //$NON-NLS-1$
138
                                                         new Integer[] {Integer.valueOf(count),
139
                                                                        Integer.valueOf(limit)}));
140
        else
141
          System.out.println(Messages.getFormattedString("JarVerifier.7", //$NON-NLS-1$
142
                                                         Integer.valueOf(limit)));
143
      }
144
    if (Configuration.DEBUG)
145
      log.exiting(this.getClass().getName(), "start"); //$NON-NLS-1$
146
  }
147
 
148
  /**
149
   * @param sigFileName the name of the signature file; i.e. the name to use for
150
   *          both the .SF and .DSA files.
151
   * @return <code>true</code> if the designated file-name (usually a key-store
152
   * <i>alias</i> name) has been successfully checked as the signer of the
153
   * corresponding <code>.SF</code> file. Returns <code>false</code> otherwise.
154
   * @throws IOException
155
   * @throws ZipException
156
   * @throws CertificateException
157
   * @throws CRLException
158
   */
159
  private boolean verifySF(String sigFileName) throws CRLException,
160
      CertificateException, ZipException, IOException
161
  {
162
    if (Configuration.DEBUG)
163
      {
164
        log.entering(this.getClass().getName(), "verifySF"); //$NON-NLS-1$
165
        log.fine("About to verify signature of " + sigFileName + "..."); //$NON-NLS-1$ //$NON-NLS-2$
166
      }
167
    // 1. find the corresponding .DSA file for this .SF file
168
    JarEntry dsaEntry = jarFile.getJarEntry(JarUtils.META_INF + sigFileName
169
                                            + JarUtils.DSA_SUFFIX);
170
    if (dsaEntry == null)
171
      throw new SecurityException(Messages.getFormattedString("JarVerifier.13", //$NON-NLS-1$
172
                                                              sigFileName));
173
    // 2. read the .DSA file contents as a PKCS7 SignedData
174
    InputStream in = jarFile.getInputStream(dsaEntry);
175
    PKCS7SignedData pkcs7SignedData = new PKCS7SignedData(in);
176
 
177
    // 4. get the encrypted digest octet string from the first SignerInfo
178
    //    this octet string is the digital signature of the .SF file contents
179
    Set signerInfos = pkcs7SignedData.getSignerInfos();
180
    if (signerInfos == null || signerInfos.isEmpty())
181
      throw new SecurityException(Messages.getString("JarVerifier.14")); //$NON-NLS-1$
182
 
183
    SignerInfo signerInfo = (SignerInfo) signerInfos.iterator().next();
184
    byte[] encryptedDigest = signerInfo.getEncryptedDigest();
185
    if (encryptedDigest == null)
186
      throw new SecurityException(Messages.getString("JarVerifier.16")); //$NON-NLS-1$
187
 
188
    if (Configuration.DEBUG)
189
      log.fine("\n" + Util.dumpString(encryptedDigest, "--- signedSFBytes ")); //$NON-NLS-1$ //$NON-NLS-2$
190
 
191
    // 5. get the signer public key
192
    Certificate cert = pkcs7SignedData.getCertificates()[0];
193
    PublicKey verifierKey = cert.getPublicKey();
194
    if (Configuration.DEBUG)
195
      log.fine("--- verifier public key = " + verifierKey); //$NON-NLS-1$
196
 
197
    // 6. verify the signature file signature
198
    OID digestEncryptionAlgorithmOID = signerInfo.getDigestEncryptionAlgorithmId();
199
    ISignature signatureAlgorithm;
200
    ISignatureCodec signatureCodec;
201
    if (digestEncryptionAlgorithmOID.equals(Main.DSA_SIGNATURE_OID))
202
      {
203
        signatureAlgorithm = new DSSSignature();
204
        signatureCodec = new DSSSignatureX509Codec();
205
      }
206
    else
207
      {
208
        signatureAlgorithm = new RSAPKCS1V1_5Signature(Registry.MD5_HASH);
209
        signatureCodec = new RSAPKCS1V1_5SignatureX509Codec();
210
      }
211
 
212
    Map signatureAttributes = new HashMap();
213
    signatureAttributes.put(ISignature.VERIFIER_KEY, verifierKey);
214
    signatureAlgorithm.setupVerify(signatureAttributes);
215
 
216
    Object herSignature = signatureCodec.decodeSignature(encryptedDigest);
217
 
218
    // 7. verify the signature file contents
219
    JarEntry sfEntry = jarFile.getJarEntry(JarUtils.META_INF + sigFileName
220
                                           + JarUtils.SF_SUFFIX);
221
    in = jarFile.getInputStream(sfEntry);
222
    byte[] buffer = new byte[2048];
223
    int n;
224
    while ((n = in.read(buffer)) != -1)
225
      if (n > 0)
226
        signatureAlgorithm.update(buffer, 0, n);
227
 
228
    boolean result = signatureAlgorithm.verify(herSignature);
229
    if (Configuration.DEBUG)
230
      {
231
        log.fine("Signature block [" + sigFileName + "] is " //$NON-NLS-1$ //$NON-NLS-2$
232
                 + (result ? "" : "NOT ") + "OK"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
233
        log.exiting(this.getClass().getName(), "verifySF", Boolean.valueOf(result)); //$NON-NLS-1$
234
      }
235
    return result;
236
  }
237
 
238
  /**
239
   * This method is called after at least one signer (usually a key-store
240
   * <code>alias</code> name) was found to be trusted; i.e. his/her signature
241
   * block in the corresponding <code>.DSA</code> file was successfully
242
   * verified using his/her public key.
243
   * <p>
244
   * This method, uses the contents of the corresponding <code>.SF</code> file
245
   * to compute and verify the hashes of the manifest entries in the JAR file.
246
   *
247
   * @param alias the name of the signature file; i.e. the name to use for both
248
   *          the .SF and .DSA files.
249
   * @return <code>true</code> if all the entries in the corresponding
250
   *         <code>.SF</code> file have the same hash values as their
251
   *         alter-ego in the <i>manifest</i> file of the JAR file inquestion.
252
   * @throws IOException if an I/O related exception occurs during the process.
253
   */
254
  private boolean verifySFEntries(String alias) throws IOException
255
  {
256
    if (Configuration.DEBUG)
257
      log.entering(this.getClass().getName(), "verifySFEntries"); //$NON-NLS-1$
258
    // 1. read the signature file
259
    JarEntry jarEntry = jarFile.getJarEntry(JarUtils.META_INF + alias
260
                                            + JarUtils.SF_SUFFIX);
261
    InputStream in = jarFile.getInputStream(jarEntry);
262
    Attributes attr = new Attributes();
263
    Map<String, Attributes> entries = new HashMap<String, Attributes>();
264
    JarUtils.readSFManifest(attr, entries, in);
265
 
266
    // 2. The .SF file by default includes a header containing a hash of the
267
    // entire manifest file. When the header is present, then the verification
268
    // can check to see whether or not the hash in the header indeed matches
269
    // the hash of the manifest file.
270
    boolean result = false;
271
    String hash = attr.getValue(Main.DIGEST_MANIFEST_ATTR);
272
    if (hash != null)
273
      result = verifyManifest(hash);
274
 
275
    // A verification is still considered successful if none of the files that
276
    // were in the JAR file when the signature was generated have been changed
277
    // since then, which is the case if the hashes in the non-header sections
278
    // of the .SF file equal the hashes of the corresponding sections in the
279
    // manifest file.
280
    //
281
    // 3. Read each file in the JAR file that has an entry in the .SF file.
282
    // While reading, compute the file's digest, and then compare the result
283
    // with the digest for this file in the manifest section. The digests
284
    // should be the same, or verification fails.
285
    if (! result)
286
      for (Entry<String, Attributes> me : entries.entrySet())
287
        {
288
          String name = me.getKey();
289
          attr = me.getValue();
290
          hash = attr.getValue(Main.DIGEST_ATTR);
291
          result = verifySFEntry(name, hash);
292
          if (! result)
293
            break;
294
        }
295
 
296
    if (Configuration.DEBUG)
297
      log.exiting(this.getClass().getName(), "verifySFEntries", //$NON-NLS-1$
298
                  Boolean.valueOf(result));
299
    return result;
300
  }
301
 
302
  /**
303
   * @param hash Base-64 encoded form of the manifest's digest.
304
   * @return <code>true</code> if our computation of the manifest's hash
305
   *         matches the given value; <code>false</code> otherwise.
306
   * @throws IOException if unable to acquire the JAR's manifest entry.
307
   */
308
  private boolean verifyManifest(String hash) throws IOException
309
  {
310
    return verifySFEntry(JarFile.MANIFEST_NAME, hash);
311
  }
312
 
313
  /**
314
   * @param name the name of a JAR entry to verify.
315
   * @param hash Base-64 encoded form of the designated entry's digest.
316
   * @return <code>true</code> if our computation of the JAR entry's hash
317
   *         matches the given value; <code>false</code> otherwise.
318
   * @throws IOException if an exception occurs while returning the entry's
319
   *           input stream.
320
   */
321
  private boolean verifySFEntry(String name, String hash) throws IOException
322
  {
323
    String expectedValue = getEntryHash(JarFile.MANIFEST_NAME);
324
    boolean result = expectedValue.equalsIgnoreCase(hash);
325
    if (Configuration.DEBUG)
326
      log.fine("Is " + name + " OK? " + result); //$NON-NLS-1$ //$NON-NLS-2$
327
    return result;
328
  }
329
 
330
  private String getEntryHash(String entryName) throws IOException
331
  {
332
    String result = (String) entryHashes.get(entryName);
333
    if (result == null)
334
      {
335
        JarEntry manifest = jarFile.getJarEntry(entryName);
336
        InputStream in = jarFile.getInputStream(manifest);
337
        result = util.hashStream(in);
338
        entryHashes.put(entryName, result);
339
      }
340
 
341
    return result;
342
  }
343
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.