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.
Related
I have implemented an Andoid app - server side application. The server communicates with the smart card reader. When the user touches the button
in the Android app, a connenction is being built to the server to get the user authenticated. The exchanged messages between the app
and server have the following format:
<type> 0x00 0x00 0x00 <length> 0x00 0x00 0x00 <[data]>
If the message has the type value 06 that indicates an error in the smart card reader.
If the message has the type value 07 that indicates an error in the smart card.
I am using code like below for the communication with the smart card reader:
// show the list of available terminals
TerminalFactory factory = TerminalFactory.getDefault();
List<CardTerminal> terminals = factory.terminals().list();
System.out.println("Terminals: " + terminals);
// get the first terminal
CardTerminal terminal = terminals.get(0);
// establish a connection with the card
Card card = terminal.connect("T=0");
System.out.println("card: " + card);
CardChannel channel = card.getBasicChannel();
ResponseAPDU r = channel.transmit(new CommandAPDU(c1));
System.out.println("response: " + toString(r.getBytes()));
// disconnect
card.disconnect(false);
The Smart Card IO API has the CardException class for exceptions. My problem is that I do not know when to send the message of type 06 or 07 because I can not differentiate between errors that are generated by the card and errors that are generated by the reader when the CardException is thrown. How can I manage that?
The transmit() method, as used in
ResponseAPDU r = channel.transmit(new CommandAPDU(c1));
will only throw exceptions in situations related to smartcard reader errors and problems with the communication between the reader and the smartcard. It will not throw exceptions when the card itself indicates an error.
So you can catch all reader related errors by catching exceptions:
try {
ResponseAPDU r = channel.transmit(new CommandAPDU(c1));
} catch (IllegalStateException e) {
// channel has been closed or if the corresponding card has been disconnected
} catch (CardException e) {
// errors occured during communication with the smartcard stack or the card itself (e.g. no card present)
}
Errors generated by the card are, instead, indicated as error codes encoded in the response status word. These errors do not generate Java exceptions. You can test for those errors by inspecting the status word (method getSW() of ResponseAPDU):
if (r.getSW() == 0x09000) {
// success indicated by the card
} else {
// error or warning condition generated by the card
}
I am trying to record audio from an Android tablet and send it to a python server. At the start of the byte packet, I include some relevant information about the state of the Android app (A byte array called "actives" -- but considering it's receiving fine by a Java server, this should not be relevant). The android code is as follows:
int read = recorder.read(buffer, 0, buffer.length);
for (int a = 0; a < actives.length; a++) {
outBuffer[a+1] = (byte)actives[a];
logger = logger + Byte.toString(actives[a]) + ",";
}
int furthest=0;
for(int a =0; a < buffer.length; a++){
outBuffer[actives.length+1+a]=buffer[a];
if(buffer[a]!=0)furthest=a;
}
packet = new DatagramPacket(outBuffer, read,
serverAddress, PORT);
Log.d("writing", logger+Byte.toString(outBuffer[7])+".length"+Integer.toString(1+furthest+actives.length+1));
Log.d("streamer","Packet length "+outBuffer.length);
try {
socket.send(packet);
}catch (IOException e){
Log.e("streamer", "Exception: " + e);
}
Log.d("streamer","packetSent");
I receive a clean signal on the other end using a Java server.
Image of received java output: !(http://i.imgur.com/31UWzya.png)
This is my Java server:
DatagramSocket serverSocket = new DatagramSocket(3001);
int byteSize=970;
byte[] receiveData = new byte[byteSize];
DatagramPacket receivePacket = new DatagramPacket(receiveData,
receiveData.length);
while(true){ // recieve data until timeout
try {
serverSocket.receive(receivePacket);
String rcvd = "rcvd from " + receivePacket.getAddress();
System.out.println("receiver"+"Received a packet!" +rcvd);
break;
}
catch (Exception e) {
// timeout exception.
System.out.println("Timeout reached without packet!!! " + e);
timeoutReached=true;
break;
}
}
if(timeoutReached)continue;
currTime = System.currentTimeMillis();
data = receivePacket.getData();
Here is my Python server's output:
!(http://i.imgur.com/RYkcCCE.png)
And here is the code:
import socket
ip="192.ip.address"
port=3001;
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM);
sock.bind(('',port));
while(True):
data,addr=sock.recvfrom(970);
print("address",addr);
print("received a data!");
print(data);
In the last line of the python script, I have tried to change "print(data)" to "print(data.decode())", in which case I get this error:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)
I am not running these servers at the same time
My guess is that it has to do something with Java using unsigned ints and python not doing that. Is there a way in Python that I can convert this data, because data.decode() is not working? Alternatively I should be able to convert the data in Java somehow? None of the answers on stackoverflow that I have tried have worked.
Decoding is the right approach. In your android app explicitly mention the character encoding. UTF-8 is the standard Charset that is used.
Your log is pretty clear. You are trying to decode the data packet as ASCII (which is the default encoding of the decode() function) but I'm guessing its ISO_8859_1 or UTF-8 (more likely).
Next try data.decode('utf8', 'ignore') in your android app. Note: 'ignore' is an optional argument and to be used only in case of debugging as it will ignore malformed(corrupted) data and try to convert individual characters. If you want to use decode() in production use 'strict' or no second argument ('strict' is the default).
In place of 'utf8' try other options from other Python Encodings.
This was pretty brutal to attack head-on. I tried specifying the encoding in Java (before sending) like another SO post suggested, but that didn't help. So I side-stepped the problem by converting my Android byte array into a comma-separated string, then converting the string back into UTF-8 bytes.
sendString="";
for(int a =0; a < buffer.length; a++){
sendString=sendString+Byte.toString(buffer[a])+",";
}
byte[] outBuffer = sendString.getBytes("UTF-8");
Make sure you reset your string to null ("") each time you go through the while loop, or your ish will get very slow af.
Then in Python,right after receiving:
data=data.decode("utf8");
Although I am stringifying 980 characters, it does not appear to add much to the processing time... although I do wish that I could send the raw bytes, as speed is very important to me here. I'll leave the question open in case someone can come up with a better solution.
I have a problem with sending directory names over socket from my C++ client, to my Java server.
Sending ordinary messages like "hello world", works great , but the following doesn't and I can not figure out what the problem is:
char const * files = ffd.cFileName; // get directory name
string str(files, 0, strlen(files)); // convert pointer to string, right?
char mess[str.size()];
strcpy(mess, str.c_str()); // make char array :)
cout << "Send file: " << mess << " with strlen: " << strlen(mess) << " and sizeof: " << sizeof(mess) << endl;
int sent = 0;
if ((sent = send(connectSocket, mess, sizeof(mess), 0)) == SOCKET_ERROR)
{
closesocket(connectSocket);
WSACleanup();
connectToServer();
}
The java server just receives the directory names like this:
wam
p
Win
dow
s
Win
dow
s.o
ld
wxW
idg
ets
I can not understand what I'm missing because I have tried every possible way to do this and the C++ client prints like:
"Send file: windows with strlen: 7 and sizeof: 7"
I do not think that the java server is the problem since I can receive normal strings and messages perfectly, but anyway here is the JAVA code:
is = socket.getInputStream();
byteArray = new byteArray[1024];
while (true) {
c = is.read(byteArray, 0, byteArray.length);
String recv = new String(byteArray, 0, c);
System.out.println(recv);
if (recv.equals("<EOF>")){
break;
}
list.add(recv);
}
If you request something else or anything just leave a comment and I will fix it.
Question: are you sending via TCP or UDP? I'm guessing TCP, and if that is the case, you need to treat the socket as more of a stream. That stream may get broken up into a bunch of packets - you don't really control that. What I might do is to prefix the string length of each directory (ex, 3foo, 4barz, etc), read from the socket and determine what constitutes as a logical block or string, and then assemble / print the strings based on that. If you go with that route, you need to track how much you read each time until you think you are done.
I solved it, Just added byteArray = new byte[1024]; and now it works:
while (true) {
byteArray = new byte[1024]; // I ADDED THIS AND NOW THE JAVA SERVER RECEIVES IT CORRECTLY!
c = is.read(byteArray, 0, byteArray.length);
recv = new String(byteArray, 0, c);
System.out.println(recv);
if (recv.equals("<EOF>")){
break;
}
list.add(recv);
}
While trying to port some socket code from Java to Node.js (or rather, node-webkit) I came to the part where a handshake is performed between the client and the server. The Java code for this is simple (validation left out):
// C0
byte C0 = 0x03;
out.write(C0);
// C1
long timestampC1 = System.currentTimeMillis();
byte[] randC1 = new byte[1528];
rand.nextBytes(randC1);
out.writeInt((int)timestampC1);
out.writeInt(0);
out.write(randC1, 0, 1528);
out.flush();
// S0
byte S0 = (byte)in.read();
if (S0 != 0x03)
throw new IOException("Server returned incorrect version in handshake: " + S0);
// S1
byte[] S1 = new byte[1536];
in.read(S1, 0, 1536);
I was wondering if there is any way in Node.js to do something similar to Java's Socket.read(). I do not see any in the documentation, but it seems logical because Node.js is more asynchronous.
I have succeeded in writing the sending of the handshake (and confirmed that it works) using this code (methods for sending left out):
//C0
sendByte(sock, 0x03);
//C1
var ts = Date.now();
sock.randomBytes = crypto.randomBytes(1528);
sendInt(sock, ts);
sendInt(sock, 0x0);
sendBytes(sock, sock.randomBytes);
My first thought was to keep track of the connection state and to continue the handshake when a data event is received and the connection has the relevant state. However, the buffer that comes with the data event is limited to 1024 bytes, while the Java code is easily able to read 1528 bytes. The rest of the bytes seem to come in the next data call.
Should I be storing the contents from data in a Buffer and appending whenever the data is received or is there another (easier) way of doing this (a premade module, a method similar to read, ...)?
There is no synchronous read() in that same way, but what can you do in node is something like:
// using a helper function can help normalize things
function readSocket(socket, nb, cb) {
var r = socket.read(nb);
if (r === null) {
socket.once('readable', function() {
readSocket(socket, nb, cb);
});
return;
}
cb(r);
}
// read in 1 byte
readSocket(socket, 1, function(buf) {
// `buf` is a Buffer with `buf.length === 1`
});
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