KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > update > internal > security > JarVerifier


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.update.internal.security;
12
13 import java.io.File JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.security.KeyStore JavaDoc;
17 import java.security.KeyStoreException JavaDoc;
18 import java.security.NoSuchAlgorithmException JavaDoc;
19 import java.security.cert.Certificate JavaDoc;
20 import java.security.cert.CertificateException JavaDoc;
21 import java.util.ArrayList JavaDoc;
22 import java.util.Enumeration JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.jar.JarEntry JavaDoc;
26 import java.util.jar.JarFile JavaDoc;
27 import java.util.zip.ZipException JavaDoc;
28
29 import org.eclipse.core.runtime.CoreException;
30 import org.eclipse.core.runtime.IProgressMonitor;
31 import org.eclipse.osgi.util.NLS;
32 import org.eclipse.update.core.ContentReference;
33 import org.eclipse.update.core.IFeature;
34 import org.eclipse.update.core.IVerificationResult;
35 import org.eclipse.update.core.IVerifier;
36 import org.eclipse.update.core.InstallMonitor;
37 import org.eclipse.update.core.JarContentReference;
38 import org.eclipse.update.core.Utilities;
39 import org.eclipse.update.core.Verifier;
40 import org.eclipse.update.internal.core.Messages;
41 import org.eclipse.update.internal.core.UpdateCore;
42 import org.eclipse.update.internal.core.connection.ConnectionFactory;
43
44 /**
45  * The JarVerifier will check the integrity of the JAR.
46  * If the Jar is signed and the integrity is validated,
47  * it will check if one of the certificate of each file
48  * is in one of the keystore.
49  *
50  */

51
52 public class JarVerifier extends Verifier {
53
54     private static final String JavaDoc MANIFEST = "META-INF"; //$NON-NLS-1$
55

56     private JarVerificationResult result;
57     private List JavaDoc /*of CertificatePair*/
58     trustedCertificates;
59     private boolean acceptUnsignedFiles;
60     private List JavaDoc /* of KeyStore */
61     listOfKeystores;
62     private IProgressMonitor monitor;
63     private File JavaDoc jarFile;
64
65     private static byte[] buffer = new byte[8192];
66     
67     /*
68      * Default Constructor
69      */

70     public JarVerifier() {
71         initialize();
72     }
73
74     /*
75      * Returns the list of the keystores.
76      */

77     private List JavaDoc getKeyStores() throws CoreException {
78         if (listOfKeystores == null) {
79             listOfKeystores = new ArrayList JavaDoc(0);
80             KeyStores listOfKeystoreHandles = new KeyStores();
81             InputStream JavaDoc in = null;
82             KeyStore JavaDoc keystore = null;
83             KeystoreHandle handle = null;
84             while (listOfKeystoreHandles.hasNext()) {
85                 try {
86                     handle = listOfKeystoreHandles.next();
87                     in = ConnectionFactory.get(handle.getLocation()).getInputStream();
88                     try {
89                         keystore = KeyStore.getInstance(handle.getType());
90                         keystore.load(in, null); // no password
91
} catch (NoSuchAlgorithmException JavaDoc e) {
92                         throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToFindEncryption, (new String JavaDoc[] { handle.getLocation().toExternalForm() })), e);
93                     } catch (CertificateException JavaDoc e) {
94                         throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToLoadCertificate, (new String JavaDoc[] { handle.getLocation().toExternalForm() })), e);
95                     } catch (KeyStoreException JavaDoc e) {
96                         throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToFindProviderForKeystore, (new String JavaDoc[] { handle.getType() })), e);
97                     } finally {
98                         if (in != null) {
99                             try {
100                                 in.close();
101                             } catch (IOException JavaDoc e) {
102                             } // nothing
103
}
104                     } // try loading a keyStore
105

106                     // keystore was loaded
107
listOfKeystores.add(keystore);
108                 } catch (IOException JavaDoc e) {
109                     // nothing... if the keystore doesn't exist, continue
110
}
111
112             } // while all key stores
113

