Creating an auto-complete JComboBox

Main.java:

import javax.swing.plaf.basic.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.*;
 
public class Main extends JFrame
{
   public Main() {
      addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent we) {
            System.exit(1);
         }
      });
 
      getContentPane().setLayout(new FlowLayout(FlowLayout.LEFT));
 
      String[] elements = new String[] { "arthur", "brian", "chet", "danny", "dave",  
                                         "don", "edmond", "eddy", "glen", "gregory", "john",
                                         "ken", "malcolm", "pete", "stephanie", "yvonne" };
 
      JComboBox comboBox = new AutoCompleteComboBox(elements);
      getContentPane().add(comboBox);
   }
      
   public static void main(String []args) {
      Main main = new Main();
      main.setSize(300, 300);
      main.setVisible(true);
   }
}
 
class AutoCompleteComboBox extends JComboBox
{
   public int caretPos=0;
   public JTextField inputField=null;
 
   public AutoCompleteComboBox(final Object elements[]) {
      super(elements);
      setEditor(new BasicComboBoxEditor());
      setEditable(true);
   }
 
   public void setSelectedIndex(int index) {
      super.setSelectedIndex(index);
 
      inputField.setText(getItemAt(index).toString());
      inputField.setSelectionEnd(caretPos + tf.getText().length());
      inputField.moveCaretPosition(caretPos);
   }
 
   public void setEditor(ComboBoxEditor editor) {
      super.setEditor(editor);
      if (editor.getEditorComponent() instanceof JTextField) {
         inputField = (JTextField) editor.getEditorComponent();
 
         inputField.addKeyListener(new KeyAdapter() {
            public void keyReleased( KeyEvent ev ) {
               char key=ev.getKeyChar();
               if (! (Character.isLetterOrDigit(key) || Character.isSpaceChar(key) )) return;
 
               caretPos=inputField.getCaretPosition();
               String text="";
               try {
                  text=inputField.getText(0, caretPos);
               }
               catch (javax.swing.text.BadLocationException e) {
                  e.printStackTrace();
               }
 
               for (int i=0; i<getItemCount(); i++) {
                  String element = (String) getItemAt(i);
                  if (element.startsWith(text)) {
                     setSelectedIndex(i);
                     return;
                  }
               }
            }
         });
      }
   }
}

Rounding down a float or a double

Use the floor method in the Math class. It rounds down to the next lower integer:

import java.math.*;
                           
public class Main 
{  
   public static void main(String args[]) throws Exception {
      double d1 = 1.3, 
             d2 = 1.8,
             d3 = -1.3, 
             d4 = -1.8;
 
      System.out.println(Math.floor(d1));
      System.out.println(Math.floor(d2));
      System.out.println(Math.floor(d3));
      System.out.println(Math.floor(d4));
   }
}

outputs:

1.0
1.0
-2.0
-2.0

Using BigDecimals

As opposed to float or double, a BigDecimal allows you to store arbitrary precision signed decimal numbers.

Here’s a simple example that creates a BigDecimal.

Main.java:

import java.math.*;
import java.util.*;
 
public class Main {
   public static void main(String args[]) {
      BigDecimal bd1 = new BigDecimal(createLongDecimal());
      BigDecimal bd2 = new BigDecimal(createLongDecimal());
 
      BigDecimal total = bd1.add(bd2);
 
      System.out.println("  " + bd1);
      System.out.println("+ " + bd2);
      System.out.println("= " + total);
   }
 
   public static String createLongDecimal() {
      Random r = new Random();
      StringBuffer total = new StringBuffer();
      for (int i=0; i<15; i++) {
         total.append(Math.abs(r.nextInt()));
         if (i == 10) total.append('.');
      }
  
      return total.toString();
   }
}

outputs:

  720503977823516025489207568136178482425783671110507142011860888752101
66134093438986271599747576765999033.18149285701107649443668155859929740
+ 720503977823516025489207568136178482425783671110507142011860888752101
66134093438986271599747576765999033.18149285701107649443668155859929740
= 144100795564703205097841513627235696485156734222101428402372177750420
332268186877972543199495153531998066.36298571402215298887336311719859480

There are other ways to create a BigDecimal, a constructor that allows you to pass a double and one with a BigInteger.
The constructor that accepts a double is “somewhat inpredictable”, according to the API.
Consider the following program.

Main.java:

import java.math.*;
  
public class Main {
   public static void main(String args[]) {
      BigDecimal bd1 = new BigDecimal(.1);
 
      System.out.println(bd1);
   } 
}

outputs:

