signaturefileverifier.java

来自「This is a resource based on j2me embedde」· Java 代码 · 共 495 行

JAVA
495
字号
/* * @(#)SignatureFileVerifier.java	1.29 06/10/10 * * Copyright  1990-2008 Sun Microsystems, Inc. All Rights Reserved.   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER   *    * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License version   * 2 only, as published by the Free Software Foundation.    *    * This program is distributed in the hope that it will be useful, but   * WITHOUT ANY WARRANTY; without even the implied warranty of   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU   * General Public License version 2 for more details (a copy is   * included at /legal/license.txt).    *    * You should have received a copy of the GNU General Public License   * version 2 along with this work; if not, write to the Free Software   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA   * 02110-1301 USA    *    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa   * Clara, CA 95054 or visit www.sun.com if you need additional   * information or have any questions.  * */ package sun.security.util;import java.security.cert.Certificate;import java.security.cert.X509Certificate;import java.security.cert.CertificateException;import java.security.*;import java.io.*;import java.util.*;import java.util.jar.*;import sun.security.pkcs.*;import sun.misc.BASE64Decoder;public class SignatureFileVerifier {    /* Are we debugging ? */    private static final Debug debug = Debug.getInstance("jar");    /* cache of Certificate[] objects */    private ArrayList certCache;    /** the PKCS7 block for this .DSA/.RSA file */    private PKCS7 block;    /** the raw bytes of the .SF file */    private byte sfBytes[];    /** the name of the signature block file, uppercased and without     *  the extension (.DSA/.RSA)      */    private String name;    /** the ManifestDigester */    private ManifestDigester md;    /** cache of created MessageDigest objects */    private HashMap createdDigests;    /* workaround for parsing Netscape jars  */    private boolean workaround = false;    /**     * Create the named SignatureFileVerifier.     *     * @param name the name of the signature block file (.DSA/.RSA)     *     * @param rawBytes the raw bytes of the signature block file     */    public SignatureFileVerifier(ArrayList certCache,				 ManifestDigester md,				 String name,				 byte rawBytes[])	throws IOException    {	block = new PKCS7(rawBytes);	sfBytes = block.getContentInfo().getData();	this.name = name.substring(0, name.lastIndexOf("."))                                                   .toUpperCase(Locale.ENGLISH);	this.md = md;	this.certCache = certCache;    }    /**     * returns true if we need the .SF file     */    public boolean needSignatureFileBytes()    {	return sfBytes == null;    }    /**     * returns true if we need this .SF file.     *     * @param name the name of the .SF file without the extension     *     */    public boolean needSignatureFile(String name)    {	return this.name.equalsIgnoreCase(name);    }    /**     * used to set the raw bytes of the .SF file when it      * is external to the signature block file.     */    public void setSignatureFile(byte sfBytes[])    {	this.sfBytes = sfBytes;    }    /** get digest from cache */    private MessageDigest getDigest(String algorithm)    {	if (createdDigests == null)	    createdDigests = new HashMap();	MessageDigest digest = 	    (MessageDigest) createdDigests.get(algorithm);	if (digest == null) {	    try {		digest = MessageDigest.getInstance(algorithm);		createdDigests.put(algorithm, digest);	    } catch (NoSuchAlgorithmException nsae) {		// ignore	    }	}	return digest;    }    /**     * process the signature block file. Goes through the .SF file     * and adds certificates for each section where the .SF section     * hash was verified against the Manifest section.     *     *     */    public void process(Hashtable certificates)	throws IOException, SignatureException, NoSuchAlgorithmException,	    JarException    {	Manifest sf = new Manifest();	sf.read(new ByteArrayInputStream(sfBytes));	String version = 	    sf.getMainAttributes().getValue(Attributes.Name.SIGNATURE_VERSION);	if ((version == null) || !(version.equalsIgnoreCase("1.0"))) {	    // FIXME: should this be an exception?	    // for now we just ignore this signature file	    return;	}	SignerInfo[] infos = block.verify(sfBytes);	if (infos == null) {	    throw new SecurityException("cannot verify signature block file " +					name);	}	BASE64Decoder decoder = new BASE64Decoder();	Certificate[] newCerts = getCertificates(infos, block);	// make sure we have something to do all this work for...	if (newCerts == null)	    return;	Iterator entries = sf.getEntries().entrySet().iterator();	// see if we can verify the whole manifest first	boolean manifestSigned = verifyManifestHash(sf, md, decoder);	// go through each section in the signature file	while(entries.hasNext()) {	    Map.Entry e = (Map.Entry)entries.next();	    String name = (String) e.getKey();	    if (manifestSigned ||	       (verifySection((Attributes) e.getValue(), name, md, decoder))) {		if (name.startsWith("./"))		    name = name.substring(2);		if (name.startsWith("/"))		    name = name.substring(1);		updateCerts(newCerts, certificates, name);		if (debug != null) {		    debug.println("processSignature signed name = "+name);		}	    } else if (debug != null) {		debug.println("processSignature unsigned name = "+name);	    }	}    }    /**     * See if the whole manifest was signed.     */    private boolean verifyManifestHash(Manifest sf,				       ManifestDigester md,				       BASE64Decoder decoder)	 throws IOException    {	Attributes mattr = sf.getMainAttributes();	boolean manifestSigned = false;	Iterator mit = mattr.entrySet().iterator();	// go through all the attributes and process *-Digest-Manifest entries	while(mit.hasNext()) {	    Map.Entry se = (Map.Entry)mit.next();	    String key = se.getKey().toString();	    if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST-MANIFEST")) {		// 16 is length of "-Digest-Manifest"		String algorithm = key.substring(0, key.length()-16);		MessageDigest digest = getDigest(algorithm);		if (digest != null) {		    byte[] computedHash = md.manifestDigest(digest);		    byte[] expectedHash = 			decoder.decodeBuffer((String)se.getValue());		    if (debug != null) {		     debug.println("Signature File: Manifest digest " +					  digest.getAlgorithm());		     debug.println( "  sigfile  " + toHex(expectedHash));		     debug.println( "  computed " + toHex(computedHash));		     debug.println();		    }		    if (MessageDigest.isEqual(computedHash,					      expectedHash)) {			manifestSigned = true;		    } else {			//we will continue and verify each section		    }		}	    }	}	return manifestSigned;    }    /**     * given the .SF digest header, and the data from the     * section in the manifest, see if the hashes match.     * if not, throw a SecurityException.     *     * @return true if all the -Digest headers verified     * @exception SecurityException if the hash was not equal     */    private boolean verifySection(Attributes sfAttr,				  String name,				  ManifestDigester md,				  BASE64Decoder decoder)	 throws IOException    {	boolean oneDigestVerified = false;	ManifestDigester.Entry mde = md.get(name,block.isOldStyle());	if (mde == null) {	    throw new SecurityException(		  "no manifiest section for signature file entry "+name);	}	if (sfAttr != null) {	    //sun.misc.HexDumpEncoder hex = new sun.misc.HexDumpEncoder();	    //hex.encodeBuffer(data, System.out);	    Iterator it = sfAttr.entrySet().iterator();	    // go through all the attributes and process *-Digest entries	    while(it.hasNext()) {		Map.Entry se = (Map.Entry)it.next();		String key = se.getKey().toString();				if (key.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {		    // 7 is length of "-Digest"		    String algorithm = key.substring(0, key.length()-7);		    MessageDigest digest = getDigest(algorithm);		    if (digest != null) {			boolean ok = false;			byte[] expected = 			    decoder.decodeBuffer((String)se.getValue());			byte[] computed;			if (workaround) {			    computed = mde.digestWorkaround(digest);			} else {			    computed = mde.digest(digest);			}			if (debug != null) {			  debug.println("Signature Block File: " +				   name + " digest=" + digest.getAlgorithm());			  debug.println("  expected " + toHex(expected));			  debug.println("  computed " + toHex(computed));			  debug.println();			}			if (MessageDigest.isEqual(computed, expected)) {			    oneDigestVerified = true;			    ok = true;			} else {			    // attempt to fallback to the workaround			    if (!workaround) {			       computed = mde.digestWorkaround(digest);			       if (MessageDigest.isEqual(computed, expected)) {				   if (debug != null) {				       debug.println("  re-computed " + toHex(computed));				       debug.println();				   }				   workaround = true;				   oneDigestVerified = true;				   ok = true;			       }			    }			}			if (!ok){			    throw new SecurityException("invalid " + 				       digest.getAlgorithm() + 				       " signature file digest for " + name);			}		    }		}	    }	}	return oneDigestVerified;    }    /**     * Given the PKCS7 blocks and SignerInfo[], create a Vector     * of certificate objects. We do this only *once* for a given     * signature block file.     */    private Certificate[] getCertificates(SignerInfo infos[], PKCS7 block) {	ArrayList certs = null;	for (int i = 0; i < infos.length; i++) {	    SignerInfo info = infos[i];	    try {		ArrayList certChain = info.getCertificateChain(block);		if (certs == null)		    certs = new ArrayList();		certs.addAll(certChain);		if (debug != null) {		    debug.println("Signature Block Certificate: " +				  (X509Certificate)certChain.get(0));		}	    } catch (IOException e) {	    }	}	if (certs != null) {	    Certificate[] certificates = new Certificate[certs.size()];	    System.arraycopy(certs.toArray(), 0,			     certificates, 0, 			     certs.size());	    return certificates;	} else {	    return null;	}    }    // for the toHex function    private static final char[] hexc =            {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};    /**     * convert a byte array to a hex string for debugging purposes     * @param data the binary data to be converted to a hex string     * @return an ASCII hex string     */    static String toHex(byte[] data) {	StringBuffer sb = new StringBuffer(data.length*2);	for (int i=0; i<data.length; i++) {	    sb.append(hexc[(data[i] >>4) & 0x0f]);	    sb.append(hexc[data[i] & 0x0f]);	}	return sb.toString();    }    // returns true if set contains cert    static boolean contains(Certificate[] set, Certificate cert)    {	for (int i=0; i < set.length; i++) {	    if (set[i].equals(cert))		return true;	}	return false;    }    // returns true if subset is a subset of set    static boolean isSubSet(Certificate[] subset, Certificate[] set)    {	// check for the same object	if (set == subset) 	    return true;	for (int i = 0; i < subset.length; i++) {	    if (!contains(set, subset[i]))		return false;	}	return true;    }    /**     * returns true if certs contains exactly the same certs as     * oldCerts and newCerts, false otherwise. oldCerts     * is allowed to be null.     */    static boolean matches(Certificate[] certs,			   Certificate[] oldCerts, Certificate[] newCerts)    {	// special case 	if ((oldCerts == null) && (certs == newCerts))	    return true;	// make sure all oldCerts are in certs	if ((oldCerts != null) && !isSubSet(oldCerts, certs))	    return false;		// make sure all newCerts are in certs	if (!isSubSet(newCerts, certs)) {	    return false;	}	// now make sure all the certificates in certs are	// also in oldCerts or newCerts	for (int i = 0; i < certs.length; i++) {	    boolean found = 		((oldCerts != null) && contains(oldCerts, certs[i])) ||		contains(newCerts, certs[i]);	    if (!found)		return false;	}	return true;    }    void updateCerts(Certificate[] newCerts, Hashtable certHash, String name)    {	Certificate[] oldCerts =	    (Certificate[])certHash.get(name);	// search through the cache for a match, go in reverse order	// as we are more likely to find a match with the last one	// added to the cache	Certificate[] certs;	for (int i = certCache.size()-1; i != -1; i--) {	    certs = (Certificate[]) certCache.get(i);	    if (matches(certs, oldCerts, newCerts)) {		certHash.put(name, certs);		return;	    }	}	if (oldCerts == null) {	    certs = newCerts;	} else {	    certs = 		new Certificate[oldCerts.length + newCerts.length];	    System.arraycopy(oldCerts, 0, certs, 0, oldCerts.length);	    System.arraycopy(newCerts, 0, certs, oldCerts.length, 			     newCerts.length);	}	certCache.add(certs);	certHash.put(name, certs);    }}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?