114         }
115
116         return listOfKeystores;
117     }
118
119     /*
120      *
121      */

122     private void initialize() {
123         result = null;
124         trustedCertificates = null;
125         acceptUnsignedFiles = false;
126         listOfKeystores = null;
127     }
128
129     /*
130      * init
131      */

132     private void init(IFeature feature, ContentReference contentRef) throws CoreException {
133         jarFile = null;
134         if (contentRef instanceof JarContentReference) {
135             JarContentReference jarReference = (JarContentReference) contentRef;
136             try {
137                 jarFile = jarReference.asFile();
138                 if (UpdateCore.DEBUG && UpdateCore.DEBUG_SHOW_INSTALL)
139                     UpdateCore.debug("Attempting to read JAR file:"+jarFile); //$NON-NLS-1$
140

141                 // # of entries
142
if (!jarFile.exists()) throw new IOException JavaDoc();
143                 JarFile JavaDoc jar = new JarFile JavaDoc(jarFile);
144                 if (jar !=null){
145                     try {
146                         jar.close();
147                     } catch (IOException JavaDoc ex) {
148                         // unchecked
149
}
150                 }
151             } catch (ZipException JavaDoc e){
152                 throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_InvalidJar, (new String JavaDoc[] { jarReference.toString() })), e);
153             } catch (IOException JavaDoc e) {
154                 throw Utilities.newCoreException(NLS.bind(Messages.JarVerifier_UnableToAccessJar, (new String JavaDoc[] { jarReference.toString() })), e);
155             }
156         }
157
158         result = new JarVerificationResult();
159         result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
160         result.setResultException(null);
161         result.setFeature(feature);
162         result.setContentReference(contentRef);
163     }
164
165     /*
166      * Returns true if one of the certificate exists in the keystore
167      */

168     private boolean existsInKeystore(Certificate JavaDoc cert) throws CoreException {
169         try {
170             List JavaDoc keyStores = getKeyStores();
171             if (!keyStores.isEmpty()) {
172                 Iterator JavaDoc listOfKeystores = keyStores.iterator();
173                 while (listOfKeystores.hasNext()) {
174                     KeyStore JavaDoc keystore = (KeyStore JavaDoc) listOfKeystores.next();
175
176                     if (keystore.getCertificateAlias(cert) != null) {
177                         return true;
178                     }
179                 }
180             }
181         } catch (KeyStoreException JavaDoc e) {
182             throw Utilities.newCoreException(Messages.JarVerifier_KeyStoreNotLoaded, e);
183         }
184         return false;
185     }
186
187     /*
188      *
189      */

190     private List JavaDoc readJarFile(JarFile JavaDoc jarFile, String JavaDoc identifier)
191         throws IOException JavaDoc, InterruptedException JavaDoc {
192         List JavaDoc list = new ArrayList JavaDoc();
193
194         Enumeration JavaDoc entries = jarFile.entries();
195         JarEntry JavaDoc currentEntry = null;
196         InputStream JavaDoc in = null;
197         if (monitor != null)
198             monitor.setTaskName(NLS.bind(Messages.JarVerifier_Verify, (new String JavaDoc[] { identifier == null ? jarFile.getName(): identifier })));
199
200         try {
201             while (entries.hasMoreElements()) {
202                 currentEntry = (JarEntry JavaDoc) entries.nextElement();
203                 list.add(currentEntry);
204                 in = jarFile.getInputStream(currentEntry);
205                 while ((in.read(buffer, 0, buffer.length)) != -1) {
206                     // Security error thrown if tempered
207
}
208                 if (in!=null)
209                     in.close();
210             }
211         } catch (IOException JavaDoc e) {
212             result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
213             result.setResultException(e);
214         } finally {
215             try {
216                 if (in != null)
217                     in.close();
218             } catch (IOException JavaDoc e1) {
219                 // ignore
220
}
221         }
222
223         return list;
224     }
225
226     /*
227      * @param newMonitor org.eclipse.core.runtime.IProgressMonitor
228      */