0.1000000000000000055511151231257827021181583404541015625

Sending email through SMTP with Java

The optional package JavaMail allows you to do this. I’ve devoted a separate category that tells you where you can d/l it plus some tutorials and links.

This following class doesn’t use JavaMail, but it uses SMTP commands to allow to send a simple mail. To run it, you need to specify an SMTP server, a from and to email address, subject and body.

Main.java:

import java.io.*; 
 
public class Main {
   public static void main(String[] args) {
      if (args.length != 5) {
         System.err.println("usage: java Main <SMTP server> <from> <to> " + 
                            "<subject> <body>");
         System.exit(1);
      }
  
      try {
         SMTPEmail smtp = new SMTPEmail(args[0]);
         smtp.sendMail(args[1], args[2], args[3], args[4]);
      }
      catch(IOException e) {
         e.printStackTrace();
      }           
   }
}

SMTPEmail.java:

import java.io.*;
import java.net.*;
 
public class SMTPEmail
{
    private Socket smtpSocket;
    private BufferedReader br;
    private BufferedWriter bw;
       
    private String smtpHost;
    
    public SMTPEmail(String smtpHost) throws IOException {
        this.smtpHost = smtpHost;
    }
    
    public void connect() throws IOException {
        smtpSocket = new Socket(smtpHost, 25);
        br = new BufferedReader(new InputStreamReader(smtpSocket.getInputStream()));
        bw = new BufferedWriter(new OutputStreamWriter(smtpSocket.getOutputStream()));
    }
    
    private void disconnect() throws IOException {
        smtpSocket.close();
    }
    
    private void send(String str) throws IOException {
        System.out.println("Sending: " + str);
        bw.write(str + "rn");
        bw.flush();
    }
    
    private String receive() throws IOException {
        String str = br.readLine();
        System.out.println("Receiving: " + str);
        return str;
    }
    
    public void sendMail(String fromAddress, String toAddress, String subject, 
                         String body) throws IOException {
        connect();
        String whoami = InetAddress.getLocalHost().getHostName();
        send("HELO " + whoami);
        receive();
        send("MAIL FROM: <" + fromAddress + "> ");
        receive();
        send("RCPT TO: <" + toAddress + "> ");
        receive();
        send("DATA");
        receive();
        send("TO: " + toAddress);
        send("FROM: " + fromAddress);
        send("SUBJECT: " + subject + "rn");
        send(body);
        send("rn.rn");
        receive();
        send("QUIT");
        disconnect();
    }
}

Downloading a secure page (HTTPS) with Java

If you try to get data from an HTTPS-enabled page, you will get a MalformedURLException, as shown in following example:

Main.java:

import java.net.*;
import java.io.*;
 
public class Main {
   public static void main(String[] args) throws Exception {
      URL url = new URL("https://www.sun.com");
      BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
      String line;
      
      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }
  
      br.close();
   }
}

outputs:

C:>java Main
Exception in thread "main" java.net.MalformedURLException: unknown protocol: https
        at java.net.URL.<init>(URL.java:497)
        at java.net.URL.<init>(URL.java:364)
        at java.net.URL.<init>(URL.java:308)
        at Main.main(Main.java, Compiled Code)

This is because Sun has no implementation for the HTTPS protocol in their core libraries. However, they created an reference implementation called Java Secure Socket Extension (JSSE) available as seperate download at http://java.sun.com/products/jsse.

Include jcert.jar, jnet.jar and jsse.jar in your classpath and add the following two lines of code to the previous program:

      System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); 
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());

Here’s the updated example that works:

import java.security.*;
import java.net.*;
import java.io.*;
 
public class Main {
   public static void main(String[] args) throws Exception {
      // create a new protocol handler
      System.setProperty("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol");
 
      // protocol handler uses this security provider
      Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
  
      URL url = new URL("https://www.sun.com");
      BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()));
      String line;
      
      while ((line = br.readLine()) != null) {
         System.out.println(line);
      }
 
      br.close();
   }
}

What is PKCS#5 padding?

Ciphers process data in blocks, for example 8 byte blocks. If the length of the data that you want to encrypt is not evenly divisible by the blocksize that your encryption algorithm uses, the data needs to be padded. PKCS#5 is one way of padding. It appends to every message a block of data varying from 1 to 8 bytes where each bytes contain the number of bytes that are padded.

For instance, if the length of the data is 10, then 6 bytes need to be padded. The last block of 8 bytes will look like this:

 +---+---+---+---+---+---+---+---+
 | D | D | 6 | 6 | 6 | 6 | 6 | 6 |
 +---+---+---+---+---+---+---+---+

