View Javadoc

1   /*
2    * jGuard is a security framework based on top of jaas (java authentication and authorization security). it is written
3    * for web applications, to resolve simply, access control problems. version $Name$
4    * http://sourceforge.net/projects/jguard/
5    *
6    * Copyright (C) 2004 Charles GAY
7    *
8    * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
9    * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
10   * any later version.
11   *
12   * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
13   * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
17   * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18   *
19   *
20   * jGuard project home page: http://sourceforge.net/projects/jguard/
21   *
22   */
23  package net.sf.jguard.core.util;
24  
25  import java.security.MessageDigest;
26  import java.security.NoSuchAlgorithmException;
27  import java.security.Security;
28  import java.util.Iterator;
29  import java.util.Set;
30  import java.util.concurrent.ArrayBlockingQueue;
31  
32  /**
33   * this class is done originally by andy tagish, with his great jaas modules. you can reach it <a
34   * href="http://free.tagish.net/jaas/index.jsp">on his website</a> the licence of the code provided by andy tagish is
35   * also the LGPL. this class is loaded by the application server classloader.
36   *
37   * @author <a href="mailto:andy@tagish.com">Andy Armstrong</a>
38   * @author <a href="mailto:diabolo512@users.sourceforge.net">Charles Gay</a>
39   * @author Lars Feistner
40   */
41  public class CryptUtils
42  {
43  
44     private final static int MAX_POOL_SIZE = 10;
45     private static ArrayBlockingQueue<MessageDigest> messageDigestPool = new ArrayBlockingQueue<MessageDigest>(MAX_POOL_SIZE);
46  
47     // by default, the NONE digestAlgorithm is provided
48     public final static String NONE_ALGORITHM = "NONE";
49     private static String digestAlgorithm = NONE_ALGORITHM;
50  
51     public static char[] salt = null;
52  
53     /**
54      * Turn a byte array into a char array containing a printable hex representation of the bytes. Each byte in the
55      * source array contributes a pair of hex digits to the output array.
56      *
57      * @param src the source array
58      * @return a char array containing a printable version of the source data
59      */
60     private static char[] hexDump( byte src[] ) {
61         char buf[] = new char[src.length * 2];
62         for ( int b = 0; b < src.length; b++ ) {
63             String byt = Integer.toHexString( src[b] & 0xFF );
64             if ( byt.length() < 2 ) {
65                 buf[b * 2 + 0] = '0';
66                 buf[b * 2 + 1] = byt.charAt( 0 );
67             } else {
68                 buf[b * 2 + 0] = byt.charAt( 0 );
69                 buf[b * 2 + 1] = byt.charAt( 1 );
70             }
71         }
72         return buf;
73     }
74  
75     /**
76      * Zero the contents of the specified array. Typically used to erase temporary storage that has held plaintext
77      * passwords so that we don't leave them lying around in memory.
78      *
79      * @param pwd the array to zero
80      */
81     public static void smudge( char pwd[] ) {
82         if ( null != pwd ) {
83             for ( int b = 0; b < pwd.length; b++ ) {
84                 pwd[b] = 0;
85             }
86         }
87     }
88  
89     /**
90      * Zero the contents of the specified array.
91      *
92      * @param pwd the array to zero
93      */
94     public static void smudge( byte pwd[] ) {
95         if ( null != pwd ) {
96             for ( int b = 0; b < pwd.length; b++ ) {
97                 pwd[b] = 0;
98             }
99         }
100    }
101 
102    /**
103     * Perform message digest hashing on the supplied password and return a char array containing the encrypted password
104     * as a printable string. The hash is computed on the low 8 bits of each character.
105     *
106     * @param pwd The password to encrypt
107     * @return a character array containing a 32 character long hex encoded MD5 hash of the password
108     * @throws NoSuchAlgorithmException
109     */
110    public static char[] cryptPassword( char[] pwd )
111        throws NoSuchAlgorithmException
112    {
113 
114        char[] newPwd = null;
115 
116        if ( salt != null ) {
117            newPwd = saltPassword( pwd );
118        } else {
119            newPwd = pwd;
120        }
121 
122        // if no algorithm is set, we don't crypt the char array
123        if ( digestAlgorithm.equals( NONE_ALGORITHM ) ) {
124            return newPwd;
125        }
126 
127        // messageDigest algorithm initialization
128        MessageDigest messageDigest = null;
129        char crypt[] = null;
130        try {
131            messageDigest = messageDigestPool.take();
132            messageDigest.reset();
133 
134            // transform char array into byte array
135            byte pwdb[] = new byte[newPwd.length];
136 
137            for ( int b = 0; b < newPwd.length; b++ ) {
138                pwdb[b] = (byte)newPwd[b];
139            }
140 
141            // char crypt[] = hexDump(md.digest(pwdb));
142            crypt = hexDump( messageDigest.digest( pwdb ) );
143            smudge( pwdb );
144        } catch ( InterruptedException ex ) {
145            // should never happen, only if application is shutting down
146            throw new IllegalStateException( ex );
147        } finally {
148            messageDigestPool.offer(messageDigest);
149        }
150 
151        return crypt;
152    }
153 
154    private static char[] saltPassword( char[] pwd ) {
155        // merge password and salt
156        char[] merged = new char[pwd.length + salt.length];
157        int mergedIndex = 0;
158        for ( int i = 0; i < pwd.length; i++ ) {
159            merged[mergedIndex] = pwd[i];
160            mergedIndex++;
161        }
162        for ( int j = 0; j < salt.length; j++ ) {
163            merged[mergedIndex] = salt[j];
164            mergedIndex++;
165        }
166        return merged;
167    }
168 
169    /**
170     * @return digestAlgorithm
171     */
172    public static String getDigestAlgorithm() {
173        return digestAlgorithm;
174    }
175 
176    /**
177     * set the message digest algorithm. before to set, we verify if the algorithm is available with the current jvm.
178     *
179     * @param algorithm - digestAlgorithm
180     * @throws NoSuchAlgorithmException
181     */
182    public static void setDigestAlgorithm(String algorithm)
183        throws NoSuchAlgorithmException
184    {
185        if ( NONE_ALGORITHM.equals( algorithm ) ) {
186            digestAlgorithm = algorithm;
187            return;
188        }
189        Set algorithmsSet = Security.getAlgorithms( "MessageDigest" );
190        if ( algorithmsSet.size() < 1 ) {
191            throw new NoSuchAlgorithmException( "no Message Digest algorithms implemented in this jvm " );
192        }
193        Iterator it = algorithmsSet.iterator();
194        boolean algorithmImplemented = false;
195        String algorithmTemp;
196 
197        short count = 0;
198        while ( it.hasNext() ) {
199            count++;
200            algorithmTemp = (String)it.next();
201            if ( algorithmTemp.equalsIgnoreCase( algorithm ) ) {
202                algorithmImplemented = true;
203                break;
204            }
205        }
206 
207        if ( algorithmImplemented == true ) {
208            digestAlgorithm = algorithm;
209        } else {
210            throw new NoSuchAlgorithmException( "Message Digest algorithm '" + algorithm + "' not implemented " );
211        }
212        initMessageDigestPool();
213    }
214 
215    private static void initMessageDigestPool()
216        throws NoSuchAlgorithmException
217    {
218        for ( int i = 0; i < MAX_POOL_SIZE; i++ ) {
219            messageDigestPool.add( MessageDigest.getInstance(digestAlgorithm));
220        }
221    }
222 
223    /**
224     * define 'salt' for a better security. it protects against <a
225     * href="http://en.wikipedia.org/wiki/Rainbow_table">'rainbow tables'</a>.
226     *
227     * @param saltCandidate
228     * @return
229     */
230    public static boolean setSalt( char[] saltCandidate ) {
231        if ( salt != null && saltCandidate != null && !saltCandidate.equals( "" ) ) {
232            return false;
233        }
234        CryptUtils.salt = saltCandidate;
235        return true;
236 
237    }
238 
239    public static char[] getSalt() {
240        return salt;
241    }
242 
243 }