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 }