If the length of your data happens to be evenly divisible by 8, an extra 8 bytes will be added looking like this:

 +---+---+---+---+---+---+---+---+
 | 8 | 8 | 8 | 8 | 8 | 8 | 8 | 8 |
 +---+---+---+---+---+---+---+---+

This is because PKCS5Padding always assumes there is a pad.

Encrypting/decrypting with Triple-DES

Triple-DES or DESede is an improvement over DES (Data Encryption Standard). It uses three DES keys k1, k2 and k3. A message is encrypted with k1 first, then decrypted with k2 and encrypted again with k3 (DESencryptiondecryptionencryption). This increases security as the key length effectively increases from 56 to 112 or 168 (two or three keys may be used in DESede). As a programmer you don’t have to worry about managing several keys, they are all encoded into one key.

The DESede key size is 128 or 192 bit and block size 64 bit.

Eventually, DES and Triple-DES will be replaced by the Advanced Encryption Standard (AES).

Main.java:

import javax.crypto.spec.*;
import java.security.*;
import javax.crypto.*;
 
public class Main
{
   public static void main(String []args) throws Exception {
      String toEncrypt = "The shorter you live, the longer you're dead!";
 
      System.out.println("Encrypting...");
      byte[] encrypted = encrypt(toEncrypt, "password");
 
      System.out.println("Decrypting...");
      String decrypted = decrypt(encrypted, "password");
    
      System.out.println("Decrypted text: " + decrypted);
   } 
 
   public static byte[] encrypt(String toEncrypt, String key) throws Exception {
      // create a binary key from the argument key (seed)
      SecureRandom sr = new SecureRandom(key.getBytes());
      KeyGenerator kg = KeyGenerator.getInstance("DESede");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // create an instance of cipher
      Cipher cipher = Cipher.getInstance("DESede");
 
      // initialize the cipher with the key 
      cipher.init(Cipher.ENCRYPT_MODE, sk);
 
      // enctypt!
      byte[] encrypted = cipher.doFinal(toEncrypt.getBytes());
 
      return encrypted;
   }
 
   public static String decrypt(byte[] toDecrypt, String key) throws Exception {
      // create a binary key from the argument key (seed)
      SecureRandom sr = new SecureRandom(key.getBytes());
      KeyGenerator kg = KeyGenerator.getInstance("DESede");
      kg.init(sr);
      SecretKey sk = kg.generateKey();
 
      // do the decryption with that key
      Cipher cipher = Cipher.getInstance("DESede");
      cipher.init(Cipher.DECRYPT_MODE, sk);
      byte[] decrypted = cipher.doFinal(toDecrypt);
 
      return new String(decrypted);
   }
}

