SATSAMIDlet.java
/**
* Copyright (c) 2012 Nokia Corporation. All rights reserved.
* Nokia and Nokia Connecting People are registered trademarks of Nokia
Corporation.
* Oracle and Java are trademarks or registered trademarks of Oracle and/or its
* affiliates. Other product and company names mentioned herein may be trademarks
* or trade names of their respective owners.
* See LICENSE.TXT for license information.
*/
package com.nokia.example.satsa;
import com.nokia.mid.ui.orientation.Orientation;
import com.nokia.mid.ui.orientation.OrientationListener;
import java.security.GeneralSecurityException;
import javax.crypto.BadPaddingException;
import javax.crypto.ShortBufferException;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
// Main SATSA MIDlet class
// It controls the state of the user interface
// and handles the encryption and decryption process
// The class implements OrientationListener to detect a change in the orientation of the device's display */
public class SATSAMIDlet extends MIDlet implements OrientationListener{
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 {
/** Registers the orientation listener. Applications that need information about events related to display orientation changes need to register with Orientation to get notifications of the events.*/
Orientation.addOrientationListener(this);
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();
messageStore = new MessageStore(this);
// Startup the UI
showMessageList();
}
// 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 messages
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);
}
// adds a new message encrypting it an storing 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();
}
void exitMIDlet() {
if (messageStore != null) {
messageStore.close();
}
notifyDestroyed();
}
// 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 mutiple
// 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 when the password is incorrect
showError("Incorrect password.");
} catch (ShortBufferException bpe) {
showError("Incorrect password.");
} catch (GeneralSecurityException gse) {
// Handles all other exceptions
showError("General Security Exception while decrypting: "
+ gse.toString());
}
}
}
}
/** Called when display's orientation has changed. */
public void displayOrientationChanged( int newDisplayOrientation ){
/** Change MIDlet UI orientation */
Orientation.setAppOrientation(newDisplayOrientation);
}
}