2

I have a java card applet that implements encryption and decryption using RSA PKCS#1 with key CRT (Chinese Remainder Theorem) 1024. Here some of my code below:

Constructor:

    private CryptoApplet() {
        generateKeyPair();

        cipher = Cipher.getInstance(Cipher.ALG_RSA_PKCS1, false);

        outputByte = new byte[256];
        outputByte2 = new byte[117];
        outBuff = new byte[128];
        outBuff2 = new byte[117];
    }

Encrypt function (java card):

private void encrypt(APDU apdu) {
        byte[] buffer = apdu.getBuffer();
        short lc = apdu.getIncomingLength();
        short dataOffset = apdu.getOffsetCdata();

        short blocksize = 117;
        short inOffset = (short) 0;
        short outOffset = (short) 0;

        if (!pubKey.isInitialized() || pubKey == null) {
            ISOException.throwIt(STORAGE_KU_NOT_INITIALIZED);
        }
        cipher.init(pubKey, Cipher.MODE_ENCRYPT);

        short dataLeft = lc;
        while (dataLeft > 0) {
            if (dataLeft < blocksize)
                blocksize = dataLeft;

            outOffset = cipher.doFinal(buffer, (short) dataOffset, (short) blocksize, outBuff, (short) 0);
            Util.arrayCopy(outBuff,(short) 0,outputByte,inOffset,(short) outOffset);

            inOffset += outOffset;
            dataOffset += blocksize;
            dataLeft -= blocksize;
        }

        apdu.setOutgoingAndSend((short) 0, (short) outputByte.length);
    }

Decrypt function (java card):

private void decrypt(APDU apdu) {
        byte[] buffer = apdu.getBuffer();
        short lc = apdu.getIncomingLength();
        short dataOffset = apdu.getOffsetCdata();

        short blocksize = 128;
        short inOffset = (short) 0;
        short outOffset = (short) 0;

        cipher.init((RSAPrivateCrtKey) keys.getPrivate(), Cipher.MODE_DECRYPT);

        short dataLeft = lc;
        while (dataLeft > 0) {
            if (dataLeft < blocksize)
                blocksize = dataLeft;

            outOffset = cipher.doFinal(buffer, (short) dataOffset, (short) blocksize, outBuff2, (short) 0);
            Util.arrayCopy(outBuff2,(short) 0,outputByte2,inOffset,(short) outOffset);

            inOffset += outOffset;
            dataOffset += blocksize;
            dataLeft -= blocksize;
        }

        apdu.setOutgoingAndSend((short) 0, (short) outputByte2.length);
    }

The decrypt and encrypt function called from python code below (using package pyscard) and it works fine for encrypt. But, it got an error (response sw1 = 6F and sw2 = 00) everytime I try to call decrypt function.

Encrypt caller (python/pyscard) (function 0x41):

#! /usr/bin/env python
from smartcard.System import readers
from smartcard.util import *
import struct

# define the APDUs used in this script
f_input = open('4x6.JPG','rb')
list_input = list(bytearray(f_input.read()))
f_input.close()

SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00]

# get all the available readers
r = readers()
print "Available readers:", r

reader = r[0]
print "Using:", reader

connection = reader.createConnection()
connection.connect()

data, sw1, sw2 = connection.transmit(SELECT)
print data
print "Select Applet: %02X %02X" % (sw1, sw2)


COMMAND = [0x80, 0x41, 0x00, 0x00, 0x00]
list_output = []

lengthCommandData = 234

amountSendAPDU = len(list_input)/lengthCommandData
left = len(list_input) % lengthCommandData
if (left > 0):
    amountSendAPDU += 1

for b in range(amountSendAPDU):
    DATA_COMMAND = list_input[b*lengthCommandData:(b+1)*lengthCommandData]
    lengthData = len(DATA_COMMAND)

    tmp = lengthData/117
    left = lengthData%117
    if (left > 0):
        tmp += 1
    lengthResp = tmp*128
    LE = [ord(c) for c in struct.pack("!H",lengthResp)]
    LE = [0x00] + LE

    LC = [ord(c) for c in struct.pack("!H",lengthData)]

    data, sw1, sw2 = connection.transmit(COMMAND + LC + DATA_COMMAND + LE)
    list_output = list_output + data

f_encrypted = open('4x6.enc','wb')
f_encrypted.write(bytearray(list_output))
f_encrypted.close()

Decrypt caller (python/pyscard) (function 0x42):

#! /usr/bin/env python
from smartcard.System import readers
from smartcard.util import *
import struct

# define the APDUs used in this script
f_input = open('4x6.enc','rb')
list_input = list(bytearray(f_input.read()))
f_input.close()

SELECT = [0x00, 0xA4, 0x04, 0x00, 0x0A, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00]

# get all the available readers
r = readers()
print "Available readers:", r

reader = r[0]
print "Using:", reader

connection = reader.createConnection()
connection.connect()

data, sw1, sw2 = connection.transmit(SELECT)
print data
print "Select Applet: %02X %02X" % (sw1, sw2)


COMMAND = [0x80, 0x42, 0x00, 0x00, 0x00]
list_output = []

lengthCommandData = 128

amountSendAPDU = len(list_input)/lengthCommandData
left = len(list_input) % lengthCommandData
if (left > 0):
    amountSendAPDU += 1

