Implementing the SATSAMIDlet

This is the main class, and it controls the state changes between screens. It also calls the Codec class for encrypting (addNewMessage method) and decrypting (showDecryptedMessage method) messages. Notice that the calls to Codec needed to encrypt and decrypt messages are done in a separate thread using the inner class CryptoRunnable

To create the class:

  1. Assign the class to the package satsa and import the required classes.

    package satsa;
    
    import java.security.GeneralSecurityException;
    import javax.crypto.BadPaddingException;
    import javax.microedition.lcdui.Display;
    import javax.microedition.midlet.MIDlet;
    import javax.microedition.midlet.MIDletStateChangeException;
  2. Create the class, initialize the internal fields, and start up the UI.

    // Main SATSA MIDlet class
    // It controls the state of the user interface
    // and handles the encryption and decryption process
    public class SATSAMIDlet extends MIDlet {
      private Display display;    
      private MessageStore messageStore;
      private ListScreen listScreen;
      private EncryptScreen encryptScreen;
      private PasswordScreen passwordScreen;
      private DecryptScreen decryptScreen;
      private InfoScreen infoScreen;
      private NewScreen newScreen;
      private Codec codec;
    
      public SATSAMIDlet() {}
    
      protected void destroyApp(boolean unconditional)
        throws MIDletStateChangeException
      {
        exitMIDlet();
      }
    
      protected void pauseApp()
      {
      }
    
      protected void startApp()
        throws MIDletStateChangeException
      {
        if (display == null)
        {
          initMIDlet();
        }
      }
    
      // Initialize all the internal fields
      private void initMIDlet()
      {
        // Init the user interface
        display = Display.getDisplay(this);
        listScreen = new ListScreen(this);
        encryptScreen = new EncryptScreen(this);
        passwordScreen = new PasswordScreen(this);
        decryptScreen = new DecryptScreen(this);
        infoScreen = new InfoScreen();
        newScreen = new NewScreen(this);
        
        // Initialize the Cryptographic Codec and the message store
        codec = new Codec(this);
        messageStore = new MessageStore(this);
        
        // Startup the UI
        showMessageList();
      }
  3. First show a list of messages and then decrypt the chosen message, display the password screen, and then show the decrypted message. Call the Codec class by using the showEncryptedMessage() method to decrypt the message. In addition, create an instance of the inner class CryptoRunnable to handle the decrypting process in a separate thread.

      // Shows the list of existing messages
      void showMessageList()
      {
        messageStore.fillList(listScreen);
        display.setCurrent(listScreen);
      }
    
      // Shows an encrypted message in HEX format
      void showEncryptedMessage(int index)
      {
        encryptScreen.setIndex(index);
        encryptScreen.setMessage(codec
            .byteToHex(messageStore.getMessage(index+1)));
        display.setCurrent(encryptScreen);
      }
    
      // Displays the password screen
      void showPasswordScreen(int index)
      {
        passwordScreen.setIndex(index);
        display.setCurrent(passwordScreen);
      }
    
      // Shows a decrypted message
      void showDecryptedMessage(int index, String password)
      {
        // 128bit (16 characters) key is needed for AES
        // just fill with blanks if too short
        while (password.length() <16 )
        {
          password = password.concat(" ");
        }
        decryptScreen.setIndex(index);
        byte message[] = messageStore.getMessage(index+1);
        // Do the decryption and validation in a separate thread
        new Thread(new CryptoRunnable(message,
            password.getBytes(),
            false)).start();
      }
    
      // Display an error message
      void showError(String messageString)
      {
        infoScreen.showError(messageString, Display.getDisplay(this));
      }
    
      // Delete a message
      void deleteMessage(int index)
      {
        messageStore.deleteMessage(index+1);
        messageStore.fillList(listScreen);
      }
    
      // Shows the new message
      void showNewMessage()
      {
        newScreen.createForm();
        display.setCurrent(newScreen);
      }
      void exitMIDlet()
      {
        if (messageStore != null) {
         messageStore.close();
        }   
        notifyDestroyed();
      }
      
    
  4. Call the Codec class for encrypting the message by using the addNewMessage() method. Create an instance of the inner class CryptoRunnable to handle the encrypting process in a separate thread.

      // adds a new message encrypting it an storing it in the record store
      void addNewMessage(String message, String password)
      {
        // 128bit (16 characters) key is needed for AES
        // just fill with blanks if too short
        while (password.length() <16 )
        {
          password = password.concat(" ");
        }
        // Do the encryption in a separate thread
        new Thread(new CryptoRunnable(message.getBytes(),
            password.getBytes(),
            true)).start();      
      }
  5. Create a new inner class CryptoRunnable to handle the decryption and encryption process in a separate thread.

      // Internal class to handle the decryption and encryption
      // process in a separate thread as recommended in SATSA-CRYPTO
      private class CryptoRunnable implements Runnable {
        private byte[] message, password;
        private boolean encryption;
        
        CryptoRunnable(byte[] message, byte[] password, boolean encryption) {
          this.message = message;
          this.password = password;
          this.encryption = encryption;
        }
        
        public void run() {
          if (encryption) {
            try
            {
              // Encrypt the message
              byte[] encryptedMessage = codec.encrypt(password, message);
              // Calculate the digest of the encrypted message
              byte[] digest = codec.digest(encryptedMessage);
              // Compose the overall message
              byte[] messageBytes = 
                new byte[encryptedMessage.length + digest.length];
              System.arraycopy(digest, 0, messageBytes, 0, digest.length);
              System.arraycopy(encryptedMessage,
                  0,
                  messageBytes,
                  digest.length,
                  encryptedMessage.length);
              // Store the encoded message
              messageStore.addMessage(messageBytes);
            }
            catch (GeneralSecurityException gse)
            {
              // Use of generic Exception type since multiple
              // exceptions may be thrown at this point
              showError("General Security Exception while encrypting: "
                  + gse.toString());
            }
            showMessageList();
          } else {
            try
            {
              // Get the cipher text and the digest
              // SHA-1 digest is 160 bits long
              byte[] digest = new byte[20];
              byte[] cipherText = new byte[message.length-digest.length];
              // Decompose the message
              System.arraycopy(message, 0, digest, 0, digest.length);
              System.arraycopy(message,
                  digest.length,
                  cipherText,
                  0,
                  cipherText.length);
              
              // Verify the cipher's text digest
              if (codec.isDigestValid(cipherText, digest)) {
                // If digest is ok, let's decrypt
                byte[] plainText = codec.decrypt(password, cipherText);        
                 
                // Display the message on screen
                decryptScreen.setMessage(new String(plainText));
                display.setCurrent(decryptScreen);
              } else {
                showError("Digest of message is not valid");
              }            
            }
            catch (BadPaddingException bpe)
            {
              // This is a particular exception raised when
       // the password is incorrect
              showError("Incorrect password.");
            }
            catch (GeneralSecurityException gse)
            {
              // Handles all other exceptions
              showError("General Security Exception while decrypting: "
                  + gse.toString());
            }
          }
        }
      }
    }