How to encrypt / decrypt with DES from JSR 177 SATSA API in J2ME applications

In this post we will see how to use the DES (Data Encryption Standard) algorithm implementation, in CBC and ECB modes, from the Security and Trust Services API (SATSA) for J2ME platform. The complete example will use the DES engine (in CBC or ECB mode) to encrypt and decrypt a file managed by a J2ME (Midlet) application.

The Security and Trust Services API (SATSA) specification defines optional packages for the Java 2 Platform, Micro Edition (J2ME) and is a lightweight cryptographic version of the standard version in JSE. The API is different for J2ME platform than the one for JDK platform because it provides fewer classes. Regarding the API, both the J2ME and JSE platforms use Cipher as the main class for cryptographic algorithms.

The specifications of the API are also described by the Java Specification Request 177 (JSR-177).

The SATSA API for Java provides a lightweight cryptographic API that works on mobile devices that have, at minimum, a CLDC 1.0 configuration and MIDP 2.0 profile.

In order to understand this example, you must have basic knowledge about:

I recommend:

The DES (Data Encryption Standard) algorithm has these important characteristics:

  • the first standard defined for the cryptographic protection of data;
  • developed by IBM since 1970 for American NBS (National Bureau of Standards);
  • published as FIPS PUBS 46 (Federal Information Processing Standards Publications);
  • in 1977 is named DES (Data Encryption Standard);
  • used until 1998 when it has been broken (the encryption key as been obtained in a relative short time from a set of encrypted messages); today it is still used, but the algorithm has been strengthen using a combination of one encryption, one decryption and one encryption – EDE or 3DES;
  • also described by the ANSI X3.92 standard – DEA (Data Encryption Algorithm)
  • is a symmetric cryptographic algorithm (the same key is used both for encryption and decryption)
  • processes blocks of 64 bits (8 bytes);
  • uses keys of 64 bits – 56 bits random generated (or from a password) and 8 bits for detecting transmissions errors;
  • effective both on Intel platforms and other software or hardware platforms;
  • it can be implemented on 32 bit processors and smart cards (8-bitprocessors);
  • slower than AES (Advanced Encryption Standard);
  • it is less secure than 3DES;
  • implements Feistel networks;

For DES, the SATSA API for J2ME platform provides one implementation (their description is taken from the SATSA API documentation):

Cipher – “This class provides the functionality of a cryptographic cipher for encryption and decryption. It forms the core of the Java Cryptographic Extension (JCE) framework.” [SATSA Documentation]

In this example, we will use the standard SATSA implementation of DES, that will process 64 bits (8 bytes) blocks and will use a 64 bit key.

Because, the files that are going to be encrypted may have or NOT (most of the times) a dimension that is multiple of block size (64 bits), we must use padding for the last block. In this case we will use the PKCS5Padding method, which is the only solution provided by SATSA (the other option is NoPadding, but it will work only for files with a dimension that is a multiple of block size).

 

The encryption solution (complete encryption/decryption solution file) is defined by these steps:

1. define the Cipher instance used for encryption, eCipher in this solution, and for decryption, dCipher; the Cipher class provides one static method (it doesn’t have public constructors), getInstance(), used to create Cipher instances; the method requires a transformation string that has 2 forms: “algorithm/mode/padding” or “algorithm”, [SATSA Documentation]; we will use the first form: "DES/ECB/PKCS5Padding"; as you can see, we will use the simplest encryption mode, which is Electronic Code Book – ECB (it doesn’t require an initialization vector as CBC does);

2. init the cipher for encryption with a key; the key could be predefined or received in the constructor; these 2 first steps are implemented by the constructors:

public class DES_ECB_CBC {
    Cipher eCipher;
    Cipher dCipher;

    // Buffer used to transport the bytes from one stream to another
    byte[] buf = new byte[8];       //input buffer - 8 bytes block
    byte[] obuf = new byte[1024];   //output buffer
    byte[] key = null;

    
    public DES_ECB_CBC()
            throws InvalidKeyException,
            NoSuchAlgorithmException,
            NoSuchPaddingException
    {
        key = "SECRET!!".getBytes();	//the key must be 64 bits long
        InitDESCiphersECBMode();
    }

    public DES_ECB_CBC(byte[] keyBytes)
            throws InvalidKeyException,
            NoSuchAlgorithmException,
            NoSuchPaddingException
    {
        key = new byte[keyBytes.length];
        System.arraycopy(keyBytes, 0 , key, 0, keyBytes.length);
        InitDESCiphersECBMode();
    }

    private void InitDESCiphersECBMode()
            throws InvalidKeyException,
            NoSuchAlgorithmException, 
            NoSuchPaddingException{
        eCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        dCipher = Cipher.getInstance("DES/ECB/PKCS5Padding");
        // ECB mode does NOT requires an initialization vector; only CBC mode
        eCipher.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(key, 0, key.length, "DES"));
        dCipher.init(Cipher.DECRYPT_MODE,
                new SecretKeySpec(key, 0, key.length, "DES"));
    }
}

 

