Brute force passwords in Java

Posted on

Problem

I made this little code to see what brute forcing is like. I made this with a complete guess on how it works. This code works fine but it seems to take much much longer than it really should. I have the Scanner there so I can tell the program what password it is searching for. I have the timeMillis also just for personal reasons to see how long it takes to find a password.

I have the

System.out.println(newPass); 

to see if I ever run into errors where would I run into it. The way my code works is that it loops chars^length times until it hits the password.

If there is any way I can improve this?

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

public class Main {
    static String newPass = "";
    static String chars = "0123456789aABbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyzZ";

    public static void main(String[] args) {
        Scanner userIn = new Scanner(System.in);
        String password = userIn.nextLine();
        System.out.println("Is using symbols an option? if so type in [Y] if not type in [N]");
        String choose = userIn.nextLine();
        boolean decideSymb = true;
        boolean again = true;
        while (again == true) {
            if (choose.equalsIgnoreCase("y")) {
                again = false;
            } else if (choose.equalsIgnoreCase("n")) {
                again = false;
                decideSymb = false;
            } else {
                System.out.println("Try again! nIs using symbols an option? if so type in [Y] if not type in [N]");
                choose = userIn.nextLine();
            }
        }
        long start = System.currentTimeMillis();
        crack(password, decideSymb);
        long end = System.currentTimeMillis();
        long milliSecs = TimeUnit.MILLISECONDS.toSeconds(end - start);
        ;
        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("The password is: " + newPass);
        if (years > 0) {
            if (years == 1) {
                System.out.println("it tookn" + years + "yearn" + days + " daysn" + hours + " hoursn" + mins
                        + " minsn" + secs + " secsn" + milliSecs + " millisecondsnto find the password");
            } else {
                System.out.println("it tookn" + years + "yearsn" + days + " daysn" + hours + " hoursn" + mins
                        + " minsn" + secs + " secsn" + milliSecs + " millisecondsnto find the password");
            }
        } else if (days > 0) {
            if (days == 1) {
                System.out.println("it tookn" + days + " dayn" + hours + " hoursn" + mins + " minsn" + secs
                        + " secsn" + milliSecs + " millisecondsnto find the password");
            } else {
                System.out.println("it tookn" + days + " daysn" + hours + " hoursn" + mins + " minsn" + secs
                        + " secsn" + milliSecs + " millisecondsnto find the password");
            }
        } else if (hours > 0) {
            if (hours == 1) {
                System.out.println("it tookn" + hours + " hourn" + mins + " minsn" + secs + " secsn" + milliSecs
                        + " millisecondsnto find the password");
            } else {
                System.out.println("it tookn" + hours + " hoursn" + mins + " minsn" + secs + " secsn" + milliSecs
                        + " millisecondsnto find the password");
            }
        } else if (mins > 0) {
            if (mins == 1) {
                System.out.println("it tookn" + mins + " minn" + secs + " secsn" + milliSecs
                        + " millisecondsnto find the password");
            } else {
                System.out.println("it tookn" + mins + " minsn" + secs + " secsn" + milliSecs
                        + " millisecondsnto find the password");
            }
        } else if (secs > 0) {
            if (secs == 1) {
                System.out.println("it tookn" + secs + " secn" + milliSecs + " millisecondsnto find the password");
            } else {
                System.out.println("it tookn" + secs + " secsn" + milliSecs + " millisecondsnto find the password");
            }
        } else {
            System.out.println("it tookn" + milliSecs + " millisecondsnto find the password");
        }

        System.exit(0);
    }

    private static void crack(String password, boolean decideSymb) {
        if (decideSymb == true) {
            chars = "1234567890#%&@aABbCcDdEeFfGgHh!IiJjKkLlMmNnOoPpQqRr$SsTtUuVvWwXxYyzZ";
        }
        for (int length = 2; length <= 15; length++) {
            newPass = "";
            for (int n = 0; n < length; n++) {
                newPass += "0";
            }
            int lastInd = length - 1;
            while (!newPass.equals(password)) {
                String end = "";
                for (int n = 0; n < newPass.length(); n++) {
                    end += "Z";
                }
                if (newPass.equals(end)) {
                    break;
                }
                int indInChars = chars.indexOf(newPass.charAt(lastInd));
                if (indInChars < chars.length() && indInChars >= 0) {
                    boolean t = true;
                    int howManyZs = 0;
                    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;
            }
        }
    }
}

Keep in mind I am not gonna use this for malicious reasons. Plus, most software is invulnerable to brute force. This is merely out of fun.

Solution

Do while you must

