你如何使用javax.smartcardio来检测desfire卡?

时间:2017-01-17 05:59:29

标签: java smartcard mifare

我拼凑了以下代码示例,以使用javax.smartcardio库检测Mifare智能卡。此代码适用于列出的Mifare类型。

public class DetectCardTester {
    private static final String PROTOCOL = "T=1";

    static Map<String,String> KNOWN_CARD_TYPES = ImmutableMap.<String, String>builder()
        .put("00-01", "Mifare 1K")
        .put("00-02", "Mifare 4K")
        .put("00-03", "Mifare Ultralight")
        .put("00-26", "Mifare Mini")
        .build();

    public static void main(String... args) {
        try {
            final TerminalFactory terminalFactory = SmartcardTerminalFactory.create();
            System.out.println("Place card on the reader");
            final Card card = awaitCard(terminalFactory, 3, SECONDS);
            final ATR atr = card.getATR();
            final byte[] bytes = atr.getBytes();

            System.out.println("ATR=" + String.valueOf(Hex.encodeHex(bytes, false)));

            if (bytes != null && bytes.length > 13) {
                String typeCode = String.format("%02X-%02X", bytes[13], bytes[14]);
                if (KNOWN_CARD_TYPES.containsKey(typeCode)) {
                    System.out.println("Known Type:" + KNOWN_CARD_TYPES.get(typeCode));
                } else {
                    System.out.println("Unknown Type:" + typeCode);
                }
            }
            // TODO: Detect Desfire Card

        } catch (Throwable t) {
            t.printStackTrace();
        }
    }


    public static Card awaitCard(final TerminalFactory terminalFactory, final int qty, final TemporalUnit unit) throws CardException {

        LocalDateTime timeout = now().plus(qty, unit);
        while (now().isBefore(timeout)) {
            Optional<CardTerminal> cardTerminal = getCardPresentTerminal(terminalFactory);
            if (cardTerminal.isPresent()) {
                return cardTerminal.get().connect(PROTOCOL);
            }
        }
        throw new CardNotPresentException("Timed out waiting for card");
    }

    private static Optional<CardTerminal> getCardPresentTerminal(final TerminalFactory terminalFactory) throws CardException {
        List<CardTerminal> terminals = terminalFactory.terminals().list();

        for (CardTerminal cardTerminal : terminals) {

            if (cardTerminal.isCardPresent()) {
                return Optional.of(cardTerminal);
            }
        }

        for (CardTerminal cardTerminal : terminals) {

            // waitForCardPresent / Absent doesn't work with some Jacax.smartcard.io implementations
            // i.e. OSX http://bugs.java.com/view_bug.do?bug_id=7195480
            // This is why we have the imediate check above
            if (cardTerminal.waitForCardPresent(250)) {
                return Optional.of(cardTerminal);
            }
        }

        return Optional.empty();
    }
}

我使用以下资源将此代码放在一起:

我想实施TODO评论以检测Desfire卡。如果我在读取时放置了Desfire卡,则此代码只输出:

Place card on the reader
ATR=3B8180018080

我发现这个问题Determine card type from ATR有点帮助,但我遗漏了一些东西。有问题的卡的HistoricalByte是0x80,我无法找到任何信息。

如果可能,我将非常感谢上面扩展的代码示例,以便它可以检测Desfire卡类型。

1 个答案:

答案 0 :(得分:0)

重置答案(ATR)可能是您拥有哪张牌的一个很好的指示。您所追求的SPEC称为&#34; 7816-3&#34;它实际上是一个付费的规格,但如果你谷歌好......

无论如何,SIM规范很难理解,它们很好地定义了,但你需要深入到位级别才能完全理解它。

ATR以字节为单位进行解码,让您解码特定的ATR:

Byte1被称为&#34; TS&#34;,它被定义为3B或3F,其中3F表示&#34;反向约定&#34;使用和3B&#34;直接约定&#34;

Byte2 Called&#34; T0&#34; ,它被定义为&#34;格式字符&#34;。这个字节的第一个半字节&#34; 8&#34;是指Y1参数,第二个半字节是指&#34; K&#34;参数。 你的&#34; T0&#34;第一个半字节Y1 = 8,第二个半字节K = 1 Y1参数8 =(二进制)1000意味着仅跟随TA1参数。 如果它是9 =(二进制)1001意味着TA1开启(1)TB1关闭(0)TC1关闭(0)并且TD1开启(1)。 K定义了历史字节数,即您的情况下的1个历史字节。

长话短说,是的&#34; 80&#34;是你的历史字节(在7816-4规范中定义)。 80是您可以拥有的最基本的历史字节。 80是&#34;类别/状态指示符&#34;并且所有80都被定义为:

A status indicator (one, two or three bytes) may be present in an optional COMPACT-TLV data object

关键字是&#34;可能存在&#34;即在您的情况下,他们决定不再包含任何进一步的信息。

回到你的问题,检查ATR是否与确切的值匹配&#34; 3B8180018080&#34;应该足以识别DESfire卡。但是在我的应用程序中,我还使用了特定文件的存在(即只能在desfire卡上找到),文件内容或AID卡特定应用程序的可用性,以便更准确地对卡片进行分类。