229     public void setMonitor(IProgressMonitor newMonitor) {
230         monitor = newMonitor;
231     }
232
233     /*
234      * @see IVerifier#verify(IFeature,ContentReference,boolean, InstallMonitor)
235      */

236     public IVerificationResult verify(
237         IFeature feature,
238         ContentReference reference,
239         boolean isFeatureVerification,
240         InstallMonitor monitor)
241         throws CoreException {
242
243         if (reference == null)
244             return result;
245
246         // if parent knows how to verify, ask the parent first
247
if (getParent() != null) {
248             IVerificationResult vr =
249                 getParent().verify(feature, reference, isFeatureVerification, monitor);
250             if (vr.getVerificationCode() != IVerificationResult.TYPE_ENTRY_UNRECOGNIZED)
251                 return vr;
252         }
253
254         // the parent couldn't verify
255
setMonitor(monitor);
256         init(feature, reference);
257         result.isFeatureVerification(isFeatureVerification);
258
259         if (jarFile!=null) {
260                 result = verify(jarFile.getAbsolutePath(), reference.getIdentifier());
261         } else {
262             result.setVerificationCode(IVerificationResult.TYPE_ENTRY_UNRECOGNIZED);
263         }
264
265         return result;
266     }
267
268     /*
269      *
270      */

271     private JarVerificationResult verify(String JavaDoc file, String JavaDoc identifier) {
272
273         try {
274
275             // verify integrity
276
verifyIntegrity(file, identifier);
277
278             // do not close input stream
279
// as verifyIntegrity already did it
280

281             //if user already said yes
282
result.alreadySeen(alreadyValidated());
283
284             // verify source certificate
285
if (result.getVerificationCode()
286                 == IVerificationResult.TYPE_ENTRY_SIGNED_UNRECOGNIZED) {
287                 verifyAuthentication();
288             }
289
290             // save the fact the file is not signed, so the user will not be prompted again
291
if (result.getVerificationCode()
292                 == IVerificationResult.TYPE_ENTRY_NOT_SIGNED) {
293                 acceptUnsignedFiles = true;
294             }
295
296         } catch (Exception JavaDoc e) {
297             result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
298             result.setResultException(e);
299         }
300
301         if (monitor != null) {
302             monitor.worked(1);
303             if (monitor.isCanceled()) {
304                 result.setVerificationCode(IVerificationResult.VERIFICATION_CANCELLED);
305             }
306         }
307
308         return result;
309     }
310
311     /*
312      * Verifies that each file has at least one certificate
313      * valid in the keystore
314      *
315      * At least one certificate from each Certificate Array
316      * of the Jar file must be found in the known Certificates
317      */

318     private void verifyAuthentication() throws CoreException {
319
320         CertificatePair[] entries = result.getRootCertificates();
321         boolean certificateFound = false;
322
323         // If all the certificate of an entry are
324
// not found in the list of known certifcate
325
// the certificate is not trusted by any keystore.
326
for (int i = 0; i < entries.length; i++) {
327             certificateFound = existsInKeystore(entries[i].getRoot());
328             if (certificateFound) {
329                 result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_RECOGNIZED);
330                 result.setFoundCertificate(entries[i]);
331                 return;
332             }
333         }
334     }
335
336     /*
337      * Verifies the integrity of the JAR
338      */

