alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.modules.autoupdate;

import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import java.text.MessageFormat;

import org.openide.util.NbBundle;


/** Class for verifying signs in NBM Files.
 * @author  Martin Ryzl, Petr Hrebejk
 */
class SignVerifier extends Object {

    /** The password to the Forte keystore */
    private static final String KS_PSSWD = "open4all"; // NOI18N

    private static final String ENTRY_SEPARATOR = "/";  // NOI18N
    private static final String NBM_BIN = "netbeans/bin"; // NOI18N
    private static final String NBM_LIB = "netbeans/lib"; // NOI18N
    private static final String NBM_MAIN = "main"; // NOI18N
    private static final String NBM_MODULES = "netbeans/modules"; // NOI18N
    private static final String NBM_AUTOLOAD = NBM_MODULES + ENTRY_SEPARATOR + SafeModule.PROP_AUTOLOAD; // NOI18N
    private static final String NBM_EAGER = NBM_MODULES + ENTRY_SEPARATOR + SafeModule.PROP_EAGER; // NOI18N
    private static final String JAR_EXT = ".jar";

    public static final int NOT_CHECKED = -1;
    public static final int BAD_DOWNLOAD = 0;
    public static final int CORRUPTED = 1;
    public static final int NOT_SIGNED = 2;
    public static final int SIGNED = 3;
    public static final int TRUSTED = 4;

    private static final String NEW_LINE = "\n"; // NOI18N
    private static final String SPACE = " "; // NOI18N
    private static final String TAB = "\t"; //NOI18N

    /** The update check progress panel */
    ProgressDialog progressDialog;
    /** Wizard validator, enables the Next button in wizard */
    private Wizard.Validator validator;
    /** Total size of the verify */
    private int verifySize;
    /** KBytes verified */
    private long totalVerified;
    /** Number of modules to verify */
    private long modulesCount;

    private boolean verifyCanceled = false;
    private boolean wizardCanceled = false;

    /** Creates new VeriSign */
    SignVerifier( ProgressDialog progressDialog,
                  Wizard.Validator validator ) {
        this.validator = validator;
        this.progressDialog = progressDialog;
    }


    /** Verifies all downloaded modules */
    void doVerify() {

        verifyCanceled = false;

        Runnable task = new Runnable () {
                            public void run() {

                                progressDialog.setLabelText( ProgressDialog.PARTIAL_LABEL,
                                                             getBundle( "CTL_PreparingVerify_Label" ) );

                                verifySize = getTotalVerifySize();

                                if ( verifyCanceled )
                                    return;

                                verifyAll();

                                if ( verifyCanceled )
                                    return;

                                validator.setValid( true );
                            }
                        };

        Wizard.getRequestProcessor().post( task );
    }


    /** Total size for verify in KBytes */
    int getTotalVerifySize( ) {
        long result = 0L;
        modulesCount = 0;

        Iterator it = Wizard.getAllModules().iterator();

        while( it.hasNext() ) {
            ModuleUpdate mu = (ModuleUpdate)it.next();

            if ( mu.isSelected() && mu.isDownloadOK() && 
                    (mu.getSecurity() == NOT_CHECKED || mu.getSecurity() == BAD_DOWNLOAD )) {
                File file = Downloader.getNBM( mu );
                result += file.length();
                modulesCount++;
            }
        }
        return (int)(result / 1024);
    }


    void verifyAll() {

        progressDialog.setGaugeBounds( ProgressDialog.OVERALL_GAUGE, 0, verifySize );
        progressDialog.setGaugeValue( ProgressDialog.OVERALL_GAUGE, 0 );
        progressDialog.setLabelText( ProgressDialog.OVERALL_LABEL, "" ); // NOI18N

        progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, 0 );

        int currentModule = 0;
        totalVerified = 0;

        Iterator it = Wizard.getAllModules().iterator();

