How to send ATQA and SEL to Mifare Classic 1k - java

According to the documentation, before a read/write can occur, I need to send REQA followed by SELECT for anti-collision.
But I couldn't find any complete example code, code sequence performing this.
Every one starts with authenticate in onTagDiscovered, and starts reading/writing.
So my question is,
1. does Android performs its own REQA/SEL on its own under the hood?
2. If i wanted to, how do I perform REQA and SEL on my own?
I have come up with a sample code, based on the code here
try {
byte[] reqa = new byte[1];
reqa[0] = 0x26;
Log.e(TAG, "Sending REQA: " + bytesToHex(reqa));
mifareTag.transceive(reqa);
Log.e(TAG, bytesToHex(mifareTag.transceive(reqa)));
byte[] sel = new byte[9];
sel[0] = (byte) 0x93;
sel[1] = (byte) 0x70;
System.arraycopy(uid, uid.length - 4, sel, 2, 4);
sel[6] = (byte) (sel[2] ^ sel[3] ^ sel[4] ^ sel[5]);
java.util.zip.CRC32 x = new java.util.zip.CRC32();
x.update(sel);
long crc32 = x.getValue();
sel[7] = (byte) crc32;
sel[8] = (byte) (crc32 >> 8);
Log.e(TAG, "CRC: " + Long.toHexString(crc32));
Log.e(TAG, "Sending SEL: " + bytesToHex(sel));
Log.e(TAG, bytesToHex(mifareTag.transceive(sel)));
if (mifareTag.transceive(cmd) != null) {
Log.e(TAG, "NfcA Tag transcieve:auth success");
return true;
}
} catch (Exception ex) {
Log.e(TAG, "NfcA Tag transcieve:auth failed with exception !", ex);
return false;
}
But the problem is, with this code, what I'm receiving back is weird data.
10-22 11:04:13.465 30687-30734/com.mfreader E/RfidReader: Sending REQA: 26
10-22 11:04:13.492 30687-30734/com.mfreader E/RfidReader: B2007D9BD20804000215671E0D8B811D
10-22 11:04:13.492 30687-30734/com.mfreader E/RfidReader: CRC: a247ff86
10-22 11:04:13.493 30687-30734/com.mfreader E/RfidReader: Sending SEL: 93701A2E7D9BD286FF
10-22 11:04:13.519 30687-30734/com.mfreader E/RfidReader: B2007D9BD20804000215671E0D8B811D
10-22 11:04:13.602 30687-30734/com.mfreader E/RfidReader: NfcA Tag transcieve:auth failed with exception !
java.io.IOException: Transceive failed
at android.nfc.TransceiveResult.getResponseOrThrow(TransceiveResult.java:52)
at android.nfc.tech.BasicTagTechnology.transceive(BasicTagTechnology.java:151)
at android.nfc.tech.NfcA.transceive(NfcA.java:145)
The returned value is Sect#0Block#0 but the first 2 bytes replaced with B200 instead of 1A2E of the UID. And the succeeding Auth fails, which otherwise fails only intermittently (which is what i'm trying to solve)!
Not sure what i'm doing wrong, any help?

does Android performs its own REQA/SEL on its own under the hood?
Yes
If I wanted to, how do I perform REQA and SEL on my own?
/**
* Send raw NFC-A commands to the tag and receive the response.
*
* <p>Applications must not append the EoD (CRC) to the payload,
* it will be automatically calculated.
* <p>Applications must only send commands that are complete bytes,
* for example a SENS_REQ is not possible (these are used to
* manage tag polling and initialization).
*
* <p>Use {#link #getMaxTransceiveLength} to retrieve the maximum number of bytes
* that can be sent with {#link #transceive}.
*
* <p>This is an I/O operation and will block until complete. It must
* not be called from the main application thread. A blocked call will be canceled with
* {#link IOException} if {#link #close} is called from another thread.
*
* <p class="note">Requires the {#link android.Manifest.permission#NFC} permission.
*
* #param data bytes to send
* #return bytes received in response
* #throws TagLostException if the tag leaves the field
* #throws IOException if there is an I/O failure, or this operation is canceled
*/
public byte[] transceive(byte[] data) throws IOException {
return transceive(data, true);
}
You can select tag by method mifareTag.connect(), after authenticating with android methods from android docs you can write and read.
https://developer.android.com/reference/android/nfc/tech/MifareClassic
What means error 'B2' do you got it somewhere in documentation?