339     private void verifyIntegrity(String JavaDoc file, String JavaDoc identifier) {
340
341         JarFile JavaDoc jarFile = null;
342
343         try {
344             // If the JAR is signed and not valid
345
// a security exception will be thrown
346
// while reading it
347
jarFile = new JarFile JavaDoc(file, true);
348             List JavaDoc filesInJar = readJarFile(jarFile, identifier);
349
350             // you have to read all the files once
351
// before getting the certificates
352
if (jarFile.getManifest() != null) {
353                 Iterator JavaDoc iter = filesInJar.iterator();
354                 boolean certificateFound = false;
355                 while (iter.hasNext()) {
356                     JarEntry JavaDoc currentJarEntry = (JarEntry JavaDoc) iter.next();
357                     Certificate JavaDoc[] certs = currentJarEntry.getCertificates();
358                     if ((certs != null) && (certs.length != 0)) {
359                         certificateFound = true;
360                         result.addCertificates(certs);
361                     } else {
362                         String JavaDoc jarEntryName = currentJarEntry.getName();
363                         if (!jarEntryName.toUpperCase().startsWith(MANIFEST)
364                             && !currentJarEntry.isDirectory()) {
365                             // if the jarEntry is not in MANIFEST, consider the whole file unsigned
366
break;
367                         }
368
369                     }
370                 }
371
372                 if (certificateFound)
373                     result.setVerificationCode(IVerificationResult.TYPE_ENTRY_SIGNED_UNRECOGNIZED);
374                 else
375                     result.setVerificationCode(IVerificationResult.TYPE_ENTRY_NOT_SIGNED);
376             } else {
377                 Exception JavaDoc e = new Exception JavaDoc(NLS.bind(Messages.JarVerifier_InvalidFile, (new String JavaDoc[] { file })));
378                 result.setResultException(e);
379                 result.setVerificationCode(IVerificationResult.TYPE_ENTRY_NOT_SIGNED);
380                 UpdateCore.warn(null,e);
381             }
382         } catch (SecurityException JavaDoc e) {
383             // Jar file is signed
384
// but content has changed since signed
385
result.setVerificationCode(IVerificationResult.TYPE_ENTRY_CORRUPTED);
386         } catch (InterruptedException JavaDoc e) {
387             result.setVerificationCode(IVerificationResult.VERIFICATION_CANCELLED);
388         } catch (Exception JavaDoc e) {
389             result.setVerificationCode(IVerificationResult.UNKNOWN_ERROR);
390             result.setResultException(e);
391         } finally {
392             if (jarFile!=null){
393                 try {jarFile.close();} catch (IOException JavaDoc e){}
394             }
395         }
396
397     }
398
399     /*
400      *
401      */

402     private boolean alreadyValidated() {
403
404         if (result.getVerificationCode() == IVerificationResult.TYPE_ENTRY_NOT_SIGNED)
405             return (acceptUnsignedFiles);
406
407         if (getTrustedCertificates() != null) {
408             Iterator JavaDoc iter = getTrustedCertificates().iterator();
409             CertificatePair[] jarPairs = result.getRootCertificates();
410
411             // check if this is not a user accepted certificate for this feature
412
while (iter.hasNext()) {
413                 CertificatePair trustedCertificate = (CertificatePair) iter.next();
414                 for (int i = 0; i < jarPairs.length; i++) {
415                     if (trustedCertificate.equals(jarPairs[i])) {
416                         return true;
417                     }
418                 }
419             }
420
421             // if certificate pair not found in trusted add it for next time
422
for (int i = 0; i < jarPairs.length; i++) {
423                 addTrustedCertificate(jarPairs[i]);
424             }
425         }
426
427         return false;
428     }
429
430     /*
431      *
432      */

433     private void addTrustedCertificate(CertificatePair pair) {
434         if (trustedCertificates == null)
435             trustedCertificates = new ArrayList JavaDoc();
436         if (pair != null)
437             trustedCertificates.add(pair);
438     }
439
440     /*
441      *
442      */

443     private List JavaDoc getTrustedCertificates() {
444         if (trustedCertificates == null)
445             trustedCertificates = new ArrayList JavaDoc();
446         return trustedCertificates;
447     }
448
449     /**
450      * @see IVerifier#setParent(IVerifier)
451      */

452     public void setParent(IVerifier parentVerifier) {
453         super.setParent(parentVerifier);
454         initialize();
455     }
456
457 }
458
Popular Tags