    System.out.println("Is using symbols an option? if so type in [Y] if not type in [N]");
    String choose = userIn.nextLine();
    boolean decideSymb = true;
    boolean again = true;
    while (again == true) {
        if (choose.equalsIgnoreCase("y")) {
            again = false;
        } else if (choose.equalsIgnoreCase("n")) {
            again = false;
            decideSymb = false;
        } else {
            System.out.println("Try again! nIs using symbols an option? if so type in [Y] if not type in [N]");
            choose = userIn.nextLine();
        }
    }

You have some awkward-feeling repeated code in here (the prompt for input). This can be refactored into a cleaner, more natural feeling do/while loop:

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

By changing this to a do/while, we were able to remove those clunky if statements along with the two flag variables.

The above loop will continuously ask for input until proper input is given.

Or use a pattern you can

Instead of using a loop to shorten the above code, you could use the Scanner‘s built-in next(Pattern pattern). This will ensure that you only get input that follows your specified pattern.

First, create a final Pattern in a class field so you can easily access it in the code:

public static final Pattern yesOrNo = Pattern.compile("(y|n)", CASE_INSENSITIVE);

The above regex should only pass for either y, n, Y, or N. Also, it may be better to compile the regex in the main method itself.

Then, in your code, instead of having the loop, you can simply write:

System.out.println("Is using symbols an option? if so type in [Y] if not type in [N]");
String choose = userIn.next(yesOrNo);

I believe this is a much better solution.


Too far for a single letter

        if (years == 1) {
            System.out.println("it tookn" + years + "yearn" + days + " daysn" + hours + " hoursn" + mins
                    + " minsn" + secs + " secsn" + milliSecs + " millisecondsnto find the password");
        } else {
            System.out.println("it tookn" + years + "yearsn" + days + " daysn" + hours + " hoursn" + mins
                    + " minsn" + secs + " secsn" + milliSecs + " millisecondsnto find the password");
        }

You have two entirely separate conditional statements just to add a single character? That’s a waste, and it makes your code look ugly.

Honestly, I don’t think you’ll even need to worry about that “s”.

  1. The chances of it being exactly 1 are very low.
  2. It’s such a small thing that most applications ignore.

However, if you really want to have that “s” there, use a ternary instead:

System.out.println("it tookn" + years + "year" + (years == 1 ? "s" : "") + "n" + days + " daysn" + hours + " hoursn" + mins + " minsn" + secs + " secsn" + milliSecs + " millisecondsnto find the password");

The date is exactly

This chunk of code here:

    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);

Doesn’t belong. It should be extracted into a separate method that accepts the milliSecs variable. As for returning, it could probably return a custom class that looks like this:

public class TimeData {
    private final long secs;
    private final long mins;
    ...
}

with getters for each field. This will make your code more OO.

If you want to be extra OOP-y, you can put the method that generates the TimeData in this TimeData class, make it static, and have it return an instance of TimeData with filled data.

Over-complicating the time printing

Again, you are over-complicating the time printing section. In my opinion, it is perfectly okay to have something like:

“It took 0 days, 0 hours, 24 minutes, 13 seconds, and 1 millisecond.”

Those 0’s there don’t harm the UI at all. Instead of having that huge conditional chain in your code, you should just use System.out.printf and print out all the time values you have.

Going back to OOP, you could extract this into the TimeData‘s toString method.

Pluralisation

You have a lot of code dealing with this, and it’s both repetitive and not quite right (checks only for the plural-ness of the first item, so you might get “it took 1 min 1 secs 1 milliseconds”).

Something like this would be a start:

private static String pluralFormat(String word, long value) {
    return value.toString() + " " + word + (value == 1 ? "s" : "") + "n";
}

System.out.println(
    "it tookn"
    + pluralFormat("year", years)
    + pluralFormat("day", days)
    + pluralFormat("hour", hours)
    + pluralFormat("min", mins)
    + pluralFormat("sec", secs)
    + pluralFormat("millisecond", milliSecs)
    + "to find the password"
);

Or you could use separate printlns for each line to avoid manually appending the newline character.

Cracking algorithm

It’s a little hard to follow what’s going on here.

First, usually “cracking” a password means that you know a hashed password (the output of hashfunction(password)) and you crack it by finding what value of password gives you that hash. Here there is no hash function, you’re just building string permutations until you’ve arrived at password. This is fine, I just wanted to make a note.

Second, since I’m not fully sure what’s going on with the howManyZs and your algorithm in general, I’ll just offer some structural suggestions:

Repeating strings

In a couple of places you build a string of n repeated characters. You could turn that into a static utility method:

private static String repeatString(String s, int n) {
    StringBuilder sb = new StringBuilder(n);
    while (n-- > 0) {
        sb.append(s);
    }
    return sb.toString();
}

so that you’d end up with, for example

String end = repeatString("Z", newpass.length());

Parameters and variable scopes

To keep data more encapsulated and make your crack function more reusable, I recommend declaring it like this:

private static String crack(String password, String possibleChars)
  • Take in possibleChars directly, and let the caller pass either chars or chars + symbols as needed.
  • Return the cracked password instead of storing it in a global field newPass.

Leave a Reply

Your email address will not be published. Required fields are marked *