package no.ntnu.item.cda; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.apache.hadoop.filecache.DistributedCache; import org.apache.hadoop.fs.Path; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapred.JobConf; import org.apache.hadoop.mapred.MapReduceBase; import org.apache.hadoop.mapred.Mapper; import org.apache.hadoop.mapred.OutputCollector; import org.apache.hadoop.mapred.Reporter; import de.rtner.security.auth.spi.PBKDF2Engine; import de.rtner.security.auth.spi.PBKDF2Formatter; import de.rtner.security.auth.spi.PBKDF2HexFormatter; import de.rtner.security.auth.spi.PBKDF2Parameters; public class CDAMapper extends MapReduceBase implements Mapper<LongWritable, Text, Text, LongWritable> { public static byte[] salt; public static byte[] cipher; public static String password; private int THREADS; private int WORDS_PER_THREAD; private Path[] localFiles; public static DictionaryThread[] dictionary_threads; private SecretKey key; private byte[] plain; @Override public synchronized void configure(JobConf conf) { try { System.out.println("Starting MAPPER config: " + System.currentTimeMillis()); THREADS = conf.getInt("THREADS", 1); WORDS_PER_THREAD = conf.getInt("PASSWORDS_PER_LINE", 1) / THREADS; dictionary_threads = new DictionaryThread[THREADS]; password = ""; localFiles = DistributedCache.getLocalCacheFiles(conf); File tmp = new File(localFiles[0].toString()); FileInputStream in = new FileInputStream(tmp); byte[] b = new byte[(int) tmp.length()]; in.read(b); in.close(); salt = new byte[16]; cipher = new byte[b.length - salt.length]; System.arraycopy(b, 0, salt, 0, salt.length); System.arraycopy(b, salt.length, cipher, 0, cipher.length); System.out.println("MAPPER START: " + System.currentTimeMillis()); } catch (IOException e) { System.out.println("Could not read from distributed cache!"); e.printStackTrace(); } } @Override public void map(LongWritable key, Text value, OutputCollector<Text, LongWritable> output, Reporter reporter) throws IOException { String[] line = value.toString().split(" "); String[] line_chunk = new String[WORDS_PER_THREAD]; // Create threads to decrypt words in chunk for (int i = 0, c = 0; i < (THREADS * WORDS_PER_THREAD) && i < line.length; i += WORDS_PER_THREAD, c++) { if (line.length - i >= WORDS_PER_THREAD) { System.arraycopy(line, i, line_chunk, 0, WORDS_PER_THREAD); } else { System.arraycopy(line, i, line_chunk, 0, line.length - i); } dictionary_threads[c] = new DictionaryThread("dict" + i, line_chunk); } // If the number of passwords for this line is not a multiple of the // number of threads in use, there will be a set of remaining passwords. // These passwords are checked below. This will // not be activated if an optimized dictionary is used! if (line.length > THREADS * WORDS_PER_THREAD) { for (int i = 0; i < (line.length - THREADS); i++) { if (check(line[THREADS + i])) { System.out.println("FOUND PASSWORD: " + System.currentTimeMillis()); CDAMapper.password = line[THREADS + i]; break; } } } // Wait for all threads to finish for (DictionaryThread dt : dictionary_threads) { try { dt.thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } if (!password.equals("")) { output.collect(new Text("Password is [ " + password + " ]. Found at"), new LongWritable(System.currentTimeMillis())); } } public boolean check(String word) { this.setKey(word); this.plain = symECBDecrypt(cipher, key); // 68 = char D and indicate correct decryption of first byte if (this.plain != null && this.plain[0] == 68) { String[] parts = new String(plain).split(":"); if (parts.length == 4) return true; } return false; } public void setKey(String password) { PBKDF2Formatter formatter = new PBKDF2HexFormatter(); PBKDF2Parameters param = new PBKDF2Parameters("HmacSHA1", "ISO-8859-1", salt, 1000); PBKDF2Engine engine = new PBKDF2Engine(param); param.setDerivedKey(engine.deriveKey(password, 16)); String tmp = formatter.toString(param).split(":")[2]; key = new SecretKeySpec(tmp.getBytes(), "AES"); } public byte[] symECBDecrypt(byte[] cipherText, SecretKey key) { try { Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "BC"); cipher.init(Cipher.DECRYPT_MODE, key); return cipher.doFinal(cipherText); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } catch (NoSuchPaddingException e) { e.printStackTrace(); } catch (InvalidKeyException e) { if (e.getMessage().equals("Illegal key size or default parameter")) { System.out .println("\nERROR: Have you installed the Java(TM) Cryptography Extension (JCE) Jurisdiction Policy Files?"); e.printStackTrace(); } } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { } catch (NoSuchProviderException e) { e.printStackTrace(); } return null; } }