3

I'm new to the whole topic of Java Card and tried to look at a few code examples to get a better understanding. I found a sample for AES usage in the oracle forum but have a few problems with the following part:

     private void doAES(APDU apdu)
     {

          byte b[] = apdu.getBuffer();

          short incomingLength = (short) (apdu.setIncomingAndReceive());
          if (incomingLength != 24) ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);

          //perform encryption and append results in APDU Buffer a[] automatically 

          cipherAES.init(aesKey, Cipher.MODE_ENCRYPT);
          cipherAES.doFinal(b, (short) dataOffset, incomingLength, a, (short) (dataOffset + 24));
          cipherAES.init(aesKey, Cipher.MODE_DECRYPT);
          cipherAES.doFinal(b, (short) (dataOffset + 24), incomingLength, a, (short) (dataOffset + 48));

          // Send results
          apdu.setOutgoing();
          apdu.setOutgoingLength((short) 72);
          apdu.sendBytesLong(b, (short) dataOffset, (short) 72);
     }

From my understanding this code takes the first 24 data bytes from the incoming APDU, encrypts them and puts them into the byte array a. Then it takes the next 24 data bytes, decrypts them and puts them into a too.

But the following commands don't use these output data since

apdu.sendBytesLong(b, (short) dataOffset, (short) 72);

uses b for the output data ... this is probably not correct so please help me understand where I went wrong.

Also: what would a simple command APDU for encrypting a small text with this and the corresponding answer look like ?

I.Dontknow
  • 33
  • 4
  • That example just hurt my eyes. Please don't use it. It's got very basic mistakes at the **Java** level. The buffer handling is completely off. It should of course decrypt from `a` instead of `b` and then send `a`. The variable `a` should be transient and should only be filled with the contents of the key when required. Personally I don't like the Sun forums that much at all, a moderated system like SO would kill off such examples quickly (then again, you're not supposed to post examples out of the blue at all). – Maarten Bodewes Sep 05 '15 at 11:49

1 Answers1

5

The code from the Oracle forum is not very good, actually. It does not follow basic rules of memory usage and it is not a real-world example at all. Moreover, it would be very slow and it could even damage your smart card if used too often.

I think you should firstly read through the Java Card tutorials and learn what APDU is and something about its structure, see this question:

How to get started with Java Cards?

Then you could proceed to Java Card encryption/decryption. Something like this might help you:

public class MiniApplet extends Applet {
     public static void install(byte[] bArray, short bOffset, byte bLength) {
        // GP-compliant JavaCard applet registration
        new MiniApplet().register(bArray, (short) (bOffset + 1),
                bArray[bOffset]);
     }

    private final AESKey aesKey = (AESKey)KeyBuilder.buildKey(KeyBuilder.TYPE_AES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_AES_128, false);
    private final Cipher aes = Cipher.getInstance(Cipher.ALG_AES_BLOCK_128_CBC_NOPAD, false);

    public void process(APDU apdu) {
    // Good practice: Return 9000 on SELECT
    if (selectingApplet()) {
        return;
    }

    final byte[] buf = apdu.getBuffer();
    final short dataLen = apdu.setIncomingAndReceive(); 
    final byte ins = buf[ISO7816.OFFSET_INS];

    switch (ins) {
    case (byte) 0x00: //KEY VALUE INIT FROM APDU
        if (dataLen != 16) //checking key value length
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH)
        aesKey.setKey(buf, ISO7816.OFFSET_CDATA);
        break;
    case (byte) 0x01: //DECRYPTION
    case (byte) 0x02: //ENCRYPTION
        if ((dataLen & 0x000F) != 0) //checking if input data is block-aligned
            ISOException.throwIt(ISO7816.SW_WRONG_LENGTH)

        if (!aesKey.isInitialized())
            ISOException.throwIt(ISO7816.SW_CONDITIONS_NOT_SATISFIED);
        aes.init(aesKey, (ins == 0x02) ? Cipher.MODE_ENCRYPT : Cipher.MODE_DECRYPT);
        aes.doFinal(buf, ISO7816.OFFSET_CDATA, dataLen, buf, ISO7816.OFFSET_CDATA);
        apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, dataLen);
        break;
    default:
        // good practice: If you don't know the INStruction, say so:
        ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
    }
}

}

Note: I initialize the key value from an input command in my example. My key is stored in RAM, which means the value disappears after each card reset or another applet selection. This does not have to fit your business case and it may be wiser to generate a secret key on the card just once and store it in the persistent memory. If so, you have to use a different keytype: KeyBuilder.TYPE_AES instead of KeyBuilder.TYPE_AES_TRANSIENT_DESELECT.

Community
  • 1
  • 1
vojta
  • 5,591
  • 2
  • 24
  • 64
  • Dear Vojta, 1- why did you defined `buf` and `dataLen` as `final`? 2- Don't we need to check if the `aesKey` is initialized or not in the start of case 0x01 and 0x02 blocks? – Ebrahim Ghasemi Sep 05 '15 at 08:03
  • @Abraham final variable = a little faster access. Yes, we probably should check it, as well as if the dataLen is alligned to block size (16). I will improve my answer. – vojta Sep 05 '15 at 09:47
  • 1
    @vojta The faster access never really materialized at this level. But marking things `final` clearly shows that the variable isn't to be changed later. It's always enabled in my Eclipse "clean code" feature, although I sometimes leave it out in my code samples here to avoid precisely this question :) – Maarten Bodewes Sep 06 '15 at 11:27