Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send APDU command to card through HID OMNIKEY 5427 CK

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.

like image 929
Eric F. Avatar asked Jun 19 '17 04:06

Eric F.


1 Answers

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!

like image 65
vlp Avatar answered Oct 23 '22 13:10

vlp