001    package org.apache.fulcrum.jce.crypto;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *   http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.UnsupportedEncodingException;
023    import java.security.MessageDigest;
024    import java.security.NoSuchAlgorithmException;
025    
026    /**
027     * The implementation supplies a default password in the case that
028     * the programmer don't want to have additional hassles. It is easy to
029     * reengineer the password being used but much better than a hard-coded
030     * password in the application.
031     *
032     * The code uses parts from Markus Hahn's Blowfish library found at
033     * http://blowfishj.sourceforge.net/
034     *
035     * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
036     * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
037     */
038    
039    public class PasswordFactory implements PasswordParameters
040    {
041    
042        /**
043         * @return a default password using "xxxx-xxxx-xxxx-xxxxx"
044         */
045        public static char[] create()
046            throws NoSuchAlgorithmException, UnsupportedEncodingException
047        {
048            return create(
049                PasswordParameters.DEFAULTPASSWORD,
050                PasswordParameters.SALT,
051                PasswordParameters.COUNT
052                );
053        }
054    
055        /**
056         * @param seed the default password supplied by the caller
057         * @return a password using "xxxx-xxxx-xxxx-xxxxx"
058         */
059        public static char[] create( String seed )
060            throws NoSuchAlgorithmException, UnsupportedEncodingException
061        {
062            return create(
063                seed.toCharArray()
064                );
065        }
066    
067        /**
068         * @param seed the default password supplied by the caller
069         * @return a password using "xxxx-xxxx-xxxx-xxxxx"
070         */
071        public static final char[] create( char[] seed )
072            throws NoSuchAlgorithmException, UnsupportedEncodingException
073        {
074            return create(
075                seed,
076                PasswordFactory.SALT,
077                PasswordFactory.COUNT
078                );
079        }
080    
081        /**
082         * Creates a default password using "xxxx-xxxx-xxxx-xxxxx".
083         *
084         * @param salt the password salt
085         * @param password the default password
086         * @param count number of MessageDigest iterations
087         * @return the default password
088         * @throws NoSuchAlgorithmException the encryption algorithm is not supported
089         * @throws UnsupportedEncodingException the requested encoding is not supported
090         */
091        public static char [] create( char[] password, byte[] salt, int count )
092            throws NoSuchAlgorithmException, UnsupportedEncodingException
093        {
094            char [] result = null;
095            MessageDigest sha1 = MessageDigest.getInstance( "SHA1" );
096            byte [] passwordMask = new String( password ).getBytes( "UTF-8" );
097            byte [] temp = new byte[salt.length + passwordMask.length];
098            byte [] digest = null;
099    
100            StringBuffer stringBuffer = new StringBuffer();
101    
102            // combine the password with the salt string into a byte[9
103    
104            System.arraycopy( passwordMask, 0, temp, 0, passwordMask.length );
105            System.arraycopy( salt, 0, temp, passwordMask.length, salt.length );
106    
107            // create a hash over and over to make it a bit random
108    
109            digest = temp;
110    
111            for (int i = 0; i < count; i++)
112            {
113                sha1.update( digest );
114                digest = sha1.digest();
115            }
116    
117            // build a well-formed password string to be usable
118            // by a human
119    
120            long long1 = createLong( digest, 0 );
121            long long2 = createLong( digest, 4 );
122            long long3 = createLong( digest, 8 );
123            long long4 = createLong( digest, 12 );
124    
125            stringBuffer.append( Long.toHexString( long1 ).substring( 0, 4 ) );
126            stringBuffer.append( '-' );
127            stringBuffer.append( Long.toHexString( long2 ).substring( 0, 4 ) );
128            stringBuffer.append( '-' );
129            stringBuffer.append( Long.toHexString( long3 ).substring( 0, 4 ) );
130            stringBuffer.append( '-' );
131            stringBuffer.append( Long.toHexString( long4 ).substring( 0, 5 ) );
132    
133            // copy the password
134    
135            result = new char[stringBuffer.length()];
136    
137            for (int i = 0; i < stringBuffer.length(); i++)
138            {
139                result[i] = stringBuffer.charAt( i );
140            }
141    
142            // wipe out the StringBuffer
143    
144            for (int i = 0; i < stringBuffer.length(); i++)
145            {
146                stringBuffer.setCharAt( i, ' ' );
147            }
148    
149            return result;
150        }
151    
152        /**
153         * Gets bytes from an array into a long.
154         *
155         * @param buf where to get the bytes
156         * @param nOfs index from where to read the data
157         * @return the 64bit integer
158         */
159        private static long createLong(byte [] buf, int nOfs)
160        {
161            return
162                ((long)(( buf[nOfs    ]          << 24) |
163                        ((buf[nOfs + 1] & 0x0ff) << 16) |
164                        ((buf[nOfs + 2] & 0x0ff) <<  8) |
165                        ( buf[nOfs + 3] & 0x0ff       )) << 32) |
166                ((long)(( buf[nOfs + 4]          << 24) |
167                        ((buf[nOfs + 5] & 0x0ff) << 16) |
168                        ((buf[nOfs + 6] & 0x0ff) <<  8) |
169                        ( buf[nOfs + 7] & 0x0ff       )) & 0x0ffffffffL);
170        }
171    }