3. if you want to use Cipher Block Chaining encryption mode (it’s safer that ECB) you must provide an initialization vector for the first iteration (for details see CBC mode); it can be received or it can be generated with a default value; the void InitDESCiphersCBCMode(byte[] iv) methods implements this solution;

private void InitDESCiphersCBCMode(byte[] iv)
            throws InvalidKeyException,
            NoSuchAlgorithmException,
            NoSuchPaddingException,
            InvalidAlgorithmParameterException{

        //set a default IV if not received
        if(iv == null)
            //the IV size is also 8 bytes  = size of a block
            iv = new byte[]{(byte)0x8E, 0x12, 0x39,
            (byte)0x9C,0x07, 0x72, 0x6F, 0x5A };

        IvParameterSpec ivparam = new IvParameterSpec(iv, 0, iv.length);

        eCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        dCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");

        // CBC requires an initialization vector
        eCipher.init(Cipher.ENCRYPT_MODE,
                new SecretKeySpec(key, 0, key.length, "DES"),ivparam);
        dCipher.init(Cipher.DECRYPT_MODE,
                new SecretKeySpec(key, 0, key.length, "DES"),ivparam);
    }

4. read bytes from the file; in the solution, we read 8 bytes blocks from the file (the input buffer has 8 bytes); each block is processed by the

int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset)

method; the output of the processed block is put in the out buffer which is written in the encrypted file.


5. VERY IMPORTANT STEP ! call the doFinal() method which will process the last block in the buffer; the internal mechanism of the algorithm implementation, based in its encryption mode (ECB, CBC, or other) keeps an internal buffer which must be also discarded into the output file (this is NOT the last block of the input file); the doFinal() it is a MUST DO step; it is a common error to forget it.

The last two steps are implemented by the encrypt function:

    public void encrypt(InputStream in, long length, OutputStream out)
    throws ShortBufferException, 
            IllegalBlockSizeException,
            BadPaddingException{
        try {
            // Bytes written to out will be encrypted
            // Read in the cleartext bytes and write to out to encrypt
            int numRead = 0;    //number of bytes read from input
            int numProc = 0;    //number of bytes processed
            long bytesToRead = length;

            while ((numRead = in.read(buf)) >= 0) {
                if (bytesToRead > buf.length)
                    numProc = eCipher.update(buf, 0, numRead, obuf, 0);
                else
                    numProc = eCipher.doFinal(buf, 0, numRead, obuf, 0);
                out.write(obuf, 0, numProc);
                bytesToRead-=numRead;
            }
            in.close();
            out.flush();
            out.close();
        }
        catch (java.io.IOException e) {
            System.out.println(e.getMessage());
        }
    }

The decryption solution is similar to the encryption one and is implemented by the decrypt method. The complete solution is implemented by the DES_ECB_CBC class in complete encryption/decryption solution file.

In order to use these two functions you must open the cleartext file and the encrypted one. Here is an example of using encryption in a MIDlet application:

void encryptFile(String fileName)
    {
         try {
            FileConnection fci =
                (FileConnection)Connector.open("file://localhost/" + 
		currDirName + fileName);
            if (!fci.exists()) {
                throw new IOException("File does not exists");
            }
             //createFile("encrypt.txt", false);
             FileConnection fco =
                (FileConnection)Connector.open("file://localhost/" + 
		currDirName + "encrypt.txt");
 
             if (!fco.exists())
                 fco.create();
 
            if (!fco.exists()) {
                throw new IOException("Can not create encrypted file");
            }
 
            InputStream fis = fci.openInputStream();
            OutputStream fos = fco.openOutputStream();
 
            DES_ECB_CBC encrypter = new DES_ECB_CBC();
 
	    // Encrypt
            encrypter.encrypt(fis, fci.fileSize(), fos);
 
            fis.close();
            fos.close();
 
            Alert alert =
                new Alert("Confirmation",
			"File encryption terminated", 
			null, 
			AlertType.INFO);
            alert.setTimeout(Alert.FOREVER);
            Display.getDisplay(this).setCurrent(alert);
         }
         catch (Exception e) {
            Alert alert =
                new Alert("Encryption error!",
                    "Can not access file " + fileName + 
		    " in directory " + currDirName +
                    "Exception: " + 
		    e.getMessage(), null, AlertType.ERROR);
            alert.setTimeout(Alert.FOREVER);
            Display.getDisplay(this).setCurrent(alert);
        }
    }

The complete solution is implemented by the DES_ECB_CBC class in this complete encryption/decryption solution file.

Related posts:

Like it? Then share this post or check the external adds. Sharing is the best way to appreciate the author.