1
votes

I've already used javax.smartcardio to read serial number from smart card without much effort. However now I'm assigned to create MF on blank card (without which serial number can't be read).I'm creating APDU command for that as per ISO 7816 guidelines,but unable to create proper APDU command because my hex values are getting converted to wrong bytes.

import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.ResponseAPDU;
import javax.smartcardio.TerminalFactory;

class SmartCardAPIs {

    public int Create_MF() throws CardException{

        //--Variable declaration
        int result=0;
        Card card=null;     
        byte[] responseData=null;
        ResponseAPDU answer=null;
        String SW1=null;
        String SW2=null;
        int cla,  ins,  p1,  p2;
        byte[] data=null;
        //---------------------------------------------

        //--1--Establish connection with the smart card
        TerminalFactory factory = TerminalFactory.getDefault();
        List<CardTerminal> terminals = factory.terminals().list();
        // Use the first terminal
        CardTerminal terminal = terminals.get(0);
        // Connect with the card
        card = terminal.connect("*");
        CardChannel channel = card.getBasicChannel();           
        //---------------------------------------------

        //--2--Create MF 
        cla=0x00;
        ins=0xE0;
        p1=0x00;
        p2=0x00;
        data = new byte[] {
            (byte) 0x21,
            (byte) 0x62,
            (byte) 0x1F,
            (byte) 0x82, // **** Getting converted to -126 ****
            --
            --
            --
        };          
        answer = channel.transmit(new CommandAPDU(cla,  ins,  p1,  p2, data));
        responseData= answer.getBytes();

        if(responseData!=null)
        {
            if(responseData.length==2)
            {
                SW1=String.format("%02X ", (responseData[0])).trim();
                SW2=String.format("%02X ", (responseData[1])).trim();
            }
        }       
    }
}

I've 2 problems

1: data in command APDU is taking a byte which is wrong(marked as *).

2: SW1 and SW2 are returning as 6A 80 which means parameter in data field are incorrect(I guess because of negative value while casting int in hex format to byte,but can not help much as I'm forced to do it).

The partial APDU command which I've placed here is part of full command which I've been supplied ,and the command is 100% OK and tested as I've been successful in creating MF in blank card using smart card tools with the command.I want to do the same in java now.

I assume that problem lies with the way this APDU is being getting created,may be negative value problem(I'm not very expert with java APDU stuffs although I 've created Applet to read serial number from card).

3
Bytes in Java are always signed, so that is certainly not the problem. It is more likely that the command data does not comply to the specifications of the APDU command, which unfortunately is proprietary I think.Maarten Bodewes
Note that it won't hurt to read into some Java style guides. You don't need to declare variables in advance, nor do you need to init them with 0 or null. If you declare them final you are sure that they get assigned a value, which is much more "valuable". Methods and variable names start with lowercase, and Java normally puts the { behind the last line. This makes it much more readable for other Java developers.Maarten Bodewes
As far as java style is concerned,I'm not a java guy but yes I always need to instantiate variables to null if I require them to be used in more than one places,which are conditioned with ifs. I don't think c# developers writes bad codes, convention and convenience sometimes don't go together. SW1 and SW2 are for convenience in understanding Response APDU. Tolerance for diversity don't hurts. Also it helps more when someone acts as a problem solver from his experience rather than acts as PREACHER, whose preaching may not be of much use to the current scenario.user1740595
It becomes an issue if the code becomes less clear. If you had read the API you would have noticed that there is a getData() method that never returns null (neither does getBytes()), so the if becomes completely irrelevant. You might also have found the getSW(), getSW1() and getSW2() methods in the ResponseData type. And now the declarations are separate from their uses, which makes it hard to refactor the code. But it seems you are too confident in your own code, so this is the last I will say about it.Maarten Bodewes

3 Answers

2
votes

Since you say, you have a working command at hand and surely double-checked its java representation: how sure are you, that the command did not execute successfully in a preceeding attempt? (Obviously only one MF is allowed.) Problem is, that the reported error code contradicts the "correct APDU known" assumption and this is the only resolution I can think of. The java code looks correct, as far as it is given.

Other idea: The command data field of an ISO Create File should start with something like 0x6X for FCI. Possibly your 0x21 is the length of the whole template (supposed to be sent as LC), which java constructs itself from the length of the byte array, so try leaving it out. The fact, that the 3rd byte is 0x1F, meaning 0x21 minus tag and length, supports that hypothesis.

0
votes

I have tried this command :

..

..

private static final byte[] Select_App = {(byte)0x00,(byte)0xA4,(byte)0x04,(byte)0x00,(byte)0x04,(byte)0x50,(byte)0x54,(byte)0x4B,(byte)0x65};

.. ..

and they worked fine, I got the right respons from the card. But I tried that command on android. So I think that byte casting is right we don't need to convert anything

Well, about java.smartcardio.*; I have the same problem with you, I can't send any command using that librari. But someone said that I have to use some java wrapper to send some commands. Umm... I'm still working on it btw... :-)

-1
votes

You need to convert byte to hex properly by following Methods.

Integer class = StringUtil.parseHex("your class as string");

Class StringUtil


public class StringUtil {
public static Integer parseHex(String iStr) {
    int mask = 255;
    if (iStr.length() > 2)
        mask = 65535;
    try {
        return Integer.valueOf(Integer.parseInt(iStr, 16) & mask);
    } catch (Exception ex) {
    }
    return null;
}