| package org.apache.fulcrum.jce.crypto; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.io.UnsupportedEncodingException; |
| import java.security.MessageDigest; |
| import java.security.NoSuchAlgorithmException; |
| |
| /** |
| * The implementation supplies a default password in the case that |
| * the programmer don't want to have additional hassles. It is easy to |
| * reengineer the password being used but much better than a hard-coded |
| * password in the application. |
| * |
| * The code uses parts from Markus Hahn's Blowfish library found at |
| * http://blowfishj.sourceforge.net/ |
| * |
| * @author <a href="mailto:siegfried.goeschl@it20one.at">Siegfried Goeschl </a> |
| * @author <a href="mailto:maakus@earthlink.net">Markus Hahn</a> |
| */ |
| |
| public class PasswordFactory implements PasswordParameters |
| { |
| |
| private static PasswordFactory instance; |
| |
| String algo; |
| |
| int count = PasswordParameters.COUNT; |
| |
| public PasswordFactory(String algo) { |
| this.algo = algo; |
| } |
| |
| public PasswordFactory(String algo, int count) { |
| this.algo = algo; |
| this.count = count; |
| } |
| |
| /** |
| * Factory method to get a default instance |
| * @return an instance of the CryptoStreamFactory |
| */ |
| public synchronized static PasswordFactory getInstance() |
| { |
| if( PasswordFactory.instance == null ) |
| { |
| PasswordFactory.instance = new PasswordFactory("SHA1"); |
| } |
| |
| return PasswordFactory.instance; |
| } |
| |
| /** |
| * Factory method to get a default instance |
| * |
| * @param algo algorithm |
| * @return an instance of the CryptoStreamFactory |
| */ |
| public synchronized static PasswordFactory getInstance(String algo) |
| { |
| if( PasswordFactory.instance == null ) |
| { |
| PasswordFactory.instance = new PasswordFactory(algo); |
| } |
| |
| return PasswordFactory.instance; |
| } |
| |
| /** |
| * Factory method to get a default instance |
| * |
| * @param algo algorithm |
| * @param count the number of MessageDigest iterations |
| * @return an instance of the CryptoStreamFactory |
| */ |
| public synchronized static PasswordFactory getInstance(String algo, int count) |
| { |
| if( PasswordFactory.instance == null ) |
| { |
| PasswordFactory.instance = new PasswordFactory(algo, count); |
| } |
| |
| return PasswordFactory.instance; |
| } |
| |
| /** |
| * Create a new password |
| * |
| * @return a default password using "xxxx-xxxx-xxxx-xxxxx" |
| * |
| * @throws NoSuchAlgorithmException the encryption algorithm is not supported |
| * @throws UnsupportedEncodingException the requested encoding is not supported |
| */ |
| public char[] create() |
| throws NoSuchAlgorithmException, UnsupportedEncodingException |
| { |
| return create( |
| PasswordParameters.DefaultPassword(), |
| PasswordParameters.Salt(), |
| count |
| ); |
| } |
| |
| /** |
| * Create a new password using a seed |
| * |
| * @param seed the default password supplied by the caller |
| * @return a password using "xxxx-xxxx-xxxx-xxxxx" |
| * |
| * @throws NoSuchAlgorithmException the encryption algorithm is not supported |
| * @throws UnsupportedEncodingException the requested encoding is not supported |
| */ |
| public char[] create( String seed ) |
| throws NoSuchAlgorithmException, UnsupportedEncodingException |
| { |
| return create( |
| seed.toCharArray() |
| ); |
| } |
| |
| /** |
| * @param seed the default password supplied by the caller |
| * @return a password using "xxxx-xxxx-xxxx-xxxxx" |
| * @throws NoSuchAlgorithmException the encryption algorithm is not supported |
| * @throws UnsupportedEncodingException the requested encoding is not supported |
| */ |
| public final char[] create( char[] seed ) |
| throws NoSuchAlgorithmException, UnsupportedEncodingException |
| { |
| return create( |
| seed, |
| PasswordParameters.Salt(), |
| count |
| ); |
| } |
| |
| /** |
| * Creates a default password using "xxxx-xxxx-xxxx-xxxxx". |
| * |
| * @param salt the password salt |
| * @param password the default password |
| * @param count number of MessageDigest iterations |
| * @return the default password |
| * @throws NoSuchAlgorithmException the encryption algorithm is not supported |
| * @throws UnsupportedEncodingException the requested encoding is not supported |
| */ |
| public char [] create( char[] password, byte[] salt, int count ) |
| throws NoSuchAlgorithmException, UnsupportedEncodingException |
| { |
| char [] result = null; |
| MessageDigest sha1 = MessageDigest.getInstance( algo ); |
| byte [] passwordMask = new String( password ).getBytes( "UTF-8" ); |
| byte [] temp = new byte[salt.length + passwordMask.length]; |
| byte [] digest = null; |
| |
| StringBuilder stringBuffer = new StringBuilder(); |
| |
| // combine the password with the salt string into a byte[9 |
| |
| System.arraycopy( passwordMask, 0, temp, 0, passwordMask.length ); |
| System.arraycopy( salt, 0, temp, passwordMask.length, salt.length ); |
| |
| // create a hash over and over to make it a bit random |
| |
| digest = temp; |
| |
| for (int i = 0; i < count; i++) |
| { |
| sha1.update( digest ); |
| digest = sha1.digest(); |
| } |
| |
| // build a well-formed password string to be usable |
| // by a human |
| |
| long long1 = createLong( digest, 0 ); |
| long long2 = createLong( digest, 4 ); |
| long long3 = createLong( digest, 8 ); |
| long long4 = createLong( digest, 12 ); |
| |
| stringBuffer.append( Long.toHexString( long1 ).substring( 0, 4 ) ); |
| stringBuffer.append( '-' ); |
| stringBuffer.append( Long.toHexString( long2 ).substring( 0, 4 ) ); |
| stringBuffer.append( '-' ); |
| stringBuffer.append( Long.toHexString( long3 ).substring( 0, 4 ) ); |
| stringBuffer.append( '-' ); |
| stringBuffer.append( Long.toHexString( long4 ).substring( 0, 5 ) ); |
| |
| // copy the password |
| result = new char[stringBuffer.length()]; |
| |
| for (int i = 0; i < stringBuffer.length(); i++) |
| { |
| result[i] = stringBuffer.charAt( i ); |
| } |
| |
| // wipe out the StringBuilder |
| for (int i = 0; i < stringBuffer.length(); i++) |
| { |
| stringBuffer.setCharAt( i, ' ' ); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Gets bytes from an array into a long. |
| * |
| * @param buf where to get the bytes |
| * @param nOfs index from where to read the data |
| * @return the 64bit integer |
| */ |
| private static long createLong(byte [] buf, int nOfs) |
| { |
| return |
| ((long)(( buf[nOfs ] << 24) | |
| ((buf[nOfs + 1] & 0x0ff) << 16) | |
| ((buf[nOfs + 2] & 0x0ff) << 8) | |
| ( buf[nOfs + 3] & 0x0ff )) << 32) | |
| ((long)(( buf[nOfs + 4] << 24) | |
| ((buf[nOfs + 5] & 0x0ff) << 16) | |
| ((buf[nOfs + 6] & 0x0ff) << 8) | |
| ( buf[nOfs + 7] & 0x0ff )) & 0x0ffffffffL); |
| } |
| } |