1 import java.awt.BorderLayout ; 2 import java.awt.Color ; 3 import java.awt.FlowLayout ; 4 import java.awt.Font ; 5 import java.awt.event.ActionEvent ; 6 import java.awt.event.ActionListener ; 7 import java.awt.event.KeyAdapter ; 8 import java.awt.event.KeyEvent ; 9 import java.io.File ; 10 import java.io.IOException ; 11 import java.io.InputStream ; 12 import java.io.OutputStream ; 13 14 import javax.swing.BoxLayout ; 15 import javax.swing.JButton ; 16 import javax.swing.JDialog ; 17 import javax.swing.JFrame ; 18 import javax.swing.JLabel ; 19 import javax.swing.JOptionPane ; 20 import javax.swing.JPanel ; 21 import javax.swing.JPasswordField ; 22 import javax.swing.JTextArea ; 23 import javax.swing.JTextField ; 24 import javax.swing.SwingUtilities ; 25 26 import ch.ethz.ssh2.Connection; 27 import ch.ethz.ssh2.InteractiveCallback; 28 import ch.ethz.ssh2.KnownHosts; 29 import ch.ethz.ssh2.ServerHostKeyVerifier; 30 import ch.ethz.ssh2.Session; 31 32 44 public class SwingShell 45 { 46 47 51 52 static final String knownHostPath = "~/.ssh/known_hosts"; 53 static final String idDSAPath = "~/.ssh/id_dsa"; 54 static final String idRSAPath = "~/.ssh/id_rsa"; 55 56 JFrame loginFrame = null; 57 JLabel hostLabel; 58 JLabel userLabel; 59 JTextField hostField; 60 JTextField userField; 61 JButton loginButton; 62 63 KnownHosts database = new KnownHosts(); 64 65 public SwingShell() 66 { 67 File knownHostFile = new File (knownHostPath); 68 if (knownHostFile.exists()) 69 { 70 try 71 { 72 database.addHostkeys(knownHostFile); 73 } 74 catch (IOException e) 75 { 76 } 77 } 78 } 79 80 84 class EnterSomethingDialog extends JDialog 85 { 86 private static final long serialVersionUID = 1L; 87 88 JTextField answerField; 89 JPasswordField passwordField; 90 91 final boolean isPassword; 92 93 String answer; 94 95 public EnterSomethingDialog(JFrame parent, String title, String content, boolean isPassword) 96 { 97 this(parent, title, new String [] { content }, isPassword); 98 } 99 100 public EnterSomethingDialog(JFrame parent, String title, String [] content, boolean isPassword) 101 { 102 super(parent, title, true); 103 104 this.isPassword = isPassword; 105 106 JPanel pan = new JPanel (); 107 pan.setLayout(new BoxLayout (pan, BoxLayout.Y_AXIS)); 108 109 for (int i = 0; i < content.length; i++) 110 { 111 if ((content[i] == null) || (content[i] == "")) 112 continue; 113 JLabel contentLabel = new JLabel (content[i]); 114 pan.add(contentLabel); 115 116 } 117 118 answerField = new JTextField (20); 119 passwordField = new JPasswordField (20); 120 121 if (isPassword) 122 pan.add(passwordField); 123 else 124 pan.add(answerField); 125 126 KeyAdapter kl = new KeyAdapter () 127 { 128 public void keyTyped(KeyEvent e) 129 { 130 if (e.getKeyChar() == '\n') 131 finish(); 132 } 133 }; 134 135 answerField.addKeyListener(kl); 136 passwordField.addKeyListener(kl); 137 138 getContentPane().add(BorderLayout.CENTER, pan); 139 140 setResizable(false); 141 pack(); 142 setLocationRelativeTo(null); 143 } 144 145 private void finish() 146 { 147 if (isPassword) 148 answer = new String (passwordField.getPassword()); 149 else 150 answer = answerField.getText(); 151 152 dispose(); 153 } 154 } 155 156 161 class TerminalDialog extends JDialog 162 { 163 private static final long serialVersionUID = 1L; 164 165 JPanel botPanel; 166 JButton logoffButton; 167 JTextArea terminalArea; 168 169 Session sess; 170 InputStream in; 171 OutputStream out; 172 173 int x, y; 174 175 180 class RemoteConsumer extends Thread 181 { 182 char[][] lines = new char[y][]; 183 int posy = 0; 184 int posx = 0; 185 186 private void addText(byte[] data, int len) 187 { 188 for (int i = 0; i < len; i++) 189 { 190 char c = (char) (data[i] & 0xff); 191 192 if (c == 8) { 194 if (posx < 0) 195 continue; 196 posx--; 197 continue; 198 } 199 200 if (c == '\r') 201 { 202 posx = 0; 203 continue; 204 } 205 206 if (c == '\n') 207 { 208 posy++; 209 if (posy >= y) 210 { 211 for (int k = 1; k < y; k++) 212 lines[k - 1] = lines[k]; 213 posy--; 214 lines[y - 1] = new char[x]; 215 for (int k = 0; k < x; k++) 216 lines[y - 1][k] = ' '; 217 } 218 continue; 219 } 220 221 if (c < 32) 222 { 223 continue; 224 } 225 226 if (posx >= x) 227 { 228 posx = 0; 229 posy++; 230 if (posy >= y) 231 { 232 posy--; 233 for (int k = 1; k < y; k++) 234 lines[k - 1] = lines[k]; 235 lines[y - 1] = new char[x]; 236 for (int k = 0; k < x; k++) 237 lines[y - 1][k] = ' '; 238 } 239 } 240 241 if (lines[posy] == null) 242 { 243 lines[posy] = new char[x]; 244 for (int k = 0; k < x; k++) 245 lines[posy][k] = ' '; 246 } 247 248 lines[posy][posx] = c; 249 posx++; 250 } 251 252 StringBuffer sb = new StringBuffer (x * y); 253 254 for (int i = 0; i < lines.length; i++) 255 { 256 if (i != 0) 257 sb.append('\n'); 258 259 if (lines[i] != null) 260 { 261 sb.append(lines[i]); 262 } 263 264 } 265 setContent(sb.toString()); 266 } 267 268 public void run() 269 { 270 byte[] buff = new byte[8192]; 271 272 try 273 { 274 while (true) 275 { 276 int len = in.read(buff); 277 if (len == -1) 278 return; 279 addText(buff, len); 280 } 281 } 282 catch (Exception e) 283 { 284 } 285 } 286 } 287 288 public TerminalDialog(JFrame parent, String title, Session sess, int x, int y) throws IOException 289 { 290 super(parent, title, true); 291 292 this.sess = sess; 293 294 in = sess.getStdout(); 295 out = sess.getStdin(); 296 297 this.x = x; 298 this.y = y; 299 300 botPanel = new JPanel (new FlowLayout (FlowLayout.LEFT)); 301 302 logoffButton = new JButton ("Logout"); 303 botPanel.add(logoffButton); 304 305 logoffButton.addActionListener(new ActionListener () 306 { 307 public void actionPerformed(ActionEvent e) 308 { 309 310 dispose(); 311 } 312 }); 313 314 Font f = new Font ("Monospaced", Font.PLAIN, 16); 315 316 terminalArea = new JTextArea (y, x); 317 terminalArea.setFont(f); 318 terminalArea.setBackground(Color.BLACK); 319 terminalArea.setForeground(Color.ORANGE); 320 325 terminalArea.setCaretColor(Color.BLACK); 326 327 KeyAdapter kl = new KeyAdapter () 328 { 329 public void keyTyped(KeyEvent e) 330 { 331 int c = e.getKeyChar(); 332 333 try 334 { 335 out.write(c); 336 } 337 catch (IOException e1) 338 { 339 } 340 e.consume(); 341 } 342 }; 343 344 terminalArea.addKeyListener(kl); 345 346 getContentPane().add(terminalArea, BorderLayout.CENTER); 347 getContentPane().add(botPanel, BorderLayout.PAGE_END); 348 349 setResizable(false); 350 pack(); 351 setLocationRelativeTo(parent); 352 353 new RemoteConsumer().start(); 354 } 355 356 public void setContent(String lines) 357 { 358 terminalArea.setText(lines); 361 } 362 } 363 364 369 class AdvancedVerifier implements ServerHostKeyVerifier 370 { 371 public boolean verifyServerHostKey(String hostname, int port, String serverHostKeyAlgorithm, 372 byte[] serverHostKey) throws Exception 373 { 374 final String host = hostname; 375 final String algo = serverHostKeyAlgorithm; 376 377 String message; 378 379 380 381 int result = database.verifyHostkey(hostname, serverHostKeyAlgorithm, serverHostKey); 382 383 switch (result) 384 { 385 case KnownHosts.HOSTKEY_IS_OK: 386 return true; 387 388 case KnownHosts.HOSTKEY_IS_NEW: 389 message = "Do you want to accept the hostkey (type " + algo + ") from " + host + " ?\n"; 390 break; 391 392 case KnownHosts.HOSTKEY_HAS_CHANGED: 393 message = "WARNING! Hostkey for " + host + " has changed!\nAccept anyway?\n"; 394 break; 395 396 default: 397 throw new IllegalStateException (); 398 } 399 400 401 402 String hexFingerprint = KnownHosts.createHexFingerprint(serverHostKeyAlgorithm, serverHostKey); 403 String bubblebabbleFingerprint = KnownHosts.createBubblebabbleFingerprint(serverHostKeyAlgorithm, 404 serverHostKey); 405 406 message += "Hex Fingerprint: " + hexFingerprint + "\nBubblebabble Fingerprint: " + bubblebabbleFingerprint; 407 408 409 410 int choice = JOptionPane.showConfirmDialog(loginFrame, message); 411 412 if (choice == JOptionPane.YES_OPTION) 413 { 414 415 416 String hashedHostname = KnownHosts.createHashedHostname(hostname); 417 418 419 420 database.addHostkey(new String [] { hashedHostname }, serverHostKeyAlgorithm, serverHostKey); 421 422 423 424 try 425 { 426 KnownHosts.addHostkeyToFile(new File (knownHostPath), new String [] { hashedHostname }, 427 serverHostKeyAlgorithm, serverHostKey); 428 } 429 catch (IOException ignore) 430 { 431 } 432 433 return true; 434 } 435 436 if (choice == JOptionPane.CANCEL_OPTION) 437 { 438 throw new Exception ("The user aborted the server hostkey verification."); 439 } 440 441 return false; 442 } 443 } 444 445 450 class InteractiveLogic implements InteractiveCallback 451 { 452 int promptCount = 0; 453 String lastError; 454 455 public InteractiveLogic(String lastError) 456 { 457 this.lastError = lastError; 458 } 459 460 461 462 public String [] replyToChallenge(String name, String instruction, int numPrompts, String [] prompt, 463 boolean[] echo) throws IOException 464 { 465 String [] result = new String [numPrompts]; 466 467 for (int i = 0; i < numPrompts; i++) 468 { 469 470 471 String [] content = new String [] { lastError, name, instruction, prompt[i] }; 472 473 if (lastError != null) 474 { 475 476 lastError = null; 477 } 478 479 EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "Keyboard Interactive Authentication", 480 content, !echo[i]); 481 482 esd.setVisible(true); 483 484 if (esd.answer == null) 485 throw new IOException ("Login aborted by user"); 486 487 result[i] = esd.answer; 488 promptCount++; 489 } 490 491 return result; 492 } 493 494 497 498 public int getPromptCount() 499 { 500 return promptCount; 501 } 502 } 503 504 510 class ConnectionThread extends Thread 511 { 512 String hostname; 513 String username; 514 515 public ConnectionThread(String hostname, String username) 516 { 517 this.hostname = hostname; 518 this.username = username; 519 } 520 521 public void run() 522 { 523 Connection conn = new Connection(hostname); 524 525 try 526 { 527 532 533 String [] hostkeyAlgos = database.getPreferredServerHostkeyAlgorithmOrder(hostname); 534 535 if (hostkeyAlgos != null) 536 conn.setServerHostKeyAlgorithms(hostkeyAlgos); 537 538 conn.connect(new AdvancedVerifier()); 539 540 545 546 boolean enableKeyboardInteractive = true; 547 boolean enableDSA = true; 548 boolean enableRSA = true; 549 550 String lastError = null; 551 552 while (true) 553 { 554 if ((enableDSA || enableRSA) && conn.isAuthMethodAvailable(username, "publickey")) 555 { 556 if (enableDSA) 557 { 558 File key = new File (idDSAPath); 559 560 if (key.exists()) 561 { 562 EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "DSA Authentication", 563 new String [] { lastError, "Enter DSA private key password:" }, true); 564 esd.setVisible(true); 565 566 boolean res = conn.authenticateWithPublicKey(username, key, esd.answer); 567 568 if (res == true) 569 break; 570 571 lastError = "DSA authentication failed."; 572 } 573 enableDSA = false; } 575 576 if (enableRSA) 577 { 578 File key = new File (idRSAPath); 579 580 if (key.exists()) 581 { 582 EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, "RSA Authentication", 583 new String [] { lastError, "Enter RSA private key password:" }, true); 584 esd.setVisible(true); 585 586 boolean res = conn.authenticateWithPublicKey(username, key, esd.answer); 587 588 if (res == true) 589 break; 590 591 lastError = "RSA authentication failed."; 592 } 593 enableRSA = false; } 595 596 continue; 597 } 598 599 if (enableKeyboardInteractive && conn.isAuthMethodAvailable(username, "keyboard-interactive")) 600 { 601 InteractiveLogic il = new InteractiveLogic(lastError); 602 603 boolean res = conn.authenticateWithKeyboardInteractive(username, il); 604 605 if (res == true) 606 break; 607 608 if (il.getPromptCount() == 0) 609 { 610 615 lastError = "Keyboard-interactive does not work."; 616 617 enableKeyboardInteractive = false; } 619 else 620 { 621 lastError = "Keyboard-interactive auth failed."; } 623 624 continue; 625 } 626 627 if (conn.isAuthMethodAvailable(username, "password")) 628 { 629 final EnterSomethingDialog esd = new EnterSomethingDialog(loginFrame, 630 "Password Authentication", 631 new String [] { lastError, "Enter password for " + username }, true); 632 633 esd.setVisible(true); 634 635 if (esd.answer == null) 636 throw new IOException ("Login aborted by user"); 637 638 boolean res = conn.authenticateWithPassword(username, esd.answer); 639 640 if (res == true) 641 break; 642 643 lastError = "Password authentication failed."; 645 continue; 646 } 647 648 throw new IOException ("No supported authentication methods available."); 649 } 650 651 656 657 Session sess = conn.openSession(); 658 659 int x_width = 90; 660 int y_width = 30; 661 662 sess.requestPTY("dumb", x_width, y_width, 0, 0, null); 663 sess.startShell(); 664 665 TerminalDialog td = new TerminalDialog(loginFrame, username + "@" + hostname, sess, x_width, y_width); 666 667 668 669 td.setVisible(true); 670 671 } 672 catch (IOException e) 673 { 674 JOptionPane.showMessageDialog(loginFrame, "Exception: " + e.getMessage()); 676 } 677 678 683 684 conn.close(); 685 686 691 692 Runnable r = new Runnable () 693 { 694 public void run() 695 { 696 loginFrame.dispose(); 697 } 698 }; 699 700 SwingUtilities.invokeLater(r); 701 } 702 } 703 704 void loginPressed() 705 { 706 String hostname = hostField.getText().trim(); 707 String username = userField.getText().trim(); 708 709 if ((hostname.length() == 0) || (username.length() == 0)) 710 { 711 JOptionPane.showMessageDialog(loginFrame, "Please fill out both fields!"); 712 return; 713 } 714 715 loginButton.setEnabled(false); 716 hostField.setEnabled(false); 717 userField.setEnabled(false); 718 719 ConnectionThread ct = new ConnectionThread(hostname, username); 720 721 ct.start(); 722 } 723 724 void showGUI() 725 { 726 loginFrame = new JFrame ("Ganymed SSH2 SwingShell"); 727 728 hostLabel = new JLabel ("Hostname:"); 729 userLabel = new JLabel ("Username:"); 730 731 hostField = new JTextField ("", 20); 732 userField = new JTextField ("", 10); 733 734 loginButton = new JButton ("Login"); 735 736 loginButton.addActionListener(new ActionListener () 737 { 738 public void actionPerformed(java.awt.event.ActionEvent e) 739 { 740 loginPressed(); 741 } 742 }); 743 744 JPanel loginPanel = new JPanel (); 745 746 loginPanel.add(hostLabel); 747 loginPanel.add(hostField); 748 loginPanel.add(userLabel); 749 loginPanel.add(userField); 750 loginPanel.add(loginButton); 751 752 loginFrame.getRootPane().setDefaultButton(loginButton); 753 754 loginFrame.getContentPane().add(loginPanel, BorderLayout.PAGE_START); 755 757 loginFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 758 759 loginFrame.pack(); 760 loginFrame.setResizable(false); 761 loginFrame.setLocationRelativeTo(null); 762 loginFrame.setVisible(true); 763 } 764 765 void startGUI() 766 { 767 Runnable r = new Runnable () 768 { 769 public void run() 770 { 771 showGUI(); 772 } 773 }; 774 775 SwingUtilities.invokeLater(r); 776 777 } 778 779 public static void main(String [] args) 780 { 781 SwingShell client = new SwingShell(); 782 client.startGUI(); 783 } 784 } 785 | Popular Tags |