package de.axone.tools;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.Random;
import java.util.Set;
import java.util.TreeSet;
import de.axone.exception.Assert;
public class PasswordBuilder {
public static final int DEFAULT_LENGTH = 12;
public static final int DEFAULT_ROUNDS_EXP = 12; // <4096 rounds
public static final String DEFAULT_ALGO = "SHA-256";
public static final String DEFAULT_PROVIDER = "SUN";
public static final String HASH_SPLIT = "\\$";
/*
private static final char [] allowedCharsDENIC =
"aAbBcCdDeEfFgGhHijJkKLmMnNpPqQrRsStTuUvVwWxXyYzZ23456789+-/".toCharArray();
*/
private static final char [] allowedCharsLowerCase =
"abcdefghijkmnopqrstuvwxyz".toCharArray();
private static final char [] allowedCharsUpperCase =
"ABCDEFGHJKLMNPQRSTUVWXYZ".toCharArray();
private static final char [] allowedCharsNumbers =
"23456789".toCharArray();
private static final char [] allowedCharsSpecial =
"!$*#+-=%()?/&@".toCharArray();
private static void addAll( Set<Character> set, char[] toAdd ){
for( char c : toAdd ) set.add( c );
}
private static char[] makeValues( boolean humanize, boolean includeUpperCase, boolean includeLowerCase, boolean includeNumbers, boolean includeSpecial ){
int c=0;
if( includeUpperCase ) c++;
if( includeLowerCase ) c++;
if( includeNumbers ) c++;
if( includeSpecial ) c++;
if( c == 0 ) throw new IllegalArgumentException( "At least spzify one arg as true" );
Set<Character> result = new TreeSet<Character>();
if( includeLowerCase ){
if( ! humanize || (! includeUpperCase && !includeNumbers ) ){
result.add( 'l' );
}
addAll( result, allowedCharsLowerCase );
}
if( includeUpperCase ){
if( ! humanize || (! includeLowerCase && !includeNumbers ) ){
result.add( 'I' );
}
if( !includeNumbers ){
result.add( 'O' );
}
addAll( result, allowedCharsUpperCase );
}
if( includeNumbers ){
if( !humanize || !includeUpperCase ){
result.add( '0' );
}
if( ! humanize || ( !includeUpperCase && !includeLowerCase ) ){
result.add( '1' );
}
addAll( result, allowedCharsNumbers );
}
if( includeSpecial ){
addAll( result, allowedCharsSpecial );
}
//Character [] resultArray = new Character[ result.size() ];
char [] resultArray = new char[ result.size() ];
int i=0;
for( Character rc : result ){
resultArray[ i++ ] = rc.charValue();
}
return resultArray;
}
public static void main( String [] args ) throws Exception {
int i=0;
try( BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) ) ){
String line;
while( ( line = in.readLine() ) != null ){
i++;
String [] parts = line.split( "\\s+", 2 );
Assert.equal( parts.length, "parts", 2 );
String id = parts[0];
String pass = parts[1];
System.out.printf( "UPDATE user SET password='%s' where id=%s;\n",
PasswordBuilder.hashPassword( pass ), id );
}
} catch( Exception e ){
E.rr( "In line: " + i );
throw e;
}
/*
int len = DEFAULT_LENGTH;
if( args.length == 1 ){
len = Integer.parseInt( args[0] );
}
String plain = makePasswd( len );
String salt = makeSalt( DEFAULT_LENGTH );
String hashed = hashPassword( plain, "SHA-1", salt, DEFAULT_ROUNDS_EXP );
boolean ok = checkPassword( plain, hashed );
System.out.printf( "%s: %s -> %s\n", ok?"OK":"ERR", plain, hashed );
*/
/*
for( int i=0; i<10; i++ )
E.rr( makeSimplaPasswd() );
*/
}
/**
* Make a simple to type password
*
* The password is in the form of aaaa1111
*
* @return the generated password
*/
public static String makeSimplaPasswd(){
return makePasswd( 4, true, false, true, false, false ) +
makePasswd( 4, true, false, false, true, false );
}
/**
* Make a password with usefull settings
*
* uses DEFAULT_LENGTH as length. Currently 12 chars.
* humanized, including upper and lower case, numbers but no special chars
*
* @return the generated password
*/
public static String makePasswd(){
return makePasswd( DEFAULT_LENGTH );
}
/**
* Make a password.
*
* humanized, including upper and lower case, numbers but no special chars
*
* @param length
* @return the generated password
*/
public static String makePasswd( int length ){
return makePasswd( length, true, true, true, true, false );
}
/**
*
* Make a password.
*
* including upper and lower case, numbers and no special chars
*
* @param length
* @param humanize use only unproblematic characters
* @return the generated password
*/
public static String makePasswd( int length, boolean humanize ){
return makePasswd( length, humanize, true, true, true, false );
}
/**
* Make a password.
*
* @param length length in chars
* @param humanize use only characters which are unproblematic for humans
* @param includeUpperCase include upper case letters
* @param includeLowerCase include lower case letters
* @param includeNumbers include numbers
* @param includeSpecial include special chars
* @return the generated password
*/
public static String makePasswd( int length, boolean humanize, boolean includeUpperCase, boolean includeLowerCase, boolean includeNumbers, boolean includeSpecial ){
char [] allowedChars = makeValues( humanize, includeUpperCase, includeLowerCase, includeNumbers, includeSpecial );
return makePasswd( allowedChars, length );
}
public static String makePasswd( char [] vocabulary, int length ){
Random r=null;
try {
r = SecureRandom.getInstance( "SHA1PRNG", "SUN" );
} catch( GeneralSecurityException e ) {
e.printStackTrace();
}
// Fallback if something is wrong with Java sec. Should never happen
if( r == null ){
r = new Random();
}
StringBuilder result = new StringBuilder( length );
int chars = vocabulary.length;
int idx;
for( int i = 0; i < length; i++ ){
//idx = (int)( r.nextFloat() * chars );
idx = r.nextInt( chars );
result.append( vocabulary[ idx ] );
}
return result.toString();
}
private static String makeSalt( int chars ){
return makePasswd( chars, false, true, true, true, false );
}
public static boolean checkPassword( String plain, String hashed ) {
Assert.notNull( hashed, "hashed" );
String parts[] = hashed.split( HASH_SPLIT );
if( parts.length == 1 ){
return hashed.equals( plain );
} else {
Assert.inRange( parts.length, "parts", 4, 4 );
String algo = parts[0];
int rounds = Integer.parseInt( parts[1] );
String salt = parts[2];
try {
String ref = hashPassword( plain, algo, salt, rounds );
return hashed.equals( ref );
} catch( NoSuchAlgorithmException e ) {
throw new IllegalArgumentException( "Algo: '" + algo + "' is unknown for hashing" );
}
}
}
public static String hashPassword( String plain, String algo, String salt, int roundsExp )
throws NoSuchAlgorithmException {
try {
MessageDigest digest = MessageDigest.getInstance( algo, DEFAULT_PROVIDER );
digest.update( salt.getBytes() );
digest.update( plain.getBytes() );
byte [] hashed = digest.digest();
int rounds = 1<<roundsExp;
for( int i=0; i < rounds; i++ ){
hashed = digest.digest( hashed );
}
String readable = Base64ApacheCommons.EncodeURLSafe( hashed );
return algo + "$" + roundsExp + "$" + salt + "$" + readable.trim();
} catch( NoSuchProviderException e ) {
throw new Error( "Unknown Provider: " + DEFAULT_PROVIDER );
}
}
public static String hashPassword( String plain, String algo )
throws NoSuchAlgorithmException {
return hashPassword( plain, algo, makeSalt( 24 ), DEFAULT_ROUNDS_EXP );
}
public static String hashPassword( String plain ){
String algo = DEFAULT_ALGO;
try {
return hashPassword( plain, algo );
} catch( NoSuchAlgorithmException e ) {
throw new Error( "Error in installation: " + algo + " is missing" );
}
}
}