Source Code Cross Referenced for JarVerifier.java in  » Apache-Harmony-Java-SE » java-package » java » util » jar » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Apache Harmony Java SE » java package » java.util.jar 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /* 
002:         * Licensed to the Apache Software Foundation (ASF) under one or more
003:         * contributor license agreements.  See the NOTICE file distributed with
004:         * this work for additional information regarding copyright ownership.
005:         * The ASF licenses this file to You under the Apache License, Version 2.0
006:         * (the "License"); you may not use this file except in compliance with
007:         * the License.  You may obtain a copy of the License at
008:         * 
009:         *     http://www.apache.org/licenses/LICENSE-2.0
010:         * 
011:         * Unless required by applicable law or agreed to in writing, software
012:         * distributed under the License is distributed on an "AS IS" BASIS,
013:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014:         * See the License for the specific language governing permissions and
015:         * limitations under the License.
016:         */
017:
018:        package java.util.jar;
019:
020:        import java.io.ByteArrayInputStream;
021:        import java.io.IOException;
022:        import java.io.OutputStream;
023:        import java.io.UnsupportedEncodingException;
024:        import java.security.GeneralSecurityException;
025:        import java.security.MessageDigest;
026:        import java.security.NoSuchAlgorithmException;
027:        import java.security.cert.Certificate;
028:        import java.util.HashMap;
029:        import java.util.Hashtable;
030:        import java.util.Iterator;
031:        import java.util.Map;
032:        import java.util.StringTokenizer;
033:        import java.util.Vector;
034:        import java.util.zip.ZipEntry;
035:
036:        import org.apache.harmony.archive.internal.nls.Messages;
037:        import org.apache.harmony.luni.util.Base64;
038:        import org.apache.harmony.security.utils.JarUtils;
039:
040:        import org.apache.harmony.archive.util.Util;
041:
042:        /**
043:         * Non-public class used by {@link JarFile} and {@link JarInputStream} to manage
044:         * the verification of signed jars. <code>JarFile</code> and
045:         * <code>JarInputStream</code> objects will be expected to have a
046:         * <code>JarVerifier</code> instance member which can be used to carry out the
047:         * tasks associated with verifying a signed jar. These tasks would typically
048:         * include:
049:         * <ul>
050:         * <li>verification of all signed signature files
051:         * <li>confirmation that all signed data was signed only by the party or
052:         * parties specified in the signature block data
053:         * <li>verification that the contents of all signature files (i.e.
054:         * <code>.SF</code> files) agree with the jar entries information found in the
055:         * jar manifest.
056:         * </ul>
057:         */
058:        class JarVerifier {
059:
060:            private final String jarName;
061:
062:            private Manifest man;
063:
064:            private HashMap<String, byte[]> metaEntries = new HashMap<String, byte[]>(
065:                    5);
066:
067:            private final Hashtable<String, HashMap<String, Attributes>> signatures = new Hashtable<String, HashMap<String, Attributes>>(
068:                    5);
069:
070:            private final Hashtable<String, Certificate[]> certificates = new Hashtable<String, Certificate[]>(
071:                    5);
072:
073:            private final Hashtable<String, Certificate[]> verifiedEntries = new Hashtable<String, Certificate[]>();
074:
075:            byte[] mainAttributesChunk;
076:
077:            /**
078:             * TODO Type description
079:             */
080:            static class VerifierEntry extends OutputStream {
081:
082:                MessageDigest digest;
083:
084:                byte[] hash;
085:
086:                Certificate[] certificates;
087:
088:                VerifierEntry(MessageDigest digest, byte[] hash,
089:                        Certificate[] certificates) {
090:                    this .digest = digest;
091:                    this .hash = hash;
092:                    this .certificates = certificates;
093:                }
094:
095:                /*
096:                 * (non-Javadoc)
097:                 * 
098:                 * @see java.io.OutputStream#write(int)
099:                 */
100:                @Override
101:                public void write(int value) {
102:                    digest.update((byte) value);
103:                }
104:
105:                /*
106:                 * (non-Javadoc)
107:                 * 
108:                 * @see java.io.OutputStream#write(byte[], int, int)
109:                 */
110:                @Override
111:                public void write(byte[] buf, int off, int nbytes) {
112:                    digest.update(buf, off, nbytes);
113:                }
114:            }
115:
116:            /**
117:             * Constructs and answers with a new instance of JarVerifier.
118:             * 
119:             * @param name
120:             *            the name of the jar file being verified.
121:             */
122:            JarVerifier(String name) {
123:                jarName = name;
124:            }
125:
126:            /**
127:             * Called for each new jar entry read in from the input stream. This method
128:             * constructs and returns a new {@link VerifierEntry} which contains the
129:             * certificates used to sign the entry and its hash value as specified in
130:             * the jar manifest.
131:             * 
132:             * @param name
133:             *            the name of an entry in a jar file which is <b>not</b> in the
134:             *            <code>META-INF</code> directory.
135:             * @return a new instance of {@link VerifierEntry} which can be used by
136:             *         callers as an {@link OutputStream}.
137:             */
138:            VerifierEntry initEntry(String name) {
139:                // If no manifest is present by the time an entry is found,
140:                // verification cannot occur. If no signature files have
141:                // been found, do not verify.
142:                if (man == null || signatures.size() == 0) {
143:                    return null;
144:                }
145:
146:                Attributes attributes = man.getAttributes(name);
147:                // entry has no digest
148:                if (attributes == null) {
149:                    return null;
150:                }
151:
152:                Vector<Certificate> certs = new Vector<Certificate>();
153:                Iterator<Map.Entry<String, HashMap<String, Attributes>>> it = signatures
154:                        .entrySet().iterator();
155:                while (it.hasNext()) {
156:                    Map.Entry<String, HashMap<String, Attributes>> entry = it
157:                            .next();
158:                    HashMap<String, Attributes> hm = entry.getValue();
159:                    if (hm.get(name) != null) {
160:                        // Found an entry for entry name in .SF file
161:                        String signatureFile = entry.getKey();
162:
163:                        Vector<Certificate> newCerts = getSignerCertificates(
164:                                signatureFile, certificates);
165:                        Iterator<Certificate> iter = newCerts.iterator();
166:                        while (iter.hasNext()) {
167:                            certs.add(iter.next());
168:                        }
169:                    }
170:                }
171:
172:                // entry is not signed
173:                if (certs.size() == 0) {
174:                    return null;
175:                }
176:                Certificate[] certificatesArray = new Certificate[certs.size()];
177:                certs.toArray(certificatesArray);
178:
179:                String algorithms = attributes.getValue("Digest-Algorithms"); //$NON-NLS-1$
180:                if (algorithms == null) {
181:                    algorithms = "SHA SHA1"; //$NON-NLS-1$
182:                }
183:                StringTokenizer tokens = new StringTokenizer(algorithms);
184:                while (tokens.hasMoreTokens()) {
185:                    String algorithm = tokens.nextToken();
186:                    String hash = attributes.getValue(algorithm + "-Digest"); //$NON-NLS-1$
187:                    if (hash == null) {
188:                        continue;
189:                    }
190:                    byte[] hashBytes;
191:                    try {
192:                        hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$
193:                    } catch (UnsupportedEncodingException e) {
194:                        throw new RuntimeException(e.toString());
195:                    }
196:
197:                    try {
198:                        return new VerifierEntry(MessageDigest
199:                                .getInstance(algorithm), hashBytes,
200:                                certificatesArray);
201:                    } catch (NoSuchAlgorithmException e) {
202:                        // Ignored
203:                    }
204:                }
205:                return null;
206:            }
207:
208:            /**
209:             * Add a new meta entry to the internal collection of data held on each jar
210:             * entry in the <code>META-INF</code> directory including the manifest
211:             * file itself. Files associated with the signing of a jar would also be
212:             * added to this collection.
213:             * 
214:             * @param name
215:             *            the name of the file located in the <code>META-INF</code>
216:             *            directory.
217:             * @param buf
218:             *            the file bytes for the file called <code>name</code>.
219:             * @see #removeMetaEntries()
220:             */
221:            void addMetaEntry(String name, byte[] buf) {
222:                metaEntries.put(Util.toASCIIUpperCase(name), buf);
223:            }
224:
225:            /**
226:             * If the associated jar file is signed, check on the validity of all of the
227:             * known signatures.
228:             * 
229:             * @return <code>true</code> if the associated jar is signed and an
230:             *         internal check verifies the validity of the signature(s).
231:             *         <code>false</code> if the associated jar file has no entries at
232:             *         all in its <code>META-INF</code> directory. This situation is
233:             *         indicative of an invalid jar file.
234:             *         <p>
235:             *         Will also return true if the jar file is <i>not</i> signed.
236:             *         </p>
237:             * @throws SecurityException
238:             *             if the jar file is signed and it is determined that a
239:             *             signature block file contains an invalid signature for the
240:             *             corresponding signature file.
241:             */
242:            synchronized boolean readCertificates() {
243:                if (metaEntries == null) {
244:                    return false;
245:                }
246:                Iterator<String> it = metaEntries.keySet().iterator();
247:                while (it.hasNext()) {
248:                    String key = it.next();
249:                    if (key.endsWith(".DSA") || key.endsWith(".RSA")) { //$NON-NLS-1$ //$NON-NLS-2$
250:                        verifyCertificate(key);
251:                        // Check for recursive class load
252:                        if (metaEntries == null) {
253:                            return false;
254:                        }
255:                        it.remove();
256:                    }
257:                }
258:                return true;
259:            }
260:
261:            /**
262:             * @param certFile
263:             */
264:            private void verifyCertificate(String certFile) {
265:                // Found Digital Sig, .SF should already have been read
266:                String signatureFile = certFile.substring(0, certFile
267:                        .lastIndexOf('.'))
268:                        + ".SF"; //$NON-NLS-1$
269:                byte[] sfBytes = metaEntries.get(signatureFile);
270:                if (sfBytes == null) {
271:                    return;
272:                }
273:
274:                byte[] sBlockBytes = metaEntries.get(certFile);
275:                try {
276:                    Certificate[] signerCertChain = JarUtils.verifySignature(
277:                            new ByteArrayInputStream(sfBytes),
278:                            new ByteArrayInputStream(sBlockBytes));
279:                    /*
280:                     * Recursive call in loading security provider related class which
281:                     * is in a signed jar.
282:                     */
283:                    if (null == metaEntries) {
284:                        return;
285:                    }
286:                    if (signerCertChain != null) {
287:                        certificates.put(signatureFile, signerCertChain);
288:                    }
289:                } catch (IOException e) {
290:                    return;
291:                } catch (GeneralSecurityException e) {
292:                    /* [MSG "archive.30", "{0} failed verification of {1}"] */
293:                    throw new SecurityException(Messages.getString(
294:                            "archive.30", jarName, signatureFile)); //$NON-NLS-1$
295:                }
296:
297:                // Verify manifest hash in .sf file
298:                Attributes attributes = new Attributes();
299:                HashMap<String, Attributes> hm = new HashMap<String, Attributes>();
300:                try {
301:                    new InitManifest(new ByteArrayInputStream(sfBytes),
302:                            attributes, hm, null, "Signature-Version"); //$NON-NLS-1$
303:                } catch (IOException e) {
304:                    return;
305:                }
306:
307:                boolean createdBySigntool = false;
308:                String createdByValue = attributes.getValue("Created-By"); //$NON-NLS-1$
309:                if (createdByValue != null) {
310:                    createdBySigntool = createdByValue.indexOf("signtool") != -1; //$NON-NLS-1$
311:                }
312:
313:                // Use .SF to verify the mainAttributes of the manifest
314:                // If there is no -Digest-Manifest-Main-Attributes entry in .SF
315:                // file, such as those created before java 1.5, then we ignore
316:                // such verification.
317:                // FIXME: The meaning of createdBySigntool
318:                if (mainAttributesChunk != null && !createdBySigntool) {
319:                    String digestAttribute = "-Digest-Manifest-Main-Attributes"; //$NON-NLS-1$
320:                    if (!verify(attributes, digestAttribute,
321:                            mainAttributesChunk, false, true)) {
322:                        /* [MSG "archive.30", "{0} failed verification of {1}"] */
323:                        throw new SecurityException(Messages.getString(
324:                                "archive.30", jarName, signatureFile)); //$NON-NLS-1$
325:                    }
326:                }
327:
328:                byte[] manifest = metaEntries.get(JarFile.MANIFEST_NAME);
329:                if (manifest == null) {
330:                    return;
331:                }
332:                // Use .SF to verify the whole manifest
333:                String digestAttribute = createdBySigntool ? "-Digest" //$NON-NLS-1$
334:                        : "-Digest-Manifest"; //$NON-NLS-1$
335:                if (!verify(attributes, digestAttribute, manifest, false, false)) {
336:                    Iterator<Map.Entry<String, Attributes>> it = hm.entrySet()
337:                            .iterator();
338:                    while (it.hasNext()) {
339:                        Map.Entry<String, Attributes> entry = it.next();
340:                        byte[] chunk = man.getChunk(entry.getKey());
341:                        if (chunk == null) {
342:                            return;
343:                        }
344:                        if (!verify(entry.getValue(), "-Digest", chunk, //$NON-NLS-1$
345:                                createdBySigntool, false)) {
346:                            /*
347:                             * [MSG "archive.31", "{0} has invalid digest for {1} in
348:                             * {2}"]
349:                             */
350:                            throw new SecurityException(Messages.getString(
351:                                    "archive.31", //$NON-NLS-1$
352:                                    new Object[] { signatureFile,
353:                                            entry.getKey(), jarName }));
354:                        }
355:                    }
356:                }
357:                metaEntries.put(signatureFile, null);
358:                signatures.put(signatureFile, hm);
359:            }
360:
361:            /**
362:             * Associate this verifier with the specified {@link Manifest} object.
363:             * 
364:             * @param mf
365:             *            a <code>java.util.jar.Manifest</code> object.
366:             */
367:            void setManifest(Manifest mf) {
368:                man = mf;
369:            }
370:
371:            /**
372:             * Verifies that the digests stored in the manifest match the decrypted
373:             * digests from the .SF file. This indicates the validity of the signing,
374:             * not the integrity of the file, as it's digest must be calculated and
375:             * verified when its contents are read.
376:             * 
377:             * @param entry
378:             *            the {@link VerifierEntry} associated with the specified
379:             *            <code>zipEntry</code>.
380:             * @param zipEntry
381:             *            an entry in the jar file
382:             * @throws SecurityException
383:             *             if the digest value stored in the manifest does <i>not</i>
384:             *             agree with the decrypted digest as recovered from the
385:             *             <code>.SF</code> file.
386:             * @see #initEntry(String)
387:             */
388:            void verifySignatures(VerifierEntry entry, ZipEntry zipEntry) {
389:                byte[] digest = entry.digest.digest();
390:                if (!MessageDigest.isEqual(digest, Base64.decode(entry.hash))) {
391:                    /* [MSG "archive.31", "{0} has invalid digest for {1} in {2}"] */
392:                    throw new SecurityException(Messages
393:                            .getString("archive.31", new Object[] { //$NON-NLS-1$
394:                                    JarFile.MANIFEST_NAME, zipEntry.getName(),
395:                                            jarName }));
396:                }
397:                verifiedEntries.put(zipEntry.getName(), entry.certificates);
398:            }
399:
400:            /**
401:             * Returns a <code>boolean</code> indication of whether or not the
402:             * associated jar file is signed.
403:             * 
404:             * @return <code>true</code> if the jar is signed, <code>false</code>
405:             *         otherwise.
406:             */
407:            boolean isSignedJar() {
408:                return certificates.size() > 0;
409:            }
410:
411:            private boolean verify(Attributes attributes, String entry,
412:                    byte[] data, boolean ignoreSecondEndline, boolean ignorable) {
413:                String algorithms = attributes.getValue("Digest-Algorithms"); //$NON-NLS-1$
414:                if (algorithms == null) {
415:                    algorithms = "SHA SHA1"; //$NON-NLS-1$
416:                }
417:                StringTokenizer tokens = new StringTokenizer(algorithms);
418:                while (tokens.hasMoreTokens()) {
419:                    String algorithm = tokens.nextToken();
420:                    String hash = attributes.getValue(algorithm + entry);
421:                    if (hash == null) {
422:                        continue;
423:                    }
424:
425:                    MessageDigest md;
426:                    try {
427:                        md = MessageDigest.getInstance(algorithm);
428:                    } catch (NoSuchAlgorithmException e) {
429:                        continue;
430:                    }
431:                    if (ignoreSecondEndline && data[data.length - 1] == '\n'
432:                            && data[data.length - 2] == '\n') {
433:                        md.update(data, 0, data.length - 1);
434:                    } else {
435:                        md.update(data, 0, data.length);
436:                    }
437:                    byte[] b = md.digest();
438:                    byte[] hashBytes;
439:                    try {
440:                        hashBytes = hash.getBytes("ISO8859_1"); //$NON-NLS-1$
441:                    } catch (UnsupportedEncodingException e) {
442:                        throw new RuntimeException(e.toString());
443:                    }
444:                    return MessageDigest.isEqual(b, Base64.decode(hashBytes));
445:                }
446:                return ignorable;
447:            }
448:
449:            /**
450:             * Returns all of the {@link java.security.cert.Certificate} instances that
451:             * were used to verify the signature on the jar entry called
452:             * <code>name</code>.
453:             * 
454:             * @param name
455:             *            the name of a jar entry.
456:             * @return an array of {@link java.security.cert.Certificate}.
457:             */
458:            Certificate[] getCertificates(String name) {
459:                Certificate[] verifiedCerts = verifiedEntries.get(name);
460:                if (verifiedCerts == null) {
461:                    return null;
462:                }
463:                return verifiedCerts.clone();
464:            }
465:
466:            /**
467:             * Remove all entries from the internal collection of data held about each
468:             * jar entry in the <code>META-INF</code> directory.
469:             * 
470:             * @see #addMetaEntry(String, byte[])
471:             */
472:            void removeMetaEntries() {
473:                metaEntries = null;
474:            }
475:
476:            /**
477:             * Returns a <code>Vector</code> of all of the
478:             * {@link java.security.cert.Certificate}s that are associated with the
479:             * signing of the named signature file.
480:             * 
481:             * @param signatureFileName
482:             *            the name of a signature file
483:             * @param certificates
484:             *            a <code>Map</code> of all of the certificate chains
485:             *            discovered so far while attempting to verify the jar that
486:             *            contains the signature file <code>signatureFileName</code>.
487:             *            This object will have been previously set in the course of one
488:             *            or more calls to
489:             *            {@link #verifyJarSignatureFile(String, String, String, Map, Map)}
490:             *            where it was passed in as the last argument.
491:             * @return all of the <code>Certificate</code> entries for the signer of
492:             *         the jar whose actions led to the creation of the named signature
493:             *         file.
494:             */
495:            public static Vector<Certificate> getSignerCertificates(
496:                    String signatureFileName,
497:                    Map<String, Certificate[]> certificates) {
498:                Vector<Certificate> result = new Vector<Certificate>();
499:                Certificate[] certChain = certificates.get(signatureFileName);
500:                if (certChain != null) {
501:                    for (Certificate element : certChain) {
502:                        result.add(element);
503:                    }
504:                }
505:                return result;
506:            }
507:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.