(tested with the BouncyCastle JCE provider, http://www.bouncycastle.org)

Writing a client/server app that makes use of SSL

To create a client and a server that exchange encrypted messages, you can use the JSSE package that can be downloaded as an optional package or comes standard with JDK1.4. I’ll assume your have correctly downloaded and installed the package or are using JDK1.4.

SSL stands for Secure Socket Layer. It contains a handshake protocol where two parties exchange a series of messages to agree on a symmetric key that will be used to encrypt the rest of the messages that are exchanged throughout the session. The problem is how to let the other party know what key to use. There can always be someone spying on the messages that are sent over the network.

The solution is to use public/private key cryptography. A message encrypted with a private key can be decrypted with the associated public key and vice versa. Only one party holds his own private key and can distribute his public key to anyone, even the spy. The other party encrypts the message with the public key and sends it to the other party, which is the only one that can decrypt the message as he, and only he, holds the private key.

Party A could encrypt the symmetric key information using the public key of party B and sends him this info. Party B decrypts it and obtaines that symmetric key which they both will use during the session.

To ensure that the public key of the Party B actually belongs to him, and is not something made up of a spy claiming he is party B, he can use a public-key certificate. A public-key certificate is issued by a certification authority (CA) which you typically have to pay a couple hundred dollars and provide proof of identity in order to obtain it.

In short, the whole process goes like this.
Assume Party B is the server and party A the client.

  1. Party A and Party B agree on an SSL version and a set of encryption algorithms.
  2. Party B sends Party B the public key/certificate.
  3. Party B may also request Party A to send his public key/certificate if he wants
    to authenticate the client (eg. in banking applications).

  • Party A generates information to create a symmetric key and sends it to Party B,
    but encrypted with the public key of Party B.
  • Party A may also send Party B his public key information if Party B requested for it.
  • Now both parties have received the symmetric key and can start exchanging actual data.

    One more thing: even though messages are send encrypted, a spy may still tamper with the data that flows between A and B. To ensure data integrity, messages are appended with an HMAC before being encrypted. The result of an HMAC (Message Authentication Code) is a small series of bytes that can be used to detect the slightest change in data, even if it’s only one bit. The whole shebang (message + HMAC) are encrypted with the secret symmetric key and is send over. The receiver then decrypts the message, calculates the HMAC again, and compares it with the one that was send to him.

    Writing the server

    Instead of creating a ServerSocket using ServerSocket ss = new ServerSocket(port), we would use the SSLServerSocketFactory that creates a ServerSocket that supports SSL. This SSL ServerSocket takes care of all the gory details

    For more information, check out the excellent JSSE Sun’s User’s Guide here.

    Start of by creating a self-signed certificate that is needed by the server to send back
    to the client during the SSL handshake.

    C:securityssl>keytool -v -genkey -keyalg RSA -keystore .keystore
    Enter keystore password:  esuspass
    What is your first and last name?
      [Unknown]:  Joris Van den Bogaert
    What is the name of your organizational unit?
      [Unknown]:  Esus Team
    What is the name of your organization?
      [Unknown]:  Esus, Inc
    What is the name of your City or Locality?
      [Unknown]:  Meerbeek
    What is the name of your State or Province?
      [Unknown]:
    What is the two-letter country code for this unit?
      [Unknown]:  BE
    Is <CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc", L=Meerbeek, ST=Unkno
    n, C=BE> correct?
      [no]:  yes
    
    Generating 1024 bit RSA key pair and self-signed certificate (MD5WithRSA)
            for: CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc", L=Meerbeek,
    ST=Unknown, C=BE
    Enter key password for <mykey>
            (RETURN if same as keystore password):
    [Saving .keystore]
    
    C:securityssl>dir .keystore
    
     Volume in drive C has no label
     Volume Serial Number is 1380-0FE3
     Directory of C:securityssl
    
    KEYSTO~1             1,379  07-24-01  3:53p .keystore
             1 file(s)          1,379 bytes
             0 dir(s)     146,989,056 bytes free
    

    Here’s the code for SecureServer.java and SecureClient.java:

    SecureServer.java:

    import javax.net.ssl.*;
    import java.net.*;
    import java.io.*;
     
    public class SecureServer {
       private static final int port = 4321;
     
       public static void main(String []args) throws Exception {
          SSLServerSocketFactory ssf = (SSLServerSocketFactory)
                                          SSLServerSocketFactory.getDefault();
          ServerSocket ss = ssf.createServerSocket(port);
     
          System.out.println("Ready to accept messages!");
          Socket s = ss.accept();
     
          System.out.println("A client has connected!");
          DataInputStream dis = new DataInputStream(s.getInputStream());
          DataOutputStream dos = new DataOutputStream(s.getOutputStream());
          String line = null;
          try {
             while (true) {
                line = dis.readUTF();
                System.out.println("Client sent: " + line);
                dos.writeUTF("You send: " + line);
             } 
          }
          finally {
             dis.close();
             dos.close();
             s.close();
          }
       }
    }

    SecureClient.java:

    import javax.net.ssl.*;
    import java.net.*;
    import java.io.*;
     
    public class SecureClient 
    {
       private static final String host = "127.0.0.1";
       private static int port = 4321;
     
       public static void main(String []args) throws Exception {
          SSLSocketFactory sf = (SSLSocketFactory) SSLSocketFactory.getDefault();
          Socket s = sf.createSocket(host, port); 
     
          DataInputStream dis = new DataInputStream(s.getInputStream());
          DataOutputStream dos = new DataOutputStream(s.getOutputStream());
     
          System.out.println("Connected.n");
          System.out.println("Type messages to send to server, exit to end!");
    
          BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 
          String line;
          while ((line = br.readLine()) != null) {
             if (line.equals("exit")) break;
     
             dos.writeUTF(line);
             dos.flush();
     
             String reply = dis.readUTF();
             System.out.println("Server reply: " + reply);
          }
     
          dis.close();
          dos.close(); 
          s.close();
       }
    }
    

    Note: change the IP address (127.0.0.1) in SecureClient.java to the IP address where your server is deployed. To test it, we’ll run it all on the same machine.

    The file .keystore containing the self-signed certificate should be located on the server’s machine. Put both the files SecureServer.class and .keystore in the directory c:\security\ssl\server and SecureClient.class in c:\securitysslclient.
    The server should know where it can find its private key. The keystore can be specified at runtime along with the password in order to access it.
    The client needs to have access to the certificate in order to check it. Certain certificates, issued by a Certificate Authority (CA) are automatically trusted (they are saved in the file /lib/security/cacerts. You can export the certificate from the server’s .keystore into a file and specify its location at the command line when running the client (trustStore). If your key is signed by a CA, then you don’t need to specify it at command line as it is contained in the file /lib/security/cacerts. This is the order in which trustStores are checked:

       1) a trustStore specified in the javax.net.ssl.trustStore property
       2) <i><JRE_HOME>/lib/security/jssecacerts</i>
       3) <i><JRE_HOME>/lib/security/cacerst</i>
    

    I’ll show you how to run the example by specifying the trustStore at command line, and by adding the certificate to the cacerts file.

    First we need to export the certificate:

    C:\security\ssl\server> keytool -export -keystore .keystore -file ssltest.cer
    Enter keystore password:  esuspass
    Certificate stored in file <ssltest.cer>
    

    Running SecureServer and SecureClient by specifying the trustStore

    On the client side, create a truststore .trustStore and import certificate contained in the file ssltest.cer:

    C:\security\ssl\client>copy ..\server ssltest.cer
            1 file(s) copied
    
    C:\security\ssl\client>keytool -import -keystore .trustStore -file ssltest.cer
    Enter keystore password:  esuspass
    Owner: CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc", L=Meerbeek, ST=Unk
    nown, C=BE
    Issuer: CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc", L=Meerbeek, ST=Un
    known, C=BE
    Serial number: 3b5d7dd7
    Valid from: Tue Jul 24 15:53:27 CEST 2001 until: Mon Oct 22 15:53:27 CEST 2001
    Certificate fingerprints:
             MD5:  07:DE:52:94:15:1D:53:88:C5:C5:51:48:0F:AF:83:76
             SHA1: A7:92:CF:29:EF:B9:74:05:6F:05:E5:75:4B:78:42:D1:35:8B:D7:55
    Trust this certificate? [no]:  yes
    Certificate was added to keystore
    

    Open another terminal to start running the server:

    C:\security\ssl\server> c:\jdk1.4\bin\java -Djavax.net.ssl.keyStore=.keystore -Dj
    avax.net.ssl.keyStorePassword=esuspass SecureServer
    

    Now wait a couple seconds until the server shows up Ready to accept messages!.

    Run the client by specifying the trustStore:

    C:\security\ssl\client>c:\jdk1.4\bin\java -Djavax.net.ssl.trustStore=.trustStore SecureClient
    

    This is the result at the server’s side:

    C:\security\ssl\server> c:\jdk1.4\bin\java -Djavax.net.ssl.keyStore=.keystore -Djavax.net.ssl.keyStorePassword=esuspass SecureServer
    Ready to accept messages!
    A client has connected!
    Client sent: whoever spies on this connection
    Client sent: can't see crap
    Client sent: or should i say... only crap
    EOF!

    and the result at the client’s side:

    C:\security\ssl\client> c:\jdk1.4\bin\java -Djavax.net.ssl.trustStore=.trustStore SecureClient
    Connected.
    
    Type messages to send to server, exit to end!
    whoever spies on this connection
    Server reply: You send: whoever spies on this connection
    can't see crap
    Server reply: You send: can't see crap
    or should i say... only crap
    Server reply: You send: or should i say... only crap
    exit
    

    To include our self-signed certificate in the default cacerts file:

    C:\jdk1.4\jre\lib\security> copy c:\securitysslserver ssltest.cer
            1 file(s) copied
     
    C:\jdk1.4\jre\lib\security> keytool -import -keystore cacerts -file ssltest.cer
    Enter keystore password:  changeit
    Owner: CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc", L=Meerbeek, ST=Unk
    nown, C=BE
    Issuer: CN=Joris Van den Bogaert, OU=Esus Team, O="Esus, Inc", L=Meerbeek, ST=Un
    known, C=BE
    Serial number: 3b5d7dd7
    Valid from: Tue Jul 24 15:53:27 CEST 2001 until: Mon Oct 22 15:53:27 CEST 2001
    Certificate fingerprints:
             MD5:  07:DE:52:94:15:1D:53:88:C5:C5:51:48:0F:AF:83:76
             SHA1: A7:92:CF:29:EF:B9:74:05:6F:05:E5:75:4B:78:42:D1:35:8B:D7:55
    Trust this certificate? [no]:  yes
    

    Run the server again. Now run the client without specifying the trustStore and see what happens!