4
\$\begingroup\$

I would like to generate Millions of Random String and Hashes in Java fast as possible. For the moment, @Turing85 gave me a pretty good code who was pretty fast only 5 sec for 50M of random String but the next problem to which I am confronted is the slowness when I hash my passwords in my String. The only problem is that the hash must match the generated password.

For the moment I got this code for sha256 and the random String into a file.

import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Random;
import java.util.concurrent.TimeUnit;

class Scratch {

    private final static String policy = "azertyuiopqsdfghjklmwxcvbnAZERTYUIOPQSDFGHJKLMWXCVBN1234567890";
    private final static Random random = new Random();

    private static final int MIN = '!';
    private static final int MAX = '~';
    private static final Random RANDOM = new Random();

    public static void main(final String... args) throws IOException, NoSuchAlgorithmException {
        final Path passwordFile = Path.of("passwords.txt");
        final Path hashFile = Path.of("hash.txt");
        if (!Files.exists(passwordFile)) {
            Files.createFile(passwordFile);
        }
        if (!Files.exists(hashFile)) {
            Files.createFile(hashFile);
        }
        final DecimalFormat df = new DecimalFormat();
        final DecimalFormatSymbols ds = df.getDecimalFormatSymbols();
        ds.setGroupingSeparator('_');
        df.setDecimalFormatSymbols(ds);
        final int numberOfPasswordsToGenerate = 50_000_000;
        final int chunkSize = 500_000;
        int passwordLength;
        final int min = 'A';
        final int max = 'Z';
        int generated = 0;
        int chunk = 0;

        final long start = System.nanoTime();
        while (generated < numberOfPasswordsToGenerate) {
            final StringBuilder passwords = new StringBuilder();
            final StringBuilder hashes = new StringBuilder();
            for (int index = chunk * chunkSize; index < (chunk + 1) * chunkSize && index < numberOfPasswordsToGenerate;
                    ++index) {
                final StringBuilder password = new StringBuilder();
                final StringBuilder hash = new StringBuilder();

                passwordLength = random.nextInt(9 - 6) + 6;
                for (int character = 0; character < passwordLength; ++character) {
                    //password.append(policy.charAt(random.nextInt(policy.length())));
                    password.append(fetchRandomLetterFromAlphabet());
                }
                passwords.append(password.toString()).append(System.lineSeparator());

                hash.append(toHexString(getSHA(password.toString())));
                hashes.append(hash.toString()).append(System.lineSeparator());

                ++generated;
                if (generated % 500_000 == 0) {
                    System.out.printf(
                            "%s / %s%n",
                            df.format(generated),
                            df.format(numberOfPasswordsToGenerate));
                }
            }
            ++chunk;
            Files.writeString(passwordFile, passwords.toString(), StandardOpenOption.WRITE);
            Files.writeString(hashFile, hashes.toString(), StandardOpenOption.WRITE);
        }
        final long consumed = System.nanoTime() - start;
        System.out.printf("Done. Took %d seconds%n", TimeUnit.NANOSECONDS.toSeconds(consumed));
        //System.out.printf("Done. Took %d seconds%n", TimeUnit.NANOSECONDS.toNanos(consumed)/*.toSeconds(consumed)*/);
    }

    private static char fetchRandomLetterFromAlphabet() {
        return (char) (RANDOM.nextInt(MAX - MIN + 1) + MIN);
    }

    public static byte[] getSHA(String input) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        return md.digest(input.getBytes(StandardCharsets.UTF_8));
    }

    public static String toHexString(byte[] hash) {
        BigInteger number = new BigInteger(1, hash);
        StringBuilder hexString = new StringBuilder(number.toString(16));
        while (hexString.length() < 32) {
            hexString.insert(0, '0');
        }
        return hexString.toString();
    }
}

btw if you didn't understand the problematic, I would like to increase speed of the sha256 algorithm to make it more efficient

Thanks :)

\$\endgroup\$
4
  • \$\begingroup\$ To my mind it's a bad idea to be storing the passwords and hashes like this. It would make more sense to generate the password, display it to the user and store only hash with the users id. Also you say you want to improve the SHA-256 algorithm but you don't present the actual algorithm. \$\endgroup\$ – user33306 Apr 2 at 11:20
  • 1
    \$\begingroup\$ In my view this question does not belong to codereview.se since this site is about code quality, not performance. \$\endgroup\$ – Timothy Truckle Apr 2 at 12:21
  • 1
    \$\begingroup\$ @timothytruckle this has been discussed several times in meta, and the consensus is that performance is also a topic of review. Too tired to search for the thread right now but I'm sure you'll find it. \$\endgroup\$ – Emily L. Apr 3 at 0:21
  • \$\begingroup\$ @EmilyL. thanks for pointing that out for me. \$\endgroup\$ – Timothy Truckle Apr 3 at 17:02
2
\$\begingroup\$

"I would like to increase the speed of the SHA256 algorithm"

You're using a library for the hash so there's really no way for you to make it faster. In theory, if you're an expert in cryptography and in computer science you could maybe build something faster yourself but most likely it'll be slower because it's actually hard...

From the resources I quickly checked online the Java sha256 implementation should be able to do atleast 100 MB/s on a modern ish computer.

You say it takes 5 seconds to do 50M hashes of an average of 7.5 characters per string. That works out to 75 MB/s, which is in the ballpark but a bit on the slow end. I don't know what CPU your using so this could be normal or it could be slow.

So as I see it there are some ways for you to make your code faster: optimize the generation of the strings (not the computation of the hash), OR use a faster sha256 implantation which likely means using JNI and a suitable C/C++ library (this may involve some hairloss and sacrificing a goat), OR probably the most effective way is to just use several threads each processing one chunk in parallel. They can then write to the same file with synchronization or separate files if you want maximum speed but you'll need to merge them later if you need a single file. Or you could do any combination of the three options above.

\$\endgroup\$
2
  • \$\begingroup\$ And how implementing the multiThreading algorithm in my code for every hash ? Yeah I used a prebuild librairy so maybe I can't speed up the hash for password \$\endgroup\$ – Forcela8 Apr 2 at 11:56
  • 1
    \$\begingroup\$ By learning multi threading for Java. There are plenty of tutorials and guides online that you can search for. \$\endgroup\$ – Emily L. Apr 3 at 0:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.