Related

Unkown error 0x16 on smartcard reader access

I am trying to change the buzzer duration on the ACR1252U.
Link to API:
http://www.acs.com.hk/download-manual/6402/API-ACR1252U-1.09.pdf
According to the API documentation I need the 'E0000028010A' command to change the buzzer status, whereby '0A' marks the duration as 0A*10ms (Page 44).
Following Java code is used:
public static void main(String[] args) {
try {
byte[] send = new byte[6];
send[0] = (byte) 0xE0; // Commandclass
send[1] = (byte) 0x00; // Protocoll
send[2] = (byte) 0x00; // Param 1
send[3] = (byte) 0x28; // Param 2: Buzzerstatus
send[4] = (byte) 0x01; // Change Flag
send[5] = (byte) 0x0A; // Duration: 0A*10ms => 100ms
Card card = getCard("DIRECT"); // Works!
CardChannel channel = card.getBasicChannel(); // Works!
CommandAPDU command = new CommandAPDU(send); // Works!
channel.transmit(command); // EXCEPTION!
} catch (Exception ex) {
ex.printStackTrace();
}
}
public static Card getCard(String target) throws Exception {
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
for (CardTerminal t : terminals) {
if (t.getName().equals("ACS ACR1252 Dual Reader PICC 0")) {
Card card = t.connect(target);
return card;
}
}
throw new Exception();
}
But this results in the following stacktrace indicating the "unkown error 0x16":
javax.smartcardio.CardException: sun.security.smartcardio.PCSCException: Unknown error 0x16
at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:219)
at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90)
at readerconfig.TagConfig.main(TagConfig.java:24)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x16
at sun.security.smartcardio.PCSC.SCardTransmit(Native Method)
at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:188)
... 2 more
I've spent hours on searching for anything in this direction however I couldn't find anything. I have even tried another device, which still generated this error.
Either I have completely gone blind or something is not set up correctly with my computer. All I can say is, that I have already successfully written and read from NFC tags using this reader. But I just can't change the config of the reader itself.
EDIT:
I've also found this alternative way to send the command:
byte[] send = new byte[5];
send[0] = (byte) 0xE0;
send[1] = (byte) 0x0;
send[2] = (byte) 0x0;
send[3] = (byte) 0x18; // Tries to read firmware version
send[4] = (byte) 0x0;
Card card = CardUtils.getCard("DIRECT"); // Works!
card.transmitControlCommand(3500, send);
But this results in the "unknown error 0x1":
javax.smartcardio.CardException: transmitControlCommand() failed
at sun.security.smartcardio.CardImpl.transmitControlCommand(CardImpl.java:236)
at readerconfig.ReaderConfig.main(ReaderConfig.java:28)
Caused by: sun.security.smartcardio.PCSCException: Unknown error 0x1
at sun.security.smartcardio.PCSC.SCardControl(Native Method)
at sun.security.smartcardio.CardImpl.transmitControlCommand(CardImpl.java:232)
... 1 more
There are two ways to interact with this reader over the Java Smartcard IO API:
The first is to open a regular APDU transmission channel (from a PC/SC point of view this maps to T=0 or T=1 protocol). You can do this using
Card card = getCard("*");
However, this will require the reader to report the presence of a card. Otherwise you can't open a connection that way.
You can then transmit APDU commands to the card (on the basic channel or a logical channel) and you can send special commands to the reader on the basic channel. These special commands have their class byte set to 0xFF to indicate that the command is intended to be interpreted by the reader (instead of being forwarded to the card). So this is not applicable for the "peripherals control" commands that start with 0xE0.
Those "peripherals control" commands have to be sent to the reader using control commands with the control code SCARD_CTL_CODE(3500). As with opening a connection to the card, you can use getCard("*") if there is a card present on the reader. However, if you want to be able to send those commands to the reader even if there is no card present, you have to open a connection in "direct" mode:
Card card = getCard("DIRECT");
You can then send control commands using the method card.transmitControlCommand(). This method takes the control code as the first argument and the command (as byte array) as the second argument. Exchanging commands on the basic channel or any logical channel using channel.transmit() will usually not work in "direct" mode (hence the error code 0x16).
The control code is calculated as
public static final int SCARD_CTL_CODE(int command) {
boolean isWindows = System.getProperty("os.name").startsWith("Windows");
if (isWindows) {
return 0x00310000 | (command << 2);
} else {
return 0x42000000 | command;
}
}
Note the difference between Windows and other platforms.
For instance, to send the buzzer control command, you would use
byte[] command = new byte[] { (byte)0xE0, (byte)0x00, (byte)0x00, (byte)0x28, (byte)0x01, (byte)0x0A };
byte[] response = card.transmitControlCommand(SCARD_CTL_CODE(3500), command);
Finally, be aware that sending IOCTL control codes over PC/SC requires special driver support. Specifically, the standard CCID driver provided by Microsoft does not support sending escape commands by default (see USB CCID Class Driver Details). This driver supports escape commands only after enabling them through the registry value "EscapeCommandEnable". The error 0x1 that you showed in your question is a typical result of this missing support for escape commands.
To reliably support all features of the reader (including escape commands) you need to use the "PC/SC Drivers" package provided by ACS on their website.
Try to use
card.transmitControlCommand(int controlCode, byte[] command)
instead of transmit. According to section 5.8 (page 41 of the pdf you linked to)
controlcode is 3500, although it is unclear to me, if that is a hex or an int, so compare to SCARD_CTL_CODE, if you are able to. At least, I interpret the documentation this.
Usually you use transmitControlCommand to talk to the reader and transmit to talk to the card.
Fixed typo in ControlCode. Kudos to Torhan Bartel for telling me.

