# Brute force passwords in Java - follow-up

Here is the updated code. The original question can be found here.

I would love it if you posted more ways I can improve this code. I am still fairly new to Java so I am not entirely sure how to make it any better.

package hexTox;

import java.util.Scanner;
import java.util.concurrent.TimeUnit;

public class Main {
static String newPass = "";

public static void main(String[] args) {
Scanner userIn = new Scanner(System.in);
String choose;
boolean decideSymb = true;
do {
System.out.println("Is using symbols an option? if so type in [Y] if not type in [N]");
choose = userIn.nextLine();
} while (!choose.equalsIgnoreCase("y") && !choose.equalsIgnoreCase("n")); //This was a long while loop with a lot of if and elses, thanks to SirPython's notice I changed it as he suggested.
if (choose.equalsIgnoreCase("n")) {
decideSymb = false;
}
long start = System.currentTimeMillis();
long end = System.currentTimeMillis();
long milliSecs = TimeUnit.MILLISECONDS.toSeconds(end - start);
System.out.println("The password is: " + newPass);
time(milliSecs);
System.exit(0);
}

private static void time(long milliSecs) { //I put this in a method rather than the Main method and changed it up as BenC and SirPython noted
long secs = milliSecs / 1000;
long mins = secs / 60;
long hours = mins / 60;
long days = hours / 24;
long years = days / 365;
days -= (years * 365);
hours -= (days * 24);
mins -= (hours * 60);
secs -= (mins * 60);
milliSecs -= (secs * 1000);
System.out.println("it took " + pluralFormat("year", years) + pluralFormat("day", days)
+ pluralFormat("hour", hours) + pluralFormat("min", mins) + pluralFormat("sec", secs)
+ pluralFormat("millisecond", milliSecs) + "to find the password");
}

private static String pluralFormat(String word, long value) { //This here was put as BenC noted to make my code more efficient
return Long.toString(value) + " " + word + (value > 1 ? "s" : "") + ", ";
}

private static void crack(String password, boolean decideSymb) {
if (decideSymb == true) {
chars = "0123456789#%&@aABbCcDdEeFfGgHh!IiJjKkLlMmNnOoPpQqRr$SsTtUuVvWwXxYyzZ"; //In my original code it started with 1 and now it starts with 0 as it is suppose to do. } for (int length = 2; length <= 15; length++) { newPass = ""; newPass = repeatString("0", length); int lastInd = length - 1; while (!newPass.equals(password)) { String end = repeatString("Z", newPass.length()); if (newPass.equals(end)) { break; } int indInChars = chars.indexOf(newPass.charAt(lastInd)); if (indInChars < chars.length() && indInChars >= 0) { boolean t = true; int howManyZs = 0; //This will replace that last Zs that are in order with 0s then update the one in front +1 char. For Example abcZZZ will evaluate to abD000 and will go on. while (t == true) { if (newPass.charAt(newPass.length() - 1 - howManyZs) == 'Z') { howManyZs++; } else { t = false; } } String reset0s = ""; for (int l = 0; l < howManyZs; l++) { reset0s += "0"; } if (lastInd < newPass.length() - 1 && lastInd > 0) { lastInd--; indInChars = chars.indexOf(newPass.charAt(lastInd)) + 1; newPass = newPass.substring(0, lastInd) + chars.charAt(indInChars) + newPass.substring(lastInd + 1); } else if (newPass.length() - howManyZs == 1) { newPass = chars.substring(chars.indexOf(newPass.charAt(0)) + 1, chars.indexOf(newPass.charAt(0)) + 2) + reset0s; } else if (newPass.length() - howManyZs > 1 && howManyZs != 0) { newPass = newPass.substring(0, newPass.length() - 1 - howManyZs) + chars.substring(chars.indexOf(newPass.charAt(newPass.length() - 1 - howManyZs)) + 1, chars.indexOf(newPass.charAt(newPass.length() - 1 - howManyZs)) + 2) + reset0s; } else { indInChars = chars.indexOf(newPass.charAt(lastInd)) + 1; newPass = newPass.substring(0, lastInd) + chars.charAt(indInChars); } System.out.println(newPass); } } if (newPass.equals(password)) { break; } } } private static String repeatString(String s, int n) { //This here was put as BenC noted to make my code more efficient StringBuilder sb = new StringBuilder(n); while (n-- > 0) { sb.append(s); } return sb.toString(); } }  • To prevent this from looking like a possible duplicate, consider adding the specific changes that were made. – Jamal Dec 21 '15 at 19:15 • Can you give a brief explanation of how your brute-forcing approach works? I'm also intrigued by why you're counting the number of Zs (howManyZs)... – h.j.k. Dec 22 '15 at 1:09 • @h.j.k. Sorry for not telling you earlier. Anyways I put it in as a comment in the code explaining what it is. By the way I am also working on a newer and faster way of brute forcing right now and will try to put up the code when done! – Oybek Kamolov Dec 22 '15 at 1:25 • @OybekKamolov ok, thanks for the update... so it starts with a and loop upwards by going to b, then abcZZZ to abD000 and so on? – h.j.k. Dec 22 '15 at 1:35 • Brute force isn't really a nice (albeit sometimes the only) solution. It will be O(n^m) in worst case, where n is the length of the password and m is the length of the charset being used. (please correct me if I am wrong with the Big O notation) – Emz Dec 22 '15 at 9:49 ## 1 Answer Make use of constants Instead of having static String chars with the default numbers and letters then in the middle of code overriding it with symbols as well, I suggest instead storing two static final String chars and static final String symbols, if you choose to use symbols as well, simply add it to the string activeCharset. That way it is easier at a first glance to see what characters exists, instead of having to scroll mid-way down the code. private static final String CHARACTERS = "0123456789aABbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyzZ"; private static final String SYMBOLS = "#%&@!$";

private static String activeCharset     = characters;


You then add to your activeCharset instead of working with boolean decideSymb

Magic values

for (int length = 2; length <= 15; length++) {


I again recommend to store the 2 and 15 as constants. Tell what they represent, right now you have to read and understand the code to figure it out.

Return values

This is a more of an opinion.

long start = System.currentTimeMillis();
long end = System.currentTimeMillis();


Your crack can return the time it took for it to execute, simply by having long start = System.currentTimeMillis(); in the start and subtracting it from long end = System.currentTimeMillis(); in the end.

Reasoning: If measuring the time it takes for the brute-force algorithm then it makes sense that it's crack method keeps track of it.

// get password from user
Scanner userIn = new Scanner (System.in);

// ask if symbols should be used
do {
System.out.println("Is using symbols an option? if so type in [Y] if not type in [N]");
choose = userIn.nextLine();
} while (!choose.equalsIgnoreCase("y") && !choose.equalsIgnoreCase("n"));

// if it is then add them
if (choose.equalsIgnoreCase("y")) {
activeCharset += SYMBOLS;
}


Reference

Footnote: I think it is great that you are doing follow up rounds on the code. It shows that you do actually care about the feedback.

• @OybekKamolov, I stand corrected, you should not use try-with-resource. Reference – Emz Dec 22 '15 at 11:41
• Try-with-resources can work. Note that closing the scanner closes the underlying stream. If you close the scanner on System.in, then you cannot reopen it. – 200_success Dec 22 '15 at 16:41