Last active
August 29, 2015 13:56
-
-
Save bufferings/8934915 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example.mdsample.java; | |
import java.security.MessageDigest; | |
import java.security.NoSuchAlgorithmException; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.concurrent.Callable; | |
import java.util.concurrent.ExecutionException; | |
import java.util.concurrent.ExecutorService; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.Future; | |
import javax.xml.bind.DatatypeConverter; | |
import org.apache.commons.lang3.StringUtils; | |
public class MyApp { | |
public static void main(String[] args) { | |
// 温めておくと速いのだ | |
findPasswordOnSingleThread("", ""); | |
// Data | |
String salt = "hoge"; | |
String hashValue = "4b364677946ccf79f841114e73ccaf4f"; | |
int trialCount = 10; | |
// Single Thread | |
System.out.println("Single Thread ===="); | |
int additionalThreadCount = 0; | |
measure(salt, hashValue, trialCount, additionalThreadCount); | |
// Multi Thread | |
System.out.println("Multi Thread ===="); | |
additionalThreadCount = 4; | |
measure(salt, hashValue, trialCount, additionalThreadCount); | |
} | |
static void measure(String salt, String hash, int trialCount, | |
int additionalThreadCount) { | |
long startTime = System.currentTimeMillis(); | |
for (int i = 0; i < trialCount; i++) { | |
long t1 = System.currentTimeMillis(); | |
String password; | |
if (additionalThreadCount == 0) { | |
password = findPasswordOnSingleThread(salt, hash); | |
} else { | |
password = findPasswordOnMultiThread(salt, hash, additionalThreadCount); | |
} | |
long t2 = System.currentTimeMillis(); | |
System.out | |
.println("password=" + password + " time=" + (t2 - t1) + "[ms]"); | |
} | |
long endTime = System.currentTimeMillis(); | |
System.out.println("average time=" + (endTime - startTime) / trialCount | |
+ "[ms]"); | |
} | |
static final byte[] DIGIT_BYTES = new byte[] { 0x30, 0x31, 0x32, 0x33, 0x34, | |
0x35, 0x36, 0x37, 0x38, 0x39 }; | |
static byte[] convertToDigitBytes(int i) { | |
return new byte[] { DIGIT_BYTES[(i / 100000) % 10], | |
DIGIT_BYTES[(i / 10000) % 10], DIGIT_BYTES[(i / 1000) % 10], | |
DIGIT_BYTES[(i / 100) % 10], DIGIT_BYTES[(i / 10) % 10], | |
DIGIT_BYTES[i % 10] }; | |
} | |
static String findPasswordOnSingleThread(String salt, String hash) { | |
byte[] prefixBytes = (salt + "$").getBytes(); | |
byte[] hashBytes = DatatypeConverter.parseHexBinary(hash); | |
return new PasswordFinder(prefixBytes, hashBytes, 0, 1).call(); | |
} | |
static String findPasswordOnMultiThread(String salt, String hash, | |
int maxThreads) { | |
byte[] prefixBytes = (salt + "$").getBytes(); | |
byte[] hashBytes = DatatypeConverter.parseHexBinary(hash); | |
List<PasswordFinder> finders = new ArrayList<>(); | |
for (int i = 0; i < maxThreads; i++) { | |
finders.add(new PasswordFinder(prefixBytes, hashBytes, i, maxThreads)); | |
} | |
ExecutorService service = Executors.newFixedThreadPool(maxThreads); | |
try { | |
List<Future<String>> futures = service.invokeAll(finders); | |
for (Future<String> future : futures) { | |
String password = future.get(); | |
if (password != null) { | |
return password; | |
} | |
} | |
return null; | |
} catch (InterruptedException | ExecutionException e) { | |
throw new RuntimeException(e); | |
} finally { | |
service.shutdown(); | |
} | |
} | |
public static class PasswordFinder implements Callable<String> { | |
final byte[] prefixBytes; | |
final byte[] hashBytes; | |
final int start; | |
final int step; | |
public PasswordFinder(byte[] prefixBytes, byte[] hashBytes, int start, | |
int step) { | |
this.prefixBytes = prefixBytes; | |
this.hashBytes = hashBytes; | |
this.start = start; | |
this.step = step; | |
} | |
@Override | |
public String call() { | |
return findPassword(prefixBytes, hashBytes, start, step); | |
} | |
String findPassword(byte[] prefixBytes, byte[] hashBytes, int start, | |
int step) { | |
MessageDigest digester; | |
try { | |
digester = MessageDigest.getInstance("MD5"); | |
} catch (NoSuchAlgorithmException e) { | |
throw new RuntimeException(e); | |
} | |
for (int i = start; i < 1000000; i += step) { | |
digester.update(prefixBytes); | |
digester.update(convertToDigitBytes(i)); | |
if (Arrays.equals(hashBytes, digester.digest())) { | |
return StringUtils.leftPad(String.valueOf(i), 6, '0'); | |
} | |
} | |
return null; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
MBA 1.7 GHz Intel Core i5 で。
Single Thread ====
password=567890 time=221[ms]
password=567890 time=204[ms]
password=567890 time=202[ms]
password=567890 time=210[ms]
password=567890 time=196[ms]
password=567890 time=205[ms]
password=567890 time=203[ms]
password=567890 time=208[ms]
password=567890 time=190[ms]
password=567890 time=201[ms]
average time=204[ms]
Multi Thread ====
password=567890 time=137[ms]
password=567890 time=135[ms]
password=567890 time=131[ms]
password=567890 time=132[ms]
password=567890 time=126[ms]
password=567890 time=139[ms]
password=567890 time=128[ms]
password=567890 time=125[ms]
password=567890 time=131[ms]
password=567890 time=129[ms]
average time=131[ms]