The SOAP requests I am sending to the queue are becoming malformed on IBM MQ

I've created a utility in Java which picks up XML files, sends them over to a queue on IBM MQ. When I go into IBM MQ Explorer, the message is showing as received, but there is an ASCII character in front if it (as shown in the "Message Data" field in the image below) which is resulting in it not being recognized as a properly formatted SOAP message which the queue can process. I have tried using XML editors to make sure that my XML files don't have any non-white space characters, but that hasn't resolved the issue.
Here is my code which I am using to put the file on the queue:
* sending message to MQ
*
* #param file
* #return messageId
* #throws UnsupportedEncodingException
* #throws IOException
*/
private byte[] sendMessageToMQ(File file) throws UnsupportedEncodingException, IOException {
int openOptions = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_OUTPUT;
try {
defaultLocalQueue = qManager.accessQueue(queueName, openOptions);
MQMessage putMessage = new MQMessage();
String msg = readFile(file);
putMessage.writeUTF(msg);
// specify the message options...
MQPutMessageOptions pmo = new MQPutMessageOptions();
// accept
// put the message on the queue
defaultLocalQueue.put(putMessage, pmo);
System.out.println("Message is put on MQ.");
return putMessage.messageId;
} catch (MQException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
writeUTF is documented as prefixing the data with the length
https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.dev.doc/q030840_.htm
Note: The writeUTF() method of MQMessage automatically encodes the
length of the string as well as the Unicode bytes it contains. When
your message will be read by another Java program (using readUTF()),
this is the simplest way to send string information.
You can set the characterSet of the MQMessage to 1208 (or whatever the ccsid of the message is currently according to the explorer), then use the writeString method
https://www.ibm.com/developerworks/community/forums/html/topic?id=77777777-0000-0000-0000-000000023465
If you need UTF-8 text in your message, but don't require the two byte
length field, set the characterSet field to 1208 (which is the CCSID
for UTF-8) and use writeString().
Note although this article talks about .net, but the same is true for Java:
http://www-01.ibm.com/support/docview.wss?uid=swg21267940

Java Socket Corrupting PNG-image

I am currently trying to use a Socket to send a PNG or JPEG image from one Client to another (in Java) but the images always becomes corrupted (when I try to open it it just says that it can't be opened because it's damaged, faulty or too big).
I have tried the methods that load the images into byte[] and if I just load an image into a byte[] and then save it back down it works perfectly so the problem must be in the sending of the byte[].
Here are the functions I use for the sending:
/**
* Attempts to send data through the socket with the BufferedOutputStream. <p>
* Any safety checks should be done beforehand
* #param data - the byte[] containing the data that shall be sent
* #return - returns 'true' if the sending succeeded and 'false' in case of IOException
*/
public boolean sendData(byte[] data){
try {
//We simply try to send the data
outS.write(data, 0, data.length);
outS.flush();
return true; //Success
} catch (IOException e) {
e.printStackTrace();
return false; //Failed
}
}
/**
* Attempts to receive data sent to the socket. It uses a BufferedInputStream
* #param size - the number of bytes that should be read
* #return - byte[] with the received bytes or 'null' in case of an IOException
*/
public byte[] receiveData(int size){
try {
int read = 0, r;
byte[] data = new byte[size];
do{
//We keep reading until we have gotten all data
r = inS.read(data, read, size-read);
if(r > 0)read += r;
}while(r>-1 && read<size); //We stop only if we either hit the end of the
//data or if we have received the amount of data we expected
return data;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
The images that arrive seems to be the correct size and all so the data is at least arriving, just corrupted.
Throw your receiveData() method away and use DataInputStream.readFully().

do I need to close InputStream/OutputStream if I close BluetoothSocket?

I have a BluetoothSocket and two streams.
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
BluetoothSocket s = (BluetoothSocket) m.invoke(device, 1);
s.connect();
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
At some moment I want to close the socket. Do I have to close the streams?
The problem is that each close() may throw an exception, I have to catch them,
and the code becomes bloated.
IIRC in some similar case it was enough to close the main object (which would be the socket in this case), and other objects were closed automatically. But this behavior is not documented for BluetoothSocket (or I could not find it).
So:
If I close a bluetooth socket, do I have to close its streams?
(And what about Sockets? Are they different? BluetoothSocket does not inherit from Socket.)
I have been working on Android Bluetooth recently. I checked the sources. And it seems that you don't need to close your streams.
Indeed, you stream are respectively objects of type BluetoothInputStream and BluetoothOutputStream in the BluetoothSocket constructor:
mInputStream = new BluetoothInputStream(this);
mOutputStream = new BluetoothOutputStream(this);
Those are streams returned when you call:
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
But when you call .close() on these streams, you call:
public void close() throws IOException {
mSocket.close();
}
So you only close the BluetoothSocket again.
In conclusion, you don't need to close those streams.
For your second question, the only thing that Socket and BluetoothSocket have in common is that they implement Closable: they will have a .close() method. It does not mean they do the same
Here is the complete code for BluetoothOutputStream:
/*package*/ final class BluetoothOutputStream extends OutputStream {
private BluetoothSocket mSocket;
/*package*/ BluetoothOutputStream(BluetoothSocket s) {
mSocket = s;
}
/**
* Close this output stream and the socket associated with it.
*/
public void close() throws IOException {
mSocket.close();
}
/**
* Writes a single byte to this stream. Only the least significant byte of
* the integer {#code oneByte} is written to the stream.
*
* #param oneByte
* the byte to be written.
* #throws IOException
* if an error occurs while writing to this stream.
* #since Android 1.0
*/
public void write(int oneByte) throws IOException {
byte b[] = new byte[1];
b[0] = (byte)oneByte;
mSocket.write(b, 0, 1);
}
/**
* Writes {#code count} bytes from the byte array {#code buffer} starting
* at position {#code offset} to this stream.
*
* #param b
* the buffer to be written.
* #param offset
* the start position in {#code buffer} from where to get bytes.
* #param count
* the number of bytes from {#code buffer} to write to this
* stream.
* #throws IOException
* if an error occurs while writing to this stream.
* #throws IndexOutOfBoundsException
* if {#code offset < 0} or {#code count < 0}, or if
* {#code offset + count} is bigger than the length of
* {#code buffer}.
* #since Android 1.0
*/
public void write(byte[] b, int offset, int count) throws IOException {
if (b == null) {
throw new NullPointerException("buffer is null");
}
if ((offset | count) < 0 || count > b.length - offset) {
throw new IndexOutOfBoundsException("invalid offset or length");
}
mSocket.write(b, offset, count);
}
/**
* Wait until the data in sending queue is emptied. A polling version
* for flush implementation. Use it to ensure the writing data afterwards will
* be packed in the new RFCOMM frame.
* #throws IOException
* if an i/o error occurs.
* #since Android 4.2.3
*/
public void flush() throws IOException {
mSocket.flush();
}
}
You should close the streams first. Only calling close() on the socket will usually work out, but if a new connection is opened immediately following (read: unit testing), you'll have problems.
At some moment I want to close the socket. Do I have to close the
streams? The problem is that each close() may throw an exception, I
have to catch them, and the code becomes bloated.
If the documentation does not mention nothing about it, I think you should close it. If your problem is just the exception, you can have an utility method, such
public void closeStream(Closeable stream) {
if (stream == null) {
return;
}
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}

javax.smartcardio: how to send native commands to Desfire card?

I am creating a java application communicating with a Mifare DESFire card through a PC/SC contactless reader and the javax.smartcardio API. I manage to send regular ISO 7816 APDUs (CLA, INS, P1-P2, Lc, Command data, Le).
I have read on Ridrix's Blog that DESFire cards (at least the EV1 version that I am using) support both APDUs and Native commands where most of the commands are only 1 byte long.
For example, the "Get Version" command:
Command: 60
Response: af 04 01 01 00 02 18 05
I tested that command with the PC/SC Diag program from SpringCard (available here) and I get a correct response.
But I cannot send this command with javax.smartcardio: this API seems to have been created for real APDUs and therefore does not allow 1 byte long commands.
Here is what I did:
public static void main(String[] args){
TerminalFactory factory = TerminalFactory.getDefault();
CardTerminals terminalList = factory.terminals();
try {
CardTerminal ct = terminalList.list().get(0);
ct.waitForCardPresent(0);
Card card = ct.connect("*");
CardChannel channel = card.getBasicChannel();
byte[] command = { 0x60 };
channel.transmit(new CommandAPDU(command));
} catch (CardException e) {
e.printStackTrace();
}
}
It gives me the following error:
Exception in thread "main" java.lang.IllegalArgumentException: apdu must be at least 4 bytes long
at javax.smartcardio.CommandAPDU.parse(Unknown Source)
at javax.smartcardio.CommandAPDU.<init>(Unknown Source)
I tried the only (AFAIK) other way to send a command:
ByteBuffer command = ByteBuffer.allocate(1);
command.put((byte) 0x60);
ByteBuffer response = ByteBuffer.allocate(512);
channel.transmit(command, response);
and get a similar error:
Exception in thread "main" java.lang.IllegalArgumentException: Command APDU must be at least 4 bytes long
at sun.security.smartcardio.ChannelImpl.checkManageChannel(Unknown Source)
at sun.security.smartcardio.ChannelImpl.doTransmit(Unknown Source)
at sun.security.smartcardio.ChannelImpl.transmit(Unknown Source)
Do you know of any way to send this kind of command using javax.smartcardio or something else?
I know it is possible to wrap these commands but I would prefer to use the (simpler) native commands.
Thanks.
javax.smartcardio is an API written to use ISO 7816-4 commands. Therefore it is not possible to send "native" commands. Basically, native commands can be anything, so it would be hard to support those.
Either you revert to JNI or you might try and find something that uses transmitControlCommand. But I'm afraid there is no real way of using DESFire without an additional library.
Personally I think it is much easier to use the wrapping layer.
Nearly 4 years later but just in case someone stubbles across this question, I did find an answer to this. Many readers today support wrapping Desfire APDU frames in a ISO 7816-4 command. I did discover a limitation whereby the data cannot exceed 55 bytes.
Checkout page 23 in this doc for full info:
http://neteril.org/files/M075031_desfire.pdf
This means that you can specify the following to wrap the APDU frame
CLA = 0x90
INC = {Your Desfire Command e.g. 0x60 - Get Version}
P1 = 0
P2 = 0
Data = 1st byte = length of data followed by byte data. Terminate data with a 0x00 byte
The response is also wrapped as follows:
SW1 = 0x91
SW2 = Result Status
Data = Response Data
So the following code can be used
public static byte CMD_WRAP_START = (byte)0x90;
public static byte CMD_WRAP_END = (byte)0x00;
private CommandAPDU wrapAPDUFrameUsingISO7816_4(byte[] apdu) throws CardException {
if (apdu.length > 55){
throw new CardException("The length of the wrapped DESFire command must not be longer than 55 bytes, checksum included.");
}
boolean hasData = apdu.length > 1;
byte[] result;
if (hasData) {
result = new byte[apdu.length + 5];
} else {
result = new byte[apdu.length + 4];
}
result[0] = CMD_WRAP_START; // CLA
result[1] = apdu[0]; // DESFIRE CMD CODE
result[2] = 0; // P1
result[3] = 0; // P2
if (hasData) {
result[4] = (byte) (apdu.length - 1); // Length of wrapped data, ONLY IF DATA EXISTS
System.arraycopy(apdu,1,result,5,apdu.length-1); // DESFIRE Command data
}
result[result.length-1] = CMD_WRAP_END;
return new CommandAPDU(result);
}
private static byte [] unwrapFromISO7816_4(byte[] wrapped) throws CardException {
if (wrapped.length<2){
throw new CardException("Expected at least 2 bytes for ISO 7816-4 wrapped response: " + String.valueOf(Hex.encodeHex(wrapped, false)));
}
if (wrapped[wrapped.length-2]!=(byte)0x91){
throw new CardException("Expected 0x91 in SW1 for ISO 7816-4 wrapped response: " + String.valueOf(Hex.encodeHex(wrapped, false)));
}
byte[] result = new byte[wrapped.length-1];
System.arraycopy(wrapped,0,result,1,wrapped.length-2); // The DESFIRE response
result[0] = wrapped[wrapped.length-1]; // The DESFIRE Status
return result;
}
Here you have the answer: Command APDU must be at least 4 bytes.
* case 1 : |CLA|INS|P1 |P2 | len = 4
* case 2s: |CLA|INS|P1 |P2 |LE | len = 5
* case 3s: |CLA|INS|P1 |P2 |LC |...BODY...| len = 6..260
* case 4s: |CLA|INS|P1 |P2 |LC |...BODY...|LE | len = 7..261
*
* (Extended length is not currently supported)
* case 2e: |CLA|INS|P1 |P2|00 |LE1|LE2| len = 7
* case 3e: |CLA|INS|P1 |P2 |00|LC1|LC2|...BODY...| len = 8..65542
* case 4e: |CLA|INS|P1 |P2 |00|LC1|LC2|...BODY...|LE1|LE2| len =10..65544
*
* EMV

Categories

Resources