Send APDU command to card through HID OMNIKEY 5427

2020-07-11 08:26发布

问题:

I'm trying to pass though APDUs command via HID OMNIKEY 5427 CK to operate MIFARE Card ie. Ultralight C card, on the Windows 10 x64 OS environment using WinSCard.dll. (I'm under NXP NDA and have full access t their documents)

I tried to find information online for days now. Seem like no published document for this model except the 2pages brochure.

The simple command like GetUID (FFCA000000) is OK, I can get back the actual card UID.

But for 'Authentication with Card', reference document from HID 5421 model said I should start with OpenGenericSession (FFA0000703010001), I tried that and reader always replied with 6D00h (error)

I tried send Authentication command directly '1Ah+00h' (FFA00005080100F30000641A0000) the reader also always replied with error code.

I've experience with HID 5421 model and it quite straight forward, not sure why this 5427 is unlike its sibling.

And yes, I contacted HID support. No luck. No useful information I could get from them.

If any one have idea or have 5427 software development guide please help. I'm pulling my hair for almost a week by now.

回答1:

Below is a proof-of-concept java code to communicate with Ultralight-C over Generic Session using Omnikey 5321/6321:

private static final byte AF = (byte)0xAF;

protected static final byte[] PREFIX = new byte[] { 0x01, 0x00, (byte) 0xF3, 0x00, 0x00, 0x64 };

protected final CardChannel channel;

protected void openGenericSession() throws CardException {
    System.out.println("OPEN GENERIC SESSION");
    transmitAssert9000(new CommandAPDU(0xFF, 0xA0, 0x00, 0x07, new byte[] { 0x01, 0x00, 0x01}));
}

protected byte[] transmitRaw(byte[] data) throws CardException {
    System.out.println(" => " + toHex(data));
    byte[] ret = transmitAssert9000(new CommandAPDU(0xFF, 0xA0, 0x00, 0x05, ArrayUtils.addAll(PREFIX, data), 256));
    if(ret.length<2) {
        throw new RuntimeException();
    }
    if((ret[0]==0x00)&&(ret[1]==0x00)) {
        // Success
        ret = Arrays.copyOfRange(ret, 2, ret.length);
        System.out.println(" <= " + toHex(ret));
        return ret;
    }
    if((ret[0]==0x08)&&(ret[1]==0x04)&&(ret.length==3)) {
        // ACK/NAK
        switch(ret[2]) {
            case 0x0A:
                System.out.println(" <= ACK");
                return ArrayUtils.EMPTY_BYTE_ARRAY;
            default:
                // Buyer beware: very simplified
                System.out.println(" <= NAK");
                throw new RuntimeException("NAK");
        }
    }
    ret = Arrays.copyOfRange(ret, 2, ret.length);
    System.out.println(" <= " + toHex(ret));
    return ret;
}

protected static byte[] assert9000(ResponseAPDU transmit) {
    if(transmit.getSW()!=0x9000) {
        throw new RuntimeException("Unexpected response code");
    }
    return transmit.getData();
}

protected byte[] transmitAssert9000(CommandAPDU commandAPDU) throws CardException {
    return assert9000(transmit(commandAPDU));
}

protected ResponseAPDU transmit(CommandAPDU commandAPDU) throws CardException {
    System.out.println(" -> " + toHex(commandAPDU.getBytes()));
    ResponseAPDU responseAPDU = channel.transmit(commandAPDU);
    System.out.println(" <- " + toHex(responseAPDU.getBytes()));
    return responseAPDU;
}

public byte[] read(int offset) throws CardException {
    System.out.println("READ");
    return transmitRaw(new byte[] {0x30, (byte)offset});
}

Note 1: this code uses javax.smartcardio and Apache Commons Lang.

Note 2: It has been some time I wrote this code, please validate my thoughts...

Note 3: For Ultralight-C authentication code see this companion answer.


Generic session example trace for Omnikey 6321 with Ultralight-C (single line arrows denote Generic Session APDUs and double line arrows denote Ultralight-C commands):

OPEN GENERIC SESSION
 -> FFA0000703010001
 <- 9000
AUTHENTICATE
 => 1A00
 -> FFA00005080100F30000641A0000
 <- 0000AF4BDA4E34B5D04A019000
 <= AF4BDA4E34B5D04A01
 => AF6F18402E0F0E5357D854833B149FBB56
 -> FFA00005170100F3000064AF6F18402E0F0E5357D854833B149FBB5600
 <- 000000F0F667CCF0E140419000
 <= 00F0F667CCF0E14041
READ
 => 3003
 -> FFA00005080100F3000064300300
 <- 0000000000000000000000000000000000009000
 <= 00000000000000000000000000000000
CLOSE GENERIC SESSION
 -> FFA0000703010002
 <- 9000

Some additional notes:

  • (AFAIK) This approach works under Windows (with Omnikey drivers). It does not work under linux (even with Omnikey drivers).

  • Note that PC/SC version 2.02 Part 3 defines MANAGE SESSION, TRANSAPARENT EXCHANGE and SWITCH PROTOCOL commands which provide the same in a standardized way (your reader might support it instead of proprietary generic session mechanism -- HID even participated on this document).

Good luck!