FULL PRODUCT VERSION :
java version "1.8.0_77"
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Windows 10 but this should be occurring on all OSs
EXTRA RELEVANT SYSTEM CONFIGURATION :
generic smart card reader for contact/contactless smart cards (omnikey 5321)
A DESCRIPTION OF THE PROBLEM :
based on the open JDK files
in
sun/security/smartcardio/ChannelImpld.java
private byte[] doTransmit(byte[] command) throws CardException
there is a check in the loop to retrieve all the data in response to a card commnad. This check has the following erroneous error check (around line 185):
if (++k >= 32) {
throw new CardException("Could not obtain response");
}
this check prevents reading of response data > 8k and worse throws an incorrect exception.
The default limit should be increased and possibly have the ability to be configured. Additionally the exception message needs to be corrected.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Get a smart card containing a structure with more than 8k of data (mine has 10k).
Smart card reader
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
ResponseAPDU should return succesfully and contain >8k of data.
ACTUAL -
Invalid exception is thrown.
Exception in thread "main" javax.smartcardio.CardException: Could not obtain response
at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:186)
at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90)
at com.tvec.smartcardio.ErrorTest.main(ErrorTest.java:58)
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" javax.smartcardio.CardException: Could not obtain response
at sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:186)
at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90)
at com.tvec.smartcardio.ErrorTest.main(ErrorTest.java:58)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package com.tvec.smartcardio;
import java.security.NoSuchAlgorithmException;
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;
import com.tvec.smart_card.pcsc.PCSCConstants.ShareMode;
public class ErrorTest {
public static void main(String args[]) throws CardException {
// PCSCWrapper wrapper = new PCSCWrtapper();
Card card = null;
String readerName = "OMNIKEY CardMan 5x21 0";
String protocol = "T=1";
ShareMode shareMode = ShareMode.Exclusive;
// TerminalFactory terminalFactory = TerminalFactory.getDefault();
TerminalFactory terminalFactory = null;
try {
java.security.Provider provider = java.security.Security.getProvider("SunPCSC");
terminalFactory = TerminalFactory.getInstance("PC/SC", null, provider);
CardTerminal terminal = terminalFactory.terminals().getTerminal(
readerName);
card = terminal.connect(protocol);
if (shareMode == ShareMode.Exclusive) {
card.beginExclusive();
}
CardChannel cardChannel = card.getBasicChannel();
ResponseAPDU responseAPDU = null;
// verify for access to large container in my test case
CommandAPDU inCommandAPDU = new CommandAPDU(
ErrorTest.toSignedByteArray("0020008008313233343536ffff"));
responseAPDU = cardChannel.transmit(inCommandAPDU);
System.out.println(Integer.toHexString(responseAPDU.getSW()));
// send command that returns a response greater than 8k
inCommandAPDU = new CommandAPDU(
ErrorTest.toSignedByteArray("00cb3fff055c035fc108"));
// exception will be thrown here when accessing large container.
// javax.smartcardio.CardException: Could not obtain response
// at
// sun.security.smartcardio.ChannelImpl.doTransmit(ChannelImpl.java:186)
// at sun.security.smartcardio.ChannelImpl.transmit(ChannelImpl.java:90)
responseAPDU = cardChannel.transmit(inCommandAPDU);
System.out.println(Integer.toHexString(responseAPDU.getSW()));
} catch (NoSuchAlgorithmException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static byte[] toSignedByteArray(String in) {
// convert the string into bytes and add them to the out array
in = in.toLowerCase();
if (in.startsWith("0x")) {
// pull off the 0x
in = in.substring(2);
}
// pad with 0 to make length a multiple of 2
// 2 chars make a byte
if (in.length() % 2 > 0) {
in = "0" + in;
}
// build the output array
byte[] out = new byte[in.length() / 2];
for (int i = 0; i < out.length; i++) {
out[i] = (byte) ((Character.digit(in.charAt(i * 2), 16) << 4) + Character
.digit(in.charAt(i * 2 + 1), 16));
}
return out;
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
I wrote my own provider using the open JDK source as a guide and a Windows JNI smartcard library. In my implementation I added a maximum loop try variable and increased the limit to 100 from 32. This however does not solve the issue for other platforms which I would like to deploy to.