1 package snow.crypto; 2 3 import java.awt.Insets ; 4 import javax.swing.*; 5 import javax.swing.event.*; 6 import javax.swing.border.*; 7 import java.awt.BorderLayout ; 8 import java.awt.Dimension ; 9 import java.awt.EventQueue ; 10 import java.awt.Color ; 11 import java.awt.event.*; 12 import java.util.*; 13 import java.math.*; 14 import java.text.*; 15 import snow.utils.storage.FileUtils; 16 import snow.utils.gui.*; 17 import sun.misc.BASE64Encoder; 18 19 import java.security.*; 20 import java.security.spec.*; 21 import javax.crypto.spec.*; 22 import javax.crypto.*; 23 import java.io.*; 24 import SnowMailClient.crypto.Utilities; 25 import SnowMailClient.crypto.FileCipherManager; 26 27 28 39 public final class PasswordSearchDialog extends JDialog 40 { 41 final JPasswordField passwordToAnalyse = new JPasswordField(20); 42 final JTextField knownPrefixField = new JTextField(6); 43 final JTextField knownSuffixField = new JTextField(6); 44 final private JSpinner minPassLength = new JSpinner(); 45 final private JSpinner maxPassLength = new JSpinner(); 46 final private JTextArea charsToTry = new JTextArea( 47 "ABCDEFGHIJKLMNOPQRSTUCVWXYZ abcdefghijklmnopqrstuvwxyz1234567890+-*/_$!?\\=.,:;%&()[]{}#üöäïéàèûôî", 3,30); 48 final private NumberFormat df = DecimalFormat.getInstance(); 49 50 final private FileField dictsFolder = new FileField("e:/java/dics/", false, 51 "Choose the folder or files where the dicts are", 52 JFileChooser.FILES_AND_DIRECTORIES); 53 final private JCheckBox tryStartOfPhrases = new JCheckBox("Try startings of words", true); 54 final private JSpinner minDictPassLength = new JSpinner(); 55 56 byte[] trueHashOfPass; 57 58 String knownPrefix = null; 59 String knownSuffix = null; 60 final File fileToDecipher; 61 62 65 public PasswordSearchDialog(final JDialog parent, final byte[] trueHashOfPass_, File fileToDecipher) 66 { 67 super(parent, "Snowmail password crack tool", true); 68 69 this.trueHashOfPass = trueHashOfPass_; 70 this.fileToDecipher = fileToDecipher; 71 72 JTextArea explain = new JTextArea( 73 "This utility will try to discover the snowmail password." 74 +"\nSnowmail password is made of the 16 first bytes of the sha-1 hash of the passphrase provided." 75 +"\nThe four first bytes of the hash of the hash are stored to allow quick password verification." 76 +"\nSha-1 hashes are not reversible. So, only dictionary and brute force tries are possible" 77 +"\nto find out the passphrase" 78 +"\n\nYou will be afraid how \"easy\" bad passwords are cracked quickly, even long ones." 79 +"\nThis will help you choosing good passwords, i.e. longer than 8 chars, with special" 80 +"\nchars and numbers. They will resist millions of years." 81 82 +"\n\nFor dictionary attack, you have to provide a folder, all words in all files (recursively)," 83 +"\nlonger than 4 chars will be tested." 84 +"\nUse for example all the free available dictionnaries from OpenOffice (in all languages !)" 85 +"\nand some files containing the most common passwords. Include some recent newspaper as well." 86 +"\n\nLook at http://en.wikipedia.org/wiki/Password_cracking to learn more"); 87 explain.setBorder(new EmptyBorder(5,5,5,5)); 88 explain.setBackground(UIManager.getColor("Panel.background")); 89 explain.setEditable(false); 90 add(explain, BorderLayout.NORTH); 91 92 JPanel input = new JPanel(); 93 GridLayout3 gl = new GridLayout3(2,input); 94 add(input, BorderLayout.CENTER); 95 96 if(trueHashOfPass==null) 97 { 98 gl.add("Password to test"); 99 gl.add(passwordToAnalyse); 100 gl.addSeparator(); 101 } 102 else 103 { 104 107 } 108 109 gl.addTitleSeparator("Eventual knowledge"); 110 gl.add("Known prefix"); 111 gl.add(knownPrefixField); 112 gl.add("Known suffix"); 113 gl.add(knownSuffixField); 114 115 gl.addTitleSeparator("Brute force attack"); 116 gl.add("Alphabet (chars to try)"); 117 gl.add(charsToTry); 118 charsToTry.setLineWrap(true); 119 gl.addSeparator(); 120 gl.add("Min password length"); 121 gl.add(minPassLength); 122 minPassLength.setPreferredSize(new Dimension (50, (int) minPassLength.getPreferredSize().getHeight())); 123 minPassLength.setValue(0); 124 gl.add("Max password length"); 125 gl.add(maxPassLength); 126 maxPassLength.setPreferredSize(new Dimension (50, (int) maxPassLength.getPreferredSize().getHeight())); 127 maxPassLength.setValue(6); 128 129 130 gl.addTitleSeparator("Dictionary attack"); 131 gl.add("Dictionaries (recurse)"); 132 gl.add(dictsFolder); 133 dictsFolder.setComponentWidth(200); 134 gl.add(""); 135 gl.add(tryStartOfPhrases); 136 gl.add("min word length"); 137 gl.add(minDictPassLength); 138 minDictPassLength.setValue(5); 139 140 JPanel controlPanel = new JPanel(); 141 add(controlPanel, BorderLayout.SOUTH); 142 JButton close = new JButton("Close",Icons.CrossIcon.shared10); 143 close.setMargin(new Insets (0,1,0,1)); 144 close.setFocusPainted(false); 145 controlPanel.add(close); 146 close.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) 147 { 148 setVisible(false); 149 dispose(); 150 }}); 151 152 JButton go = new JButton("Crack", Icons.WizIcon.shared15); 153 go.setMargin(new Insets (0,1,0,1)); 154 go.setFocusPainted(false); 155 go.setBackground(Color.orange); 156 controlPanel.add(go); 157 go.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) 158 { 159 cancel = false; 160 found = false; 161 if(trueHashOfPass_==null) 162 { 163 char[] pass = passwordToAnalyse.getPassword(); 165 trueHashOfPass = hashPassword_ID(new String (pass)); 166 } 167 168 if(knownPrefixField.getText().length()>0) 169 { 170 knownPrefix = knownPrefixField.getText(); 171 } 172 else 173 { 174 knownPrefix = null; 175 } 176 177 if(knownSuffixField.getText().length()>0) 178 { 179 knownSuffix = knownSuffixField.getText(); 180 } 181 else 182 { 183 knownSuffix = null; 184 } 185 186 try 187 { 188 if(dictsFolder.getPath()!=null) 189 { 190 List<File> files = new ArrayList<File>(); 191 FileUtils.getAllFilesRecurse(dictsFolder.getPath(), files); 192 if(files.size()>0) 194 { 195 analyseFiles(files, (Integer ) minDictPassLength.getValue()); 196 } 197 198 if(found) return; 199 } 200 } catch(Exception e) { e.printStackTrace(); } 201 202 if(cancel) return; 203 204 analyse((Integer ) minPassLength.getValue(), (Integer ) maxPassLength.getValue(), charsToTry.getText()); 205 } }); 206 207 pack(); 208 setLocationRelativeTo(null); 209 setVisible(true); 210 } 211 212 213 private void analyseFiles(final List<File> files, final int minDictPassLength) 214 { 215 final JDialog d = new JDialog(this, "Password Search from dictionaries ("+files.size()+")", true); 216 217 final JProgressBar progress = new JProgressBar(); 218 d.add(progress, BorderLayout.NORTH); 219 progress.setStringPainted(true); 220 221 JPanel center = new JPanel(); 222 GridLayout3 gl = new GridLayout3(1,center); 223 d.add(center, BorderLayout.CENTER); 224 gl.add("Number of files to use: "+files.size()); 225 long totSize = 0; 226 for(File f: files) totSize+=f.length(); 227 gl.add("Total size: " + (int)(totSize/1e6)+" MB"); 228 229 JPanel controlPanel = new JPanel(); 230 d.add(controlPanel, BorderLayout.SOUTH); 231 JButton stop = new JButton("Cancel"); 232 controlPanel.add(stop); 233 stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 234 cancel = true; 235 } }); 236 237 Thread t = new Thread () 238 { 239 public void run() 240 { 241 for(int i=0; i<files.size(); i++) 242 { 243 progress.setValue( (int)(i*100.0/files.size())); 244 progress.setString( files.get(i).getName() ); 245 246 String res = searchInFile(files.get(i), minDictPassLength, 55); 247 if(res!=null) 248 { 249 break; 250 } 251 else if(cancel) 252 { 253 JOptionPane.showMessageDialog(d, "Cancelled"); 254 break; 255 } 256 } 257 258 d.setVisible(false); 259 d.dispose(); 260 } 261 }; 262 t.start(); 263 264 d.setSize(400,300); 265 d.setLocationRelativeTo(null); 266 d.setVisible(true); 267 } 268 269 private String searchInFile(File f, int minLength, int maxLength) 270 { 271 long fileWordsAnalysed = 0; 272 BufferedReader in = null; 273 try 274 { 275 in = new BufferedReader(new FileReader(f)); 276 String line; 277 while((line=in.readLine())!=null) 278 { 279 StringTokenizer st = new StringTokenizer(line, " |()/"); while(st.hasMoreElements()) 282 { 283 String w = st.nextToken(); 284 285 if(found) return null; if(cancel) return null; 287 288 if(w.length()<minLength) continue; 290 if(w.length()>maxLength) continue; 292 293 fileWordsAnalysed++; 294 295 if(Math.random()<0.00001) 296 { 297 System.out.println("file word: "+w); 298 } 299 300 if(tryPass(w)!=null) { 302 System.out.println("Found in "+f); 303 found = true; 304 return createPass(w); 305 } 306 307 309 String w2 = null; 311 if(Character.isUpperCase(w.charAt(0))) 312 { 313 w2 = ""+Character.toLowerCase(w.charAt(0))+ w.substring(1); 314 } 315 else if(Character.isLowerCase(w.charAt(0))) 316 { 317 w2 = ""+Character.toUpperCase(w.charAt(0))+ w.substring(1); 318 } 319 320 if(w2!=null) 321 { 322 if(tryPass(w2)!=null) 323 { 324 System.out.println("Found in "+f); 325 found = true; 326 return createPass(w2); 327 } 328 } 329 330 if(this.tryStartOfPhrases.isSelected()) 332 { 333 for(int i=w.length()-1; i>=minLength-1; i--) 334 { 335 String w3 = w.substring(0, i-1); 336 if(tryPass(w3)!=null) 337 { 338 System.out.println("Found in "+f); 339 found = true; 340 return createPass(w3); 341 } 342 } 343 } 344 } 345 346 347 } 348 } 349 catch(Exception e) 350 { 351 e.printStackTrace(); 352 } 353 finally 354 { 355 FileUtils.closeIgnoringExceptions( in ); 356 } 357 System.out.println(""+fileWordsAnalysed+" words analysed in "+f); 358 return null; } 360 361 364 private String equals(byte[] tryHash, byte[] refHash) 365 { 366 for(int i=0; i<refHash.length; i++) 367 { 368 if(tryHash[i]!=refHash[i]) return null; 369 } 370 return "yeah"; 371 372 } 374 375 377 private String tryPass(String w) { 379 if(equals(hashPassword_ID(createPass(w)), trueHashOfPass)!=null) 380 { 381 if(this.fileToDecipher!=null) 382 { 383 try 384 { 385 FileCipherManager.decipherVectorFromFile(fileToDecipher, 386 SecretKeyUtilities.generateSecretKeyFromPassphrase( createPass(w).getBytes(), 16 )); 387 } 388 catch(Exception e) 389 { 390 System.out.println("Bad pass: "+createPass(w)); 391 return null; 392 } 393 } 394 395 if(fileToDecipher==null) 396 { 397 int res = JOptionPane.showConfirmDialog(this, "The password may be: \""+createPass(w)+"\"", 398 "A Password was found that has the same signature", JOptionPane.YES_NO_CANCEL_OPTION); 399 if(res==JOptionPane.CANCEL_OPTION) 400 { 401 cancel=true; 402 return null; 403 } 404 if(res!=JOptionPane.YES_OPTION) return null; 405 } 406 else 407 { 408 JOptionPane.showMessageDialog(this, "The password is: \""+createPass(w)+"\""); 409 } 410 411 found = true; 412 return createPass(w); 413 } 414 else 415 { 416 return null; 417 } 418 } 419 420 422 private String createPass(String p) 423 { 424 if(knownPrefix==null && knownSuffix==null) return p; 425 StringBuffer sb = new StringBuffer (); 426 if(knownPrefix!=null) sb.append(knownPrefix); 427 sb.append(p); 428 if(knownSuffix!=null) sb.append(knownSuffix); 429 return sb.toString(); 430 } 431 432 433 BigInteger numberAnalysedSoFar = BigInteger.ZERO; 434 BigInteger numberToAnalyse = null; 435 long startTime = 0; 436 boolean cancel = false; 437 boolean found = false; 438 final private JLabel soFarLabel = new JLabel("So far: 0 "); 439 final private JLabel remainingTimeLabel = new JLabel("Time remaining: ?"); 440 final private JLabel performanceLabel = new JLabel("Performance: ?"); 441 442 443 private void analyse(final int passMinLength, final int passMaxLength, final String charsToTry) 444 { 445 final JDialog d = new JDialog(this, "Password Search", true); 446 447 final JProgressBar progress = new JProgressBar(); 448 d.add(progress, BorderLayout.NORTH); 449 progress.setStringPainted(true); 450 451 JPanel center = new JPanel(); 452 GridLayout3 gl = new GridLayout3(1,center); 453 d.add(center, BorderLayout.CENTER); 454 gl.add("Alphabet size used: "+charsToTry.length()); 455 gl.add("Password length considered: "+passMinLength+" .. "+passMaxLength); 456 numberToAnalyse = calcNumberOfPossibilities(passMinLength, passMaxLength,charsToTry.length()); 457 String bi=df.format(numberToAnalyse); 458 gl.add("Number of possibilities: "+bi); 459 460 gl.addSeparator(); 461 gl.add(soFarLabel); 462 soFarLabel.setText(""); 463 gl.add(remainingTimeLabel); 464 remainingTimeLabel.setText(""); 465 466 gl.addSeparator(); 467 gl.add(performanceLabel); 468 performanceLabel.setText(""); 469 470 System.out.println("Alphabet size: "+charsToTry.length()); 471 System.out.println("Number of possibilities: "+numberToAnalyse); 472 473 JPanel controlPanel = new JPanel(); 474 d.add(controlPanel, BorderLayout.SOUTH); 475 JButton stop = new JButton("Cancel"); 476 controlPanel.add(stop); 477 stop.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { 478 cancel = true; 479 } }); 480 481 482 Thread t = new Thread () 483 { 484 public void run() 485 { 486 String res = searchSystematically(passMinLength, passMaxLength, charsToTry, progress); 487 d.setVisible(false); 488 d.dispose(); 489 if(cancel) 490 { 491 JOptionPane.showMessageDialog(d, "Cancelled"); 492 } 493 else if(res==null) 494 { 495 JOptionPane.showMessageDialog(d, "The password was not found, try more chars and more alphabet."); 496 } 497 } 498 }; 499 t.start(); 500 501 d.setSize(400,300); 502 d.setLocationRelativeTo(null); 503 d.setVisible(true); 504 505 } 506 507 508 private String searchSystematically(final int passMinLength, final int passMaxLength, final String charsToTry, final JProgressBar progress) 509 { 510 startTime = System.currentTimeMillis(); 511 numberAnalysedSoFar = BigInteger.ZERO; 512 513 for(int i=passMinLength; i<=passMaxLength; i++) 514 { 515 String found = searchSystematicallyExactLength(i, charsToTry, progress); 516 if(found!=null) return found; 517 } 518 return null; 519 } 520 521 522 525 private String searchSystematicallyExactLength(final int passLength, final String charsToTry, final JProgressBar progress) 526 { 527 System.out.println("Searching all passwords of length "+passLength+", poss = "+Math.pow(charsToTry.length(), passLength)); 528 529 long lastUpdate = 0; 530 531 int[] pos = new int[passLength]; 534 int n = charsToTry.length(); 535 536 long cnt = 0; 537 while(true) 538 { 539 numberAnalysedSoFar = numberAnalysedSoFar.add(BigInteger.ONE); 540 cnt++; 541 final StringBuffer sb = new StringBuffer (); 542 for(int p : pos) 543 { 544 sb.append( charsToTry.charAt(p) ); 545 } 546 if(tryPass(sb.toString())!=null) 548 { 549 found = true; 550 return createPass(sb.toString()); 551 } 552 553 if(increment(pos, n)==-1) 554 { 555 System.out.println(""+cnt); 556 return null; 557 } 558 559 if(cancel) return null; 560 561 if(System.currentTimeMillis() - lastUpdate>1000) { 563 lastUpdate = System.currentTimeMillis(); 564 try 565 { 566 EventQueue.invokeAndWait(new Runnable () { public void run() { 567 progress.setString( ""+sb.toString()); 568 soFarLabel.setText("Analysed so far: "+df.format(numberAnalysedSoFar)); 569 int prog = numberAnalysedSoFar.multiply(BigInteger.valueOf(100)).divide(numberToAnalyse).intValue(); 570 571 long millisSoFar = System.currentTimeMillis() - startTime; 573 if(millisSoFar>1000) 574 { 575 BigInteger totTimeMillis = numberToAnalyse.divide(numberAnalysedSoFar).multiply(BigInteger.valueOf(millisSoFar)); 577 BigInteger secsRemaining = totTimeMillis.subtract( BigInteger.valueOf(millisSoFar) ); 578 remainingTimeLabel.setText( "Remaining time = "+getTime(secsRemaining.divide(BigInteger.valueOf(1000)))) ; 579 580 performanceLabel.setText( "Performance: " 581 + df.format(numberAnalysedSoFar.multiply(BigInteger.valueOf(1000)).divide( BigInteger.valueOf(millisSoFar))) 582 +" password per sec"); 583 } 584 585 progress.setValue( prog ); 586 }}); 587 } catch(Exception e){} 588 } 589 } 590 591 592 } 593 594 private static String getTime(BigInteger secs) 595 { 596 599 if(secs.compareTo(BigInteger.valueOf(3600L*24*365*1000000000*2))>0) { 601 BigInteger[] dr = secs.divideAndRemainder(BigInteger.valueOf(3600L*24*365*1000000000)); 602 return ""+dr[0]+" billions of years"; 603 } 604 605 if(secs.compareTo(BigInteger.valueOf(3600L*24*365*1000000*2))>0) 606 { 607 BigInteger[] dr = secs.divideAndRemainder(BigInteger.valueOf(3600L*24*365*1000000)); 608 return ""+dr[0]+" milions of years"; 609 } 610 611 if(secs.compareTo(BigInteger.valueOf(3600L*24*365*1000))>0) 612 { 613 BigInteger[] dr = secs.divideAndRemainder(BigInteger.valueOf(3600L*24*365*100)); 614 return ""+dr[0]+" centuries"; 615 } 616 617 if(secs.compareTo(BigInteger.valueOf(3600L*24*365*2))>0) 618 { 619 BigInteger[] dr = secs.divideAndRemainder(BigInteger.valueOf(3600L*24*365)); 620 if(dr[0].compareTo(BigInteger.valueOf(3600L*24*365*5))>0) { 622 return ""+dr[0]+" years"; 623 } 624 else 625 { 626 return ""+dr[0]+" years "+getTime(dr[1]); 627 } 628 } 629 if(secs.compareTo(BigInteger.valueOf(3600*48))>0) 630 { 631 BigInteger[] dr = secs.divideAndRemainder(BigInteger.valueOf(3600*24)); 632 if(dr[0].compareTo(BigInteger.valueOf(3600*24*5))>0) 633 { 634 return ""+dr[0]+" days"; 635 } 636 else 637 { 638 return ""+dr[0]+" days "+getTime(dr[1]); 639 } 640 641 } 642 if(secs.compareTo(BigInteger.valueOf(3600*2))>0) 643 { 644 return ""+secs.divide(BigInteger.valueOf(3600))+" h"; 645 } 646 if(secs.compareTo(BigInteger.valueOf(60*2))>0) 647 { 648 return ""+secs.divide(BigInteger.valueOf(60))+" min"; 649 } 650 return ""+secs+" s"; 651 } 652 653 655 private static int increment(int[] nb, int n) 656 { 657 int add = 0; 660 for(int i=nb.length-1; i>=0; i--) 661 { 662 if(nb[i]<n-1) 663 { 664 nb[i]++; 665 return i; 666 } 667 else 668 { 669 nb[i]=0; 670 } 672 } 673 674 return -1; 675 } 676 677 private static String genRandomPass( final String charsToTry, int len) 678 { 679 StringBuffer pass = new StringBuffer (); 680 for(int i=0; i<len; i++) 681 { 682 pass.append( charsToTry.charAt( (int)( Math.random()*charsToTry.length()) )); 683 } 684 return pass.toString(); 685 } 686 687 public static BigInteger calcNumberOfPossibilities(int minLen, int maxLen, int chars) 688 { 689 BigInteger bi = new BigInteger("0"); 690 for(int i=minLen; i<=maxLen; i++) 691 { 692 bi = bi.add( new BigInteger(""+chars).pow(i) ); 693 } 694 return bi; 695 } 696 697 698 static MessageDigest md = null; 699 static 700 { 701 try 702 { 703 md = MessageDigest.getInstance("SHA1"); 704 } 705 catch(Exception e) 706 { 707 e.printStackTrace(); 708 } 709 } 710 711 725 726 728 static byte[] intermetiateDigest = null; 729 static byte[] intermetiateDigest2 = new byte[16]; 730 public static byte[] hashPassword_ID(String passphrase) 731 { 732 try 733 { 734 md.reset(); 735 intermetiateDigest = md.digest(passphrase.getBytes()); 736 System.arraycopy(intermetiateDigest, 0, intermetiateDigest2, 0, 16); 737 return md.digest(intermetiateDigest2); 738 742 } 743 catch(Exception e) 744 { 745 throw new RuntimeException (""+e.getMessage()); 746 } 747 } 748 749 750 public static void main(String [] args) 751 { 752 new PasswordSearchDialog(new JDialog(), null, null); 753 } 754 755 } | Popular Tags |