History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/util/SignatureFileVerifier.java (1 of 18352 files). Show all

Unified · Split

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