Main.java
/*
* Copyright (C) 2019 sw4j.org
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.sw4j.tool.p12breaker;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.sw4j.tool.p12breaker.breaker.P12Breaker;
import org.sw4j.tool.p12breaker.permutation.PermutationConfiguration;
import org.sw4j.tool.p12breaker.permutation.PermutationGenerator;
/**
*
* @author Uwe Plonus
*/
public class Main {
private final Logger logger = Logger.getLogger(Main.class.getName());
public static void main(final String... args) {
new Main().run(args);
}
public void run(final String... args) {
CommandLine cl = parseCommandLine(args);
if (cl.hasOption("h")) {
System.exit(0);
}
PermutationConfiguration.Builder builder = PermutationConfiguration.builder();
if (cl.hasOption("u")) {
builder.permutationString(cl.getOptionValue("u"));
} else if (cl.hasOption("c")) {
String characters = cl.getOptionValue("c");
builder.useDigits(characters.contains("d"));
builder.useUpperCaseCharacters(characters.contains("A"));
builder.useLowerCaseCharacters(characters.contains("a"));
builder.useSpecialCharacters(characters.contains("s"));
builder.useBlank(characters.contains("b"));
}
if (cl.hasOption("n")) {
try {
builder.minimumLength(Integer.parseInt(cl.getOptionValue("n")));
} catch (NumberFormatException nfex) {
logger.log(Level.INFO, "Unable to parse minimum length, using default");
}
}
if (cl.hasOption("x")) {
try {
builder.maximumLength(Integer.parseInt(cl.getOptionValue("x")));
} catch (NumberFormatException nfex) {
logger.log(Level.INFO, "Unable to parse maximum length, using default");
}
}
int threads = Runtime.getRuntime().availableProcessors();
if (cl.hasOption("t")) {
try {
threads = Integer.parseInt(cl.getOptionValue("t"));
} catch (NumberFormatException nfex) {
logger.log(Level.INFO, "Unable to parse threads, using default");
}
}
BlockingQueue<String> permutationQueue = new SynchronousQueue<>();
PermutationConfiguration configuration = builder.build();
PermutationGenerator generator = new PermutationGenerator(configuration, permutationQueue);
List<String> files = cl.getArgList();
for (String fileName: files) {
File file = new File(fileName);
if (Files.isReadable(file.toPath())) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
Files.copy(file.toPath(), baos);
byte[] p12data = baos.toByteArray();
final ExecutorService breakerService = new ThreadPoolExecutor(1, threads, 10, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(threads * 10), new ThreadPoolExecutor.CallerRunsPolicy());
final ExecutorService permutationService = Executors.newSingleThreadExecutor();
final ExecutorService distributorService = Executors.newSingleThreadExecutor();
final ExecutorService stopService = Executors.newSingleThreadExecutor();
P12Breaker.PasswordResultListener listener = (String password) -> {
stopService.submit(() -> {
System.out.println(String.format("\n%s: %s", fileName, password));
permutationService.shutdownNow();
distributorService.shutdown();
breakerService.shutdown();
});
};
AtomicInteger counter = new AtomicInteger();
AtomicInteger countPasswords = new AtomicInteger();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(0);
scheduler.scheduleAtFixedRate(() -> {
int count = counter.getAndSet(0);
System.out.print(String.format("Passwords per second: %d \r", count));
}, 1, 1, TimeUnit.SECONDS);
distributorService.submit(() -> {
while (true) {
try {
ByteArrayInputStream bais = new ByteArrayInputStream(p12data);
final String permutation = permutationQueue.take();
breakerService.submit(() -> {
P12Breaker breaker = new P12Breaker(bais,
permutation.toCharArray(), listener);
breaker.testPassword();
counter.incrementAndGet();
int passwordsTested = countPasswords.incrementAndGet();
if (passwordsTested % 10000 == 0) {
System.out.print(String.format("\nPassword tested: %s\n", permutation));
}
});
} catch (InterruptedException ex) {
logger.info("Distributor interrupted. Terminating.");
}
}
});
permutationService.submit(() -> {
try {
generator.createPermutations();
} catch (InterruptedException ex) {
logger.info("Distributor interrupted. Terminating.");
}
});
} catch (IOException ioex) {
logger.info(String.format("File %s not readable. Ignoring.", fileName));
}
} else {
logger.info(String.format("File %s not readable. Ignoring.", fileName));
}
}
}
private CommandLine parseCommandLine(final String... args) {
Options clOptions = new Options();
clOptions.addOption(Option
.builder("h")
.longOpt("help")
.hasArg(false)
.required(false)
.desc("Show this help.")
.build()
);
clOptions.addOption(Option
.builder("t")
.longOpt("threads")
.hasArg(true)
.required(false)
.argName("threads")
.desc("The number of threads to use (defaults to the number of processors).")
.build()
);
clOptions.addOption(Option
.builder("n")
.longOpt("min")
.hasArg(true)
.required(false)
.argName("min-length")
.desc("The minimum number of characters in the password.")
.build()
);
clOptions.addOption(Option
.builder("x")
.longOpt("max")
.hasArg(true)
.required(false)
.argName("max-length")
.desc("The maximum number of characters in the password.")
.build()
);
clOptions.addOption(Option
.builder("c")
.longOpt("char")
.hasArg(true)
.required(false)
.argName("characters")
.desc("The characters to use:\n" +
" d: digits (default)\n" +
" A: upper case characters (default)\n" +
" a: lower case characters (default)\n" +
" s: special characters (<>-_.:,;#'+*~?\\()[]/{}&%$\"!)\n" +
" b: blank")
.build()
);
clOptions.addOption(Option
.builder("u")
.longOpt("user")
.hasArg(true)
.required(false)
.argName("user characters")
.desc("Use user defined characters")
.build()
);
CommandLineParser parser = new DefaultParser();
CommandLine cl = new CommandLine.Builder().build();
try {
cl = parser.parse(clOptions, args, true);
} catch (ParseException pex) {
logger.log(Level.WARNING, "Problems while parsing the command line", pex);
}
if (cl.hasOption("h")) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("p12breaker [OPTION]... [FILE]...", clOptions);
}
return cl;
}
}