在 C# 中根据 ATR 确定设施代码和卡号

Determine Facility Code and Card Number from ATR in C#

我有以下卡 reader HID Omnikey 5325。 我有一张名为 HIS Proximity 的非接触式卡。

这张卡片上写的数字是133593 42101044091-3.

通过读卡,我得到以下 ATR 十六进制:3B050002F10673

使用以下应用程序,我设法查看了以下信息。

I need to extract the Facility Code and the Code Number in order to identify the card.

我设法找到了以下代码,但我遗漏了一些代码:

/// <summary>
    /// Extract a data item from the Wiegand raw data. The data item usually is something like a card 
    /// number or facility code.
    /// </summary>
    /// <param name="format"></param>
    /// <param name="identifier"></param>
    /// <returns></returns>
    public int getData(int format, int identifier)
    {
        int byteOffset = 0;
        if (m_rawWiegandData == null)
        {
            throw new Exception("no raw Wiegand data available");
        }
        //SELF-TEST
        byte[] testData_H10301 = {0x02,0x02,0x00,0x7a}; //CN=61, FC=1
        byteOffset = 0;
        int bitOffset = 1; // starting to count from right
        int numberOfBits = 16;
        int cn_h10301 = CardHex.FromByteArray(testData_H10301, byteOffset, bitOffset, numberOfBits);
        if (cn_h10301 != 61) throw new Exception("CardHex::FromArray(): System integrity error.");



        if (format == PacsDataFormat.HID_H10301 && identifier == PacsDataIdentifier.CARD_NUMBER) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 1, 16);
        //if (format == PacsDataFormat.HID_H10301 && identifier == PacsDataIdentifier.CARD_NUMBER) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 15, 16); //assuming 4-byte input, 6 leading 0 bits
        if (format == PacsDataFormat.HID_H10301 && identifier == PacsDataIdentifier.FACILITY_CODE) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 17, 8);
        //if (format == PacsDataFormat.HID_H10301 && identifier == PacsDataIdentifier.FACILITY_CODE) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 1, 8);
        if (format == PacsDataFormat.HID_H10302 && identifier == PacsDataIdentifier.CARD_NUMBER) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 1, 24);
        if (format == PacsDataFormat.HID_H10304 && identifier == PacsDataIdentifier.CARD_NUMBER) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 1, 19);
        if (format == PacsDataFormat.HID_H10304 && identifier == PacsDataIdentifier.FACILITY_CODE) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 20, 16);
        if (format == PacsDataFormat.HID_H10320 && identifier == PacsDataIdentifier.CARD_NUMBER)
        {
            long result = 0;
            // convert BCD encoded raw Wiegand skipping the least significant nibble
            //TODO: create new method in CardHex to convert from BCD to int 
            for (int k = 0; k < m_rawWiegandData.Length; k++)
            {
                int high = (int)(m_rawWiegandData[k] >> 4);
                int low = (int)(m_rawWiegandData[k] & 0x0F);
                result *= 10;
                result += high;
                if (k < m_rawWiegandData.Length - 1) // skip last digit i.e. nibble
                {
                    result *= 10;
                    result += low;
                }
            }
            return (int)result;     
        }

        // H10320 CN=12345678 dec=                      101111000110000101001110
        //        ATR_HIST(5125)=100100011010001010110011110001100 

        // H10320 CN=1 dec =     1
        //        ATR_HIST(5125)=10101

        // H10320 CN=99999999 dec =       101111101011110000011111111      1
        //        ATR_HIST(5125)=100110011001100110011001100110010100


        if (format == PacsDataFormat.HID_CORP1000 && identifier == PacsDataIdentifier.CARD_NUMBER) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 1, 20);
        if (format == PacsDataFormat.HID_CORP1000 && identifier == PacsDataIdentifier.FACILITY_CODE) return CardHex.FromByteArray(m_rawWiegandData, byteOffset, 21, 12);
        return 0;
    }
public static class PacsDataFormat
    {
        public const int UNKNOWN      = 0;
        public const int HID_H10301   = 1; // 26-bit, FAC,CN
        public const int HID_H10302   = 2; // 37-bit, CN
        public const int HID_H10304   = 3;
        public const int HID_H10320   = 4; // 32-bit, Clock-and-Data card; CN is BCD encoded
        public const int HID_CORP1000 = 5; // 35-bit, CIC,CN 
        public const int INDALA_FLEXPASS26 = 6; // 26-bit, FAC,CN
    }

    public static class PacsDataIdentifier
    {
        public const int UNKNOWN       = 0;
        public const int WEIGAND_RAW   = 1; // raw Weigand data
        public const int CARD_NUMBER   = 2; // card number as printed on card
        public const int FACILITY_CODE = 3; // facility code
        public const int CIC           = 4; // CIC
    }

我无法理解 FromByteArray 函数。 我在第 69 页找到了描述: http://www.intraproc.com/downloads/Fargo/HDP5000%20OLD/Omnikey%20CardMan%205121/Manuals/ok_contactless_developer_guide_an_en.pdf

编辑:解决方案感谢@Chris Haas

,我创建了与代码一起使用的 FromByteArray 函数
    /// call example: FromByteArray(atrByteArray, byteOffset: 0, int bitOffset: 1, int numberOfBits: 16)
    /// call example: FromByteArray(atrByteArray, byteOffset: 0, int bitOffset: 17, int numberOfBits: 8)
    public static long FromByteArray(byte[] atrByteArray, int byteOffset, int bitOffset, int numberOfBits)
    {
        var hexString = ByteArrayToString(atrByteArray);

        var start_number = Int64.Parse( hexString, NumberStyles.HexNumber );

        Int64 a_26_only = start_number & 0x3FFFFFF; //26 bits, 11 1111 1111 1111 1111 1111 1111             



        Int64 result = (a_26_only >> bitOffset) & (long)(Math.Pow(2,numberOfBits)-1);       

        return result;
    }

    public static string ByteArrayToString(byte[] ba)
    {
      StringBuilder hex = new StringBuilder(ba.Length * 2);
      foreach (byte b in ba)
        hex.AppendFormat("{0:x2}", b);
      return hex.ToString();
    }

和用法:

        byte[] atrByteArray = new byte[] {59, 5, 0, 2 , 241, 6, 115};
        var cardNumber = FromByteArray(atrByteArray, 0, 1, 16);
        var facilityCode = FromByteArray(atrByteArray, 0, 17, 8);

        Console.WriteLine(string.Format("Card number is: {0} and facility code is: {1}", cardNumber, facilityCode));

我不确定全长数字 3B050002F10673 是什么,但根据规范,您只对它最右边的 26 位感兴趣。

Int64 start = 0x3B050002F10673;

Int64 a_26_only = start & 0x3FFFFFF; //26 bits, 11 1111 1111 1111 1111 1111 1111

然后,根据规范,最右边的位是奇偶校验位,因此检查后可以丢弃它:

Int64 a_without_parity = a_26_only >> 1;

最后卡号最右16位:

Int64 card_number = a_without_parity & 0xffff;

设施代码是接下来的 8 位:

Int64 facility_code = (a_without_parity >> 16 ) & 0xff;