        while( it.hasNext() ) {

            if ( verifyCanceled )
                return;

            ModuleUpdate mu = (ModuleUpdate)it.next();

            if ( mu.isSelected() && mu.isDownloadOK() && 
                    (mu.getSecurity() == NOT_CHECKED || mu.getSecurity() == BAD_DOWNLOAD )) {

                if ( verifyCanceled )
                    return;

                progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, 0 );
                progressDialog.setLabelText( ProgressDialog.PARTIAL_LABEL,
                                             mu.getName() + " [" + (currentModule + 1) + "/" + modulesCount + "]" ); //NOI18N

                File file = Downloader.getNBM( mu );
                try {
                    Collection certificates = verifyJar( file, mu );
                    //showCollection(certificates);

                    if ( certificates == null ) {
                        mu.setSecurity( NOT_SIGNED );
                        mu.setInstallApproved( false );
                    }
                    else {
                        mu.setCerts( certificates );

                        if ( isTrusted( certificates ) ) {
                            mu.setSecurity( TRUSTED );
                            mu.setInstallApproved( true );
                        }
                        else {
                            mu.setSecurity( SIGNED );
                            mu.setInstallApproved( false );
                        }
                    }
                }
                catch( SecurityException e ) {
                    mu.setSecurity( CORRUPTED );
                    mu.setInstallApproved( false );
                }
                catch( IOException e ) {
                    mu.setSecurity( BAD_DOWNLOAD );
                    mu.setInstallApproved( false );
                    mu.setDownloadOK( false );
                }

                currentModule++;
            }
        }

        progressDialog.setGaugeValue( ProgressDialog.OVERALL_GAUGE,  verifySize );
        String mssgTotal = MessageFormat.format( getBundle( "FMT_VerifiedTotal" ),
                           new Object[] { new Integer( verifySize ),
                                          new Integer( verifySize ) } );
        progressDialog.setLabelText( ProgressDialog.OVERALL_LABEL, mssgTotal );
    }


    /**
    * @param args the command line arguments
    */
    /*
    public void check ( File file ) throws Exception {
      Collection certificates = verifyJar( file );
      
      showCollection(certificates);
      KeyStore keyStore = getKeyStore(KEYSTORE, "changeit", null);
      showCollection(getCertificates(keyStore));  
}
    */

    /** Verify jar file.
    * @return Collection of certificates
    */
    public Collection verifyJar( File jarName, ModuleUpdate mu ) throws SecurityException, IOException {

        JarInputStream jis = null;
        boolean anySigned = false;
        boolean anyUnsigned = false;
        int moduleVerified = 0;

        int flen = (int) jarName.length();

        progressDialog.setGaugeBounds( ProgressDialog.PARTIAL_GAUGE, 0, flen / 1024 );

        JarFile jf = new JarFile(jarName);
        Manifest man = jf.getManifest();
        
        Enumeration entries = jf.entries();
        LinkedList entriesList = new LinkedList();
        byte[] buffer = new byte[8192];
        
        try {
            while (entries.hasMoreElements()) {
                JarEntry entry = (JarEntry)entries.nextElement();

                // dangerous modules should be updated into install directory
                if ( entry.getName().startsWith( NBM_LIB ) || entry.getName().startsWith( NBM_BIN ))
                    mu.setDepending( true );
                
                if ( entry.getName().startsWith( NBM_MAIN ) )
                    mu.setSafeToInstall( false );

                String jarname = null;
                if ( entry.getName().startsWith( NBM_AUTOLOAD ) && entry.getName().endsWith( JAR_EXT ) ) {
                    jarname = getJarModuleName( entry.getName(), NBM_AUTOLOAD + ENTRY_SEPARATOR );
                    if ( jarname != null )
                        mu.addToJarList( jarname );
                }
                else if ( entry.getName().startsWith( NBM_EAGER ) && entry.getName().endsWith( JAR_EXT ) ) {
                    jarname = getJarModuleName( entry.getName(), NBM_EAGER + ENTRY_SEPARATOR );
                    if ( jarname != null )
                        mu.addToJarList( jarname );
                }
                else if( entry.getName().startsWith( NBM_MODULES ) && entry.getName().endsWith( JAR_EXT ) ) {
                    jarname = getJarModuleName( entry.getName(), NBM_MODULES + ENTRY_SEPARATOR );
                    if ( jarname != null )
                        mu.addToJarList( jarname );
                }
                    
                progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, moduleVerified / 1024 );

                entriesList.add(entry);
                InputStream is = jf.getInputStream(entry);
                try {
                    int n;
                    while ((n = is.read(buffer, 0, buffer.length)) != -1) {
                        // we just read. this will throw a SecurityException
                        // if  a signature/digest check fails.
                        if ( verifyCanceled )
                                return null;
                    }                
                } finally {
                    is.close();
                    totalVerified += entry.getCompressedSize();
                    moduleVerified += entry.getCompressedSize();

                    String mssgTotal = MessageFormat.format( getBundle( "FMT_VerifiedTotal" ),
                                       new Object[] { new Integer( (int)(totalVerified / 1024) ),
                                                      new Integer( verifySize ) } );
                    progressDialog.setGaugeValue( ProgressDialog.OVERALL_GAUGE, (int)(totalVerified / 1024) > verifySize ?
                                                  verifySize : (int)( totalVerified / 1024 ) );
                    progressDialog.setLabelText( ProgressDialog.OVERALL_LABEL, mssgTotal );

                    progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, moduleVerified / 1024 );
                }
            }
            if ( verifyCanceled )
                return null;
        } finally {
            jf.close();
            if ( wizardCanceled )
                Downloader.getNBM( mu ).delete();
        }        

        Set certificates = new HashSet();
        if (man != null) {
            Iterator e = entriesList.iterator();
            while (e.hasNext()) {
                JarEntry je = (JarEntry) e.next();
                String name = je.getName();
                Certificate[] certs = je.getCertificates();
                boolean isSigned = ((certs != null) && (certs.length > 0));
                anySigned |= isSigned;
                if (certs != null) {
                    for(int i = 0; i < certs.length; i++) {
                        certificates.add(certs[i]);
                        if ( verifyCanceled )
                            return null;
                    }
                }
                else { // The entry is not signed
                    if ( !je.isDirectory() && !name.toUpperCase().startsWith( "META-INF/" )   // NOI18N
                            && je.getSize() != 0 ) {
                        anyUnsigned = true;
                    }
                }
            }
        }

        if ( anySigned && anyUnsigned ) {
            throw new SecurityException( getBundle( "EXC_NotSignedEntity" ) );
        }

        progressDialog.setGaugeValue( ProgressDialog.PARTIAL_GAUGE, flen / 1024 + 10);
        return anySigned ? certificates : null;
    }

    public static String formatCerts(Collection collection) {
        StringBuffer sb = new StringBuffer( collection.size() * 300 );


        Iterator it = collection.iterator();
        while(it.hasNext()) {
            Certificate cert = (Certificate)it.next();

            if ( cert instanceof X509Certificate ) {
                try {
                    sb.append( "\n\n" ); // NOI18N
                    sb.append( X509CertToString( (X509Certificate) cert ) );
                }
                catch ( Exception e ) {
                    sb.append( cert.toString() );
                }
            }
            else {
                sb.append( cert.toString() );
            }
            sb.append( "\n\n" ); // NOI18N
        }

        return sb.toString();
    }

    /** Tests whether the cets are trusted
     */
    boolean isTrusted( Collection certs ) {

        Collection trustedCerts = getTrustedCerts();

        if ( trustedCerts.size() <= 0 || certs.size() <= 0 )
            return false;

        return trustedCerts.containsAll( certs );
    }

    /** Adds certificates into keystore
    */

    static void addCertificates( Collection certs )
    throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException {

        KeyStore ks = getKeyStore( Autoupdater.Support.getKSFile (), KS_PSSWD, null);

        Iterator it = certs.iterator();

        while ( it.hasNext() ) {
            Certificate c = (Certificate) it.next();
            
            // don't add certificate twice
            if ( ks.getCertificateAlias( c ) != null )
                continue;

            // Find free alias name
            String alias = null;
            for ( int i = 0; i < 9999; i++ ) {
                alias = "genAlias" + i; // NOI18N
                if ( !ks.containsAlias( alias ) )
                    break;
            }
            if ( alias == null )
                throw new KeyStoreException( getBundle( "EXC_TooManyCertificates" ) );

            ks.setCertificateEntry( alias, c ) ;
        }


        saveKeyStore( ks, Autoupdater.Support.getKSFile (), KS_PSSWD, null);
    }

    /** Removes certificates from the keystore */
    static void removeCertificates( Collection certs )
    throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException {

        KeyStore ks = getKeyStore( Autoupdater.Support.getKSFile (), KS_PSSWD, null);

        Iterator it = certs.iterator();

        while ( it.hasNext() ) {
            Certificate c = (Certificate) it.next();

            String alias = ks.getCertificateAlias( c );

            if ( alias != null )
                ks.deleteEntry( alias );

        }

        saveKeyStore( ks, Autoupdater.Support.getKSFile (), KS_PSSWD, null);

    }


    // Keystore utility methods --------------------------------------------------------


    /** Gets all trusted certificates */
    Collection getTrustedCerts() {

        Collection trustedCerts = new ArrayList( 10 );

        File ksFile = Autoupdater.Support.getKSFile ();

        try {
            if ( ksFile.canRead() ) {
                KeyStore ks = getKeyStore (ksFile, KS_PSSWD, null);
                trustedCerts.addAll ( getCertificates( ks ) );
            }
        }
        // In case of exception let the collection empty
        catch ( CertificateException  e ) {
        }
        catch ( KeyStoreException e ) {
        }
        catch ( IOException e ) {
        }
        catch ( NoSuchAlgorithmException e ) {
        }

        return trustedCerts;
    }

    /** Creates keystore and loads data from file.
    * @param filename - name of the keystore
    * @param storetype - type of the keystore
    * @param password
    */
    private static KeyStore getKeyStore(File file, String password, String storetype)
    throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
        if (file == null) return null;

        InputStream is = null;

        try {
            is = new FileInputStream( file );
        }
        catch ( IOException e ) {
            // Do nothing leaving is null creates empty key store
        }

        KeyStore keyStore = null;

        if ( storetype == null ) {
            keyStore = KeyStore.getInstance( KeyStore.getDefaultType() );
            keyStore.load( is, password.toCharArray() );
            if ( is != null )
                is.close();
        }

        return keyStore;
    }

    /** Creates keystore and loads data from file.
    * @param keyStore - keystore
    * @param filename - name of the keystore
    * @param storetype - type of the keystore
    * @param password
    */
    public static void saveKeyStore(KeyStore keyStore, File file, String password, String storetype)
    throws CertificateException, KeyStoreException, IOException, NoSuchAlgorithmException {
        
        if (file == null) return ;
        
        OutputStream os = new FileOutputStream( file );
        keyStore.store(os, password.toCharArray( ) );
        os.close();
    }

    /** Return all certificates in system ( Form central and user's directory )
    */
    public static Collection getCertificates(KeyStore keyStore) throws KeyStoreException {

        List certificates = new ArrayList( 10 );

        Enumeration en = keyStore.aliases();
        while (en.hasMoreElements()) {
            String alias = (String)en.nextElement();
            Certificate cert = keyStore.getCertificate(alias);
            certificates.add(cert);
        }
        return certificates;
    }

    void cancelVerify(boolean wizardCanceled) {
        verifyCanceled = true;
        this.wizardCanceled = wizardCanceled;
    }


    /** Prints a X509 certificate in a human readable format.
    */
    private static String X509CertToString(X509Certificate cert )
    throws Exception
    {
        return getBundle("MSG_Owner") + SPACE + (cert.getSubjectDN()) + NEW_LINE +
               getBundle("MSG_Issuer") + SPACE + (cert.getIssuerDN()) + NEW_LINE +
               getBundle("MSG_SerNumber") + SPACE + cert.getSerialNumber().toString(16) + NEW_LINE +
               getBundle("MSG_Valid") + SPACE + cert.getNotBefore().toString() +
               SPACE + getBundle("MSG_Until") + SPACE + cert.getNotAfter().toString() + NEW_LINE +
               getBundle("MSG_CertFinger") + NEW_LINE +
               SPACE + TAB + getBundle("MSG_MD5") + SPACE + SPACE + getCertFingerPrint("MD5", cert) + NEW_LINE +
               SPACE + TAB + getBundle("MSG_SHA1") + SPACE + getCertFingerPrint("SHA1", cert);
    }

    /** Gets the requested finger print of the certificate.
    */
    private static String getCertFingerPrint(String mdAlg, Certificate cert)
    throws Exception
    {
        byte[] encCertInfo = cert.getEncoded();
        MessageDigest md = MessageDigest.getInstance(mdAlg);
        byte[] digest = md.digest(encCertInfo);
        return toHexString(digest);
    }

    /**Converts a byte array to hex string
     */
    private static String toHexString(byte[] block) {
        StringBuffer buf = new StringBuffer();
        int len = block.length;
        for (int i = 0; i < len; i++) {
            byte2hex(block[i], buf);
            if (i < len-1) {
                buf.append(":"); // NOI18N
            }
        }
        return buf.toString();
    }


    /** Converts a byte to hex digit and writes to the supplied buffer
    */
    private static void byte2hex(byte b, StringBuffer buf) {
        char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8',
                            '9', 'A', 'B', 'C', 'D', 'E', 'F' };
        int high = ((b & 0xf0) >> 4);
        int low = (b & 0x0f);
        buf.append(hexChars[high]);
        buf.append(hexChars[low]);
    }
    
    private String getJarModuleName( String name, String prefix ) {
        if ( name.substring( prefix.length() ).indexOf( ENTRY_SEPARATOR ) == -1 ) {
            String common = NBM_MODULES + ENTRY_SEPARATOR;
            return name.substring( common.length() );
        }
        return null;
    }

    private static String getBundle( String key ) {
        return NbBundle.getMessage( SignVerifier.class, key );
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.