Hector Irvin Punzalan Hector Irvin Punzalan - 7 months ago 20
Java Question

Java card encryption using AES returns 6F00

I'm new to Java card programming. I saw an example regarding AES encryption method for Java cards and was able to create and install the applet. When I try to communicate with the applet, it returns a

6F00
error message. What am I doing wrong?

Here's a sample of my code:

private void SendData(APDU apdu)
{

aesKeyTrial= (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
byte[] outBuffer;
outBuffer = new byte[16];
byte[] trialKey;
trialKey = new byte[16];
byte[] input = {(byte)0x44,(byte)0x22,(byte)0x33,(byte)0x44,(byte)0x55,(byte)0x66,(byte)0x77,(byte)0x88,(byte)0x99,0x10,(byte)0xA2, 0x35, (byte)0x5E,0x15,0x16,0x14};
byte[] key = {0x2d, 0x2a, 0x2d, 0x42, 0x55, 0x49, 0x4c, 0x44, 0x41, 0x43, 0x4f, 0x44, 0x45, 0x2d, 0x2a, 0x2d};
byte[] buffer = apdu.getBuffer();
short len = (short) input.length;


aesKeyTrial.setKey(trialKey,(short)0);
if(len<=0||len%16!=0)
{
ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
}
aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);
aesCipher.init(aesKeyTrial, Cipher.MODE_ENCRYPT);
aesCipher.doFinal(input, ISO7816.OFFSET_CDATA, len, outBuffer, (short)0);
Util.arrayCopyNonAtomic(input, (short)0, buffer, (short)0, (short)len);
apdu.setOutgoing();
apdu.setOutgoingLength(len);
apdu.sendBytes((short) 0, (short)len);
}


The one I'm trying to encrypt is "input".

Answer

As you said in your answer, the problem of your code is that you passed wrong parameters to the doFinal (byte[] inBuff, short inOffset, short inLength,byte[] outBuff, short outOffset) method.

But, the sample code that you are using seems really bad. There are reasons that I call it bad-written:

1- It used dynamic variables in a method that is called so many times. Dynamic variables are those whom created using new keyword. These variables will allocated in EEPROM. Wrting in EEPROM is really slow rather than RAM and it also has a limited erase-write operations before it wear out, so it highly recommended to use RAM instead of EEPROM in cases that you don't need to share the data between different CAD (Card Reader) sessions. And also because there is no mandatory/automatic garbage collector in Java Card, do not define different dynamic variables repetitive, use one global instead (class variable or instance variable instead of local variables).

2- As int data type is optional in JavaCards, you must try to use short and byte as much as possible and you also need to cast all values to these types to prevent signed/unsigned problems.

Finally, I provide you an evolved version of your program:

package soQusetion;

import javacard.framework.*;
import javacard.security.AESKey;
import javacard.security.KeyBuilder;
import javacardx.crypto.Cipher;

public class SOQ1 extends Applet {

    Cipher aesCipher;
    AESKey aesKey;

    //Two different types of memory for different usage. each one has 16 (= 0x10) byte capacity.
    private byte[] volatileMem;
    private byte[] nonVolatileMem;

    //INS value for APDU command
    public static final byte INS_SET_KEY = 0x10;
    public static final byte INS_ENCRYPT = 0x20;
    public static final byte INS_DECRYPT = 0x30;

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new SOQ1();
    }

    protected SOQ1() {
        volatileMem = JCSystem.makeTransientByteArray((short) 0x10, JCSystem.CLEAR_ON_DESELECT);
        nonVolatileMem = new byte[(short) 0x10];
        aesCipher = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_ECB_NOPAD, false);
        aesKey = (AESKey) KeyBuilder.buildKey(KeyBuilder.TYPE_AES, KeyBuilder.LENGTH_AES_128, false);
        register();
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }
        byte[] buffer = apdu.getBuffer();
        apdu.setIncomingAndReceive();

        if (buffer[ISO7816.OFFSET_LC] != (byte) 0x10) {
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
        }

        switch (buffer[ISO7816.OFFSET_INS]) {
            case INS_SET_KEY:
                aesKey.setKey(buffer, ISO7816.OFFSET_CDATA);
                break;
            case INS_ENCRYPT:
                aesCipher.init(aesKey, Cipher.MODE_ENCRYPT);
                aesCipher.doFinal(buffer, ISO7816.OFFSET_CDATA, (short) 0x10, volatileMem, (short) 0x00);
                Util.arrayCopyNonAtomic(volatileMem, (short) 0x00, buffer, (short) 0x00, (short) 0x10);
                apdu.setOutgoingAndSend((short) 0x00, (short) 0x10);
                break;
            case INS_DECRYPT:
                aesCipher.init(aesKey, Cipher.MODE_DECRYPT);
                aesCipher.doFinal(buffer, ISO7816.OFFSET_CDATA, (short) 0x10, volatileMem, (short) 0x00);
                Util.arrayCopyNonAtomic(volatileMem, (short) 0x00, buffer, (short) 0x00, (short) 0x10);
                apdu.setOutgoingAndSend((short) 0x00, (short) 0x10);
                break;
            default:
                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }

    }
}

It works as below:

Select Applet begin...
Select Applet successful.
//Loading AES Key APDU Command
Send: 00 10 00 00 10 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00
Recv: 90 00
//Encrypt APDU Command
Send: 00 20 00 00 10 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 00
Recv: 62 F6 79 BE 2B F0 D9 31 64 1E 03 9C A3 40 1B B2 90 00
//Decrypt APDU Command
Send: 00 30 00 00 10 62 F6 79 BE 2B F0 D9 31 64 1E 03 9C A3 40 1B B2 00
Recv: 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF 90 00

Any suggestion to make the above program more efficient is welcome to me.