View Javadoc
1   package org.apache.fulcrum.jce.crypto;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.UnsupportedEncodingException;
23  import java.security.MessageDigest;
24  import java.security.NoSuchAlgorithmException;
25  
26  /**
27   * The implementation supplies a default password in the case that
28   * the programmer don't want to have additional hassles. It is easy to
29   * reengineer the password being used but much better than a hard-coded
30   * password in the application.
31   *
32   * The code uses parts from Markus Hahn's Blowfish library found at
33   * http://blowfishj.sourceforge.net/
34   *
35   * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a>
36   * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a>
37   */
38  
39  public class PasswordFactory implements PasswordParameters
40  {
41  
42      private static PasswordFactory instance;
43      
44      String algo;
45      
46      int count = PasswordParameters.COUNT;
47      
48      public PasswordFactory(String algo) {
49         this.algo = algo;
50      }
51      
52      public PasswordFactory(String algo, int count) {
53          this.algo = algo;
54          this.count = count;
55       }
56        
57      /**
58       * Factory method to get a default instance
59       * @return an instance of the CryptoStreamFactory
60       */
61      public synchronized static PasswordFactory getInstance() 
62      {
63          if( PasswordFactory.instance == null )
64          {
65             PasswordFactory.instance = new PasswordFactory("SHA1");
66          }
67  
68          return PasswordFactory.instance;
69      }
70      
71      /**
72       * Factory method to get a default instance
73       * 
74       * @param algo algorithm
75       * @return an instance of the CryptoStreamFactory
76       */
77      public synchronized static PasswordFactory getInstance(String algo) 
78      {
79          if( PasswordFactory.instance == null )
80          {
81             PasswordFactory.instance = new PasswordFactory(algo);
82          }
83  
84          return PasswordFactory.instance;
85      }
86      
87      /**
88       * Factory method to get a default instance
89       * 
90       * @param algo algorithm
91       * @param count the number of MessageDigest iterations
92       * @return an instance of the CryptoStreamFactory
93       */
94      public synchronized static PasswordFactory getInstance(String algo, int count) 
95      {
96          if( PasswordFactory.instance == null )
97          {
98             PasswordFactory.instance = new PasswordFactory(algo, count);
99          }
100 
101         return PasswordFactory.instance;
102     }
103     
104     /**
105      * Create a new password
106      * 
107      * @return a default password using "xxxx-xxxx-xxxx-xxxxx"
108      * 
109      * @throws NoSuchAlgorithmException the encryption algorithm is not supported
110      * @throws UnsupportedEncodingException the requested encoding is not supported
111      */
112     public char[] create()
113         throws NoSuchAlgorithmException, UnsupportedEncodingException
114     {
115         return create(
116             PasswordParameters.DefaultPassword(),
117             PasswordParameters.Salt(),
118             count
119             );
120     }
121 
122     /**
123      * Create a new password using a seed
124      * 
125      * @param seed the default password supplied by the caller
126      * @return a password using "xxxx-xxxx-xxxx-xxxxx"
127      * 
128      * @throws NoSuchAlgorithmException the encryption algorithm is not supported
129      * @throws UnsupportedEncodingException the requested encoding is not supported
130      */
131     public char[] create( String seed )
132         throws NoSuchAlgorithmException, UnsupportedEncodingException
133     {
134         return create(
135             seed.toCharArray()
136             );
137     }
138 
139     /**
140      * @param seed the default password supplied by the caller
141      * @return a password using "xxxx-xxxx-xxxx-xxxxx"
142      * @throws NoSuchAlgorithmException the encryption algorithm is not supported
143      * @throws UnsupportedEncodingException the requested encoding is not supported
144      */
145     public final char[] create( char[] seed )
146         throws NoSuchAlgorithmException, UnsupportedEncodingException
147     {
148         return create(
149             seed,
150             PasswordParameters.Salt(),
151             count
152             );
153     }
154 
155     /**
156      * Creates a default password using "xxxx-xxxx-xxxx-xxxxx".
157      *
158      * @param salt the password salt
159      * @param password the default password
160      * @param count number of MessageDigest iterations
161      * @return the default password
162      * @throws NoSuchAlgorithmException the encryption algorithm is not supported
163      * @throws UnsupportedEncodingException the requested encoding is not supported
164      */
165     public char [] create( char[] password, byte[] salt, int count )
166         throws NoSuchAlgorithmException, UnsupportedEncodingException
167     {
168         char [] result = null;
169         MessageDigest sha1 = MessageDigest.getInstance( algo );
170         byte [] passwordMask = new String( password ).getBytes( "UTF-8" );
171         byte [] temp = new byte[salt.length + passwordMask.length];
172         byte [] digest = null;
173 
174         StringBuilder stringBuffer = new StringBuilder();
175 
176         // combine the password with the salt string into a byte[9
177 
178         System.arraycopy( passwordMask, 0, temp, 0, passwordMask.length );
179         System.arraycopy( salt, 0, temp, passwordMask.length, salt.length );
180 
181         // create a hash over and over to make it a bit random
182 
183         digest = temp;
184 
185         for (int i = 0; i < count; i++)
186         {
187             sha1.update( digest );
188             digest = sha1.digest();
189         }
190 
191         // build a well-formed password string to be usable
192         // by a human
193 
194         long long1 = createLong( digest, 0 );
195         long long2 = createLong( digest, 4 );
196         long long3 = createLong( digest, 8 );
197         long long4 = createLong( digest, 12 );
198 
199         stringBuffer.append( Long.toHexString( long1 ).substring( 0, 4 ) );
200         stringBuffer.append( '-' );
201         stringBuffer.append( Long.toHexString( long2 ).substring( 0, 4 ) );
202         stringBuffer.append( '-' );
203         stringBuffer.append( Long.toHexString( long3 ).substring( 0, 4 ) );
204         stringBuffer.append( '-' );
205         stringBuffer.append( Long.toHexString( long4 ).substring( 0, 5 ) );
206 
207         // copy the password
208         result = new char[stringBuffer.length()];
209 
210         for (int i = 0; i < stringBuffer.length(); i++)
211         {
212             result[i] = stringBuffer.charAt( i );
213         }
214 
215         // wipe out the StringBuilder
216         for (int i = 0; i < stringBuffer.length(); i++)
217         {
218             stringBuffer.setCharAt( i, ' ' );
219         }
220 
221         return result;
222     }
223 
224     /**
225      * Gets bytes from an array into a long.
226      *
227      * @param buf where to get the bytes
228      * @param nOfs index from where to read the data
229      * @return the 64bit integer
230      */
231     private static long createLong(byte [] buf, int nOfs)
232     {
233         return
234             ((long)(( buf[nOfs    ]          << 24) |
235                     ((buf[nOfs + 1] & 0x0ff) << 16) |
236                     ((buf[nOfs + 2] & 0x0ff) <<  8) |
237                     ( buf[nOfs + 3] & 0x0ff       )) << 32) |
238             ((long)(( buf[nOfs + 4]          << 24) |
239                     ((buf[nOfs + 5] & 0x0ff) << 16) |
240                     ((buf[nOfs + 6] & 0x0ff) <<  8) |
241                     ( buf[nOfs + 7] & 0x0ff       )) & 0x0ffffffffL);
242     }
243 }