for b in range(amountSendAPDU):
    DATA_COMMAND = list_input[b*lengthCommandData:(b+1)*lengthCommandData]
    lengthData = len(DATA_COMMAND)

    tmp = lengthData/128
    left = lengthData%128
    if (left > 0):
        tmp += 1
    lengthResp = tmp*117
    LE = [ord(c) for c in struct.pack("!H",lengthResp)]
    LE = [0x00] + LE

    LC = [ord(c) for c in struct.pack("!H",lengthData)]

    data, sw1, sw2 = connection.transmit(COMMAND + LC + DATA_COMMAND + LE)
    list_output = list_output + data
    print "%02X %02X" % (sw1, sw2)

f_encrypted = open('output.JPG','wb')
f_encrypted.write(bytearray(list_output))
f_encrypted.close()

The question:

  1. As I tried to find the problem, I found the problem is on cipher.doFinal() in decrypt function (java card). But I still don't know the cause of the error in cipher.doFinal(). Can someone help me to fix this decrypt problem?
  2. I tried to use extended applet, but I can't pass the data more than 255 bytes in python/pyscard. Can someone help me and guide me how to pass the data from python/pyscard with length more than 255 bytes?
  3. What is the maximum size of the data that can be send through APDU? As far as I know, extended APDU can receive 65535 bytes data, but when I tried to send 59904 bytes data from python/pyscard, it gives me error like:

error 3:

Traceback (most recent call last):
          File "encrypt.py", line 58, in <module>
            data, sw1, sw2 = connection.transmit(COMMAND + LC + DATA_COMMAND + LE)
          File "C:\Users\X\Anaconda\lib\site-packages\smartcard\CardConnectionDecorat
        or.py", line 82, in transmit
            return self.component.transmit(bytes, protocol)
          File "C:\Users\X\Anaconda\lib\site-packages\smartcard\CardConnection.py", l
        ine 144, in transmit
            data, sw1, sw2 = self.doTransmit(bytes, protocol)
          File "C:\Users\X\Anaconda\lib\site-packages\smartcard\pcsc\PCSCCardConnecti
        on.py", line 205, in doTransmit
            SCardGetErrorMessage(hresult))
        smartcard.Exceptions.CardConnectionException: Failed to transmit with protocol T
        1. The data area passed to a system call is too small.

Thank you and sorry for the long question, newbie here.

Leonardo
  • 31
  • 5
  • 2
    Do you extend the `ExtendedLength` interface in the applet? Note that the buffer size at least depends on the Java Card runtime configuration (i.e. the chip and the OS used). Also note that you should not repeat calls to RSA, instead you should be using hybrid cryptosystem. – Maarten Bodewes Nov 01 '15 at 15:43
  • 2
    I am not a Python expert. However, I can see a few problems in your Java Card code. You copy no data into 'buffer', so you produce wrong output. Moreover, this usage of RSA is not correct. It is as weak as symmetric cipher used in ECB mode... Could you please surround the whole content of your 'process' method with try-catch and find out the type of the exception thrown using instanceof? I will give you some more advice as soon as I get to my PC; I am typing this on my cellphone... – vojta Nov 01 '15 at 15:51
  • vojta is right, the standard call exports data from the APDU `buffer` – Maarten Bodewes Nov 01 '15 at 16:47
  • 1
    I think there is missing something? Both encrypt and decrypt function are using the APDU buffer as input but the output is never put back into the buffer and you simply use `apdu.setOutgoingAndSend()` which is returning data from the APDUBuffer. So whats the point of the applet? I think the error might origin because you never call `apdu.setIncomingAndReceive()` but you are using the data buffers – Paul Bastian Nov 02 '15 at 12:00
  • Thank you guys for the response.. MaartenBodewes you're right, now I try to change my code to hybrid cryptosystem.. Oh yeah, I do implements `ExtendedLength` interface.. vojta I still have some problem with pyscard connection.transmit that refused to send extended apdu with LC 3 bytes and LE 3 bytes.. but when I send classic apdu the response was ok.. do you have any suggestion? PaulBastian I'm now changing my code with `apdu.setIncomingAndReceive()` and `apdu.receiveBytes()` now.. wish me luck :) – Leonardo Nov 02 '15 at 16:23
  • 1
    if you have a case4 APDU that includes LC + DATA then LE is only 2 bytes (usually 00 00) – Paul Bastian Nov 02 '15 at 20:38
  • Yep, Paul is right, the extended length indicator byte (value 00h, followed by more data) is not repeated in the Le if it is already present in Lc. – Maarten Bodewes Nov 02 '15 at 21:06
  • 1
    All your buffers (`outputByte`, `outBuff`, `outputByte2`, `outBuff2`) are stored in the persistent memory. You should move them to RAM to speed up your applet. – vojta Nov 03 '15 at 10:23
  • 1
    Please, surround the whole `process` method with a try-catch block. General rule: an uncaught exception (not an instance of `ISOException`, of course) results to `6F00` in the standard APDU processing (`process()` method) and to `6A80` during the installation (`install()` method). You should catch all these exceptions and throw some ISOException with your own status word instead. – vojta Nov 03 '15 at 10:27

0 Answers0