Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Warning

მასალა რედაქტირების პროცესშია

წინამდებარე სტატია გიჩვენებთ, როგორ შეიძლება Java პროგრამირების ენის გამოყენებით ID ბარათის MIFARE Classic 1k–ს ემულატორთან მუშაობა შესაძლებელია შემდეგი კოდით

...

themeEclipse
linenumberstrue
languagejava

. კერძოდ - მისი ყველა სექტორის ყველა ბლოკის წაკითხვა.

კოდი განკუთვნილია სამაგალითოდ და არ წარმოადგენს რაიმე რეალური ამოცანის გადაწყვეტას. თუმცა ის წარმოდგენას შეგიქმნით, რა ტიპის ბრძანებები უნდა გაუგზავნოთ თქვენს წამკითხველს რათა მან შესძლოს ID ბარათიდან ან სხვა MIFARE Classic 1k თავსებადი ბარათიდან ინფორმაციის ამოკითხვა.

პროგრამა იყენებს PC/SC ინტერფეისს. აქედან გამომდინარე, მისი პორტირება შესაძლებელია ნებისმიერ სხვა პროგრამირების ენაზე, რომლის საშუალებითაც PC/SC ინტერფეისთან მუშაობაა შესაძლებელი.

Code Block
package ge.eid.card.mifare;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.List;
import javax.smartcardio.Card;
import javax.smartcardio.CardChannel;
import javax.smartcardio.CardException;
import javax.smartcardio.CardTerminal;
import javax.smartcardio.CommandAPDU;
import javax.smartcardio.TerminalFactory;


/**
 * ეს პროგრამული კოდი კითხულობს ყველა სექტორის ყველა ბლოკს MIFARE Classic 1k ემულატორიდან
 * პროგრამა გატესტილია ბარათის უკონტაქტო წამკითხველზე HID OMNIKEY 5321 USB მოდელის წამკითხველებზე
 * როგორც ორმაგი (კონტაქტური და უკონტაქტო) ისე მხოლოდ უკონტაქტო ინტერფეისით.
 * 
 * პროგრამა იყენებს PC/SC ინტერფეისს, რომელიც სტანდარტულია როგორც Windows-ში, ისე სხვა ოპერაციულ
 * სისტემებში. ამ უკანასკნელებში pcsc lite-ს გამოყენებით
 *
 * @author mikheil
 */
public class Main {

	// IOCTL ბრძანება რომლითაც შესაძლებელია ბარათის წამკითხველში MIFARE
	// ემულაციის გამორთვა - ემულაციას აკეთებს თავად ბარათი
    public static final int CM_IOCTL_SET_RFID_CONTROL_FLAGS = scardCtlCode(3213);

    private static final boolean EMULATED = true;
    private static final boolean FACTORY_KEYS = false;
    private static final boolean ZERO_KEYS = false;

   private static/**
final boolean AZRY_KEYS = false; * PC/SC Control Code
     * 
     * @param code
     * @return
     */
    public static int scardCtlCode(int code) {
        String osName = System.getProperty("os.name").toLowerCase();
        if (osName.indexOf("windows") > -1) {
            return 0x31 << 16 | code << 2;
        } else {
            return 0x42000000 + code;
        }
    }
    /**
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
		// ავირჩიოთ ტერმინალი
        CardTerminal terminal = nullselectCardTerminal();

        // showდაველოდოთ theბარათს
list of available terminals     System.out.println("Waiting for a card..");
TerminalFactory factory = TerminalFactory.getDefault(      terminal.waitForCardPresent(0);

		// დავუკავშირდეთ ბარათს. იხ დოკუმენტაცია თავად მეთოდზე
List<CardTerminal> terminals = factory.terminals().list();
           Card card = getCardConnection(terminal, EMULATED);

		// ავიღოთ კავშირის არხი
   String readerName    CardChannel channel = ""card.getBasicChannel();

       for (int// iშეგვიძლია =წავიკითხოთ 0;ბარათის iUID
< terminals.size(); i++) {     byte[] baReadUID = new byte[5];
   String terminalFull = terminals.get(i).toString();
     baReadUID = new byte[] { (byte) 0xFF, (byte) 0xCA, (byte) 0x00,
         readerName = terminalFull.substring(terminalFull.length() - 2);
       (byte) 0x00, (byte) 0x00 };
          System.out.println("TerminalUID: " + terminalFull)send(baReadUID, channel, true));
        // წარმატებული წაკითხვის შემთხვევაში /*
        UID-ს შემდეგ ეწერება 9000

        // ჩავტვირთოთ A გასაღები წამკითხველის მეხსიერებაში
		// მაგალითად, 0x07 ზონაში 
   * if (readerName.equalsIgnoreCase("01")) { terminal =    byte[] baLoadKey = createKeyLoadingCommand((byte) 0x07);
        System.out.println("LOAD KEY: " + send(baLoadKey, channel, true));
        // წარმატების შემთხვევაში გამოვა 9000
   * terminals.get(i); }     
        byte[] baRead;
        // გადავუაროთ ყველა ბლოკს (16 სექტორი, 4 ბლოკი თითოეულში)
        for (int blockId = 0; blockId < 4 * 16; blockId++) {
			// თითოეული სექტორისათვის საკმარისია ერთ ბლოკზე იდენტიფიკაცია
			// ამიტომაც ჩავატაროთ პროცესი დასაწყისში და if (terminalFull.contains("CL"))ყოველ მეოთხე ბლოკზე
            if (blockId % 4 == 0) {
                terminal = terminals.get(iSystem.out.println();
                System.out.println("CHOOSENAuthenticating Terminal:Sector " + terminalFull);(blockId / 4)
            }         }   + " block 3");
  // Establish a connection with the card         System.out.println("Waiting for a card.."byte authBlockId = (byte) (blockId + 3);
        terminal.waitForCardPresent(0)        byte keyAddress = (byte) 0x07;
        Card card = terminal.connect("T=0"       byte[] authDateBytes = createBlockAuthenticationCommand(
                        authBlockId, keyAddress);
        if (EMULATED) {        System.out.println("AUTHENTICATE: "
            byte[] ioctl = new byte[] { 4, 0, 0, 0, 4, 0  + send(authDateBytes, 0channel, 0 };
       true));
				// წარმატებული აუთენტიფიკაციის შემთხვევაში ეწერება 9000
            }

			// თუ იდენტიფიკაცია გავლილია, შეგვიძლია წავიკითხოთ ბლოკი
            baRead = new byte[] resp{ = card.transmitControlCommand(
  (byte) 0xFF, (byte) 0xB0, (byte) 0x00,
                 CM_IOCTL_SET_RFID_CONTROL_FLAGS, ioctl)   (byte) (blockId), (byte) 0x10 };
            System.out.println("IOCTLREAD: Sent")" + send(baRead, channel, false));
            System.out.println(new BigInteger(1, resp).toString(16));
    // წარმატებული წაკითხვის შემთხვევაში მონაცემების შემდეგ ეწერება 9000
       terminal.waitForCardPresent(0); }
    }

    /**
card = terminal.connect("T=0");   * ამ მეთოდით ხდება იდენტიფიკაციის ბრძანების }აწყობა
     *
  CardChannel channel = card.getBasicChannel();
        // Start with something simple, read UID, kinda like Hello World! * @param blockId ბლოკი, რომლის მიმართაც უნდა გავიაროთ აუთენტიფიკაცია
     * @param keyAddress მისამართი, სადაც ჩატვირთული იყო გასაღები
     */			
    private static byte[] baReadUID = new byte[5];
  createBlockAuthenticationCommand(byte blockId,
            byte keyAddress) {
		byte keyTypeId = (byte) 0x60; // A გასაღები
     baReadUID    byte[] authData = new byte[] { (byte) 0xFF0x01, (byte) 0xCA, (byte) 0x000, blockId, keyTypeId,
                (byte) 0x00, (byte) 0x00 }keyAddress };
		// ავაწყოთ აუთენტიფიკაციის ბრძანება
        CommandAPDU c1 = new CommandAPDU(0xFF, 0x86, 0x00, 0x00, authData);
        System.out.println("UID: " + send(baReadUID, channel, true))byte[] authDateBytes = c1.getBytes();
        return authDateBytes;
    }

    //**
If successfull, the output will end* withამ 9000მეთოდით ხდება წამკითხველის მეხსიერებაში გასაღების ჩატვირთვის
   // OK, now,* theბრძანების realაწყობა
work     *
   // Get Serial* Number@param keyAddress მისამართი, სადაც ჩატვირთული იყო გასაღები
  // Load key   */			
    private static byte[] createKeyLoadingCommand(byte loadKeyTo) {
        byte[] baLoadKey = new byte[12];
        if (FACTORY_KEYS) {
			// მწარმოებლის გასაღებები. მოჰყვება ცარიელ ბარათებს
            baLoadKey = new byte[] { (byte) 0xFF, (byte) 0x82, (byte) 0x20,
                    (byte) 0x07loadKeyTo, (byte) 0x06, (byte) 0xFF, (byte) 0xFF,
                    (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF };
        } else {
            if (ZERO_KEYS) {
				// ნულოვანი გასაღებები
                baLoadKey = new byte[] { (byte) 0xFF, (byte) 0x82, (byte) 0x20,
                        (byte) 0x07loadKeyTo, (byte) 0x06, (byte) 0x00, (byte) 0x00,
                        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
            } else {
				// MAD სტანდარტული A გასაღებები, რომელიც ადევს MAD სექტორს. 
				// ID ბარათზე ყველა ცარიელ საქტორს სექტორს იგივე გასაღები უყენია
				// გასაღების მნიშვნელობაა A0A1A2A3A4A5
                baLoadKey = new byte[] { (byte) 0xFF, (byte) 0x82, (byte) 0x20,
                          (byte) 0x20loadKeyTo, (byte) 0x070x06, (byte) 0x060xA0, (byte) 0xA00xA1,
                            (byte) 0xA10xA2, (byte) 0xA20xA3, (byte) 0xA30xA4, (byte) 0xA4,0xA5 };
            }
        }
     (byte) 0xA5 } return baLoadKey;
    }


    /**
}     * ამ მეთოდით ხდება }წამკითხველთან დაკავშირება. OMNIKEY 5321 USB 
   System.out.println("LOAD KEY: " + send(baLoadKey, channel, true));  * ავტომატურად ააქტიურებს MIFARE ემულატორს თავად წამკითხველში, ამიტომ
     * ის არ მუშაობს ID ბარათთან.
     * იმისათვის, რომ წამკითხველმა იმუშაოს ID ბარათთან, აუცილებელია
     * ემულაციის რეჟიმის //გამორთვა. If successfull, will output 9000
MIFARE ემულაციას თვითონ ბარათი აკეთებს
     *
     * @param terminal მისამართი, სადაც ჩატვირთული იყო გასაღები
     * @param intonCardEmulation sec = 0;

  თუ true-ა, მეთოდი გამორთავს წამკითხველზე MIFARE ემულაციას
     *//	
If successfull    private static Card getCardConnection(CardTerminal terminal,
will output 9000          boolean onCardEmulation) throws CardException {
        Card card = terminal.connect("T=0");
        if (onCardEmulation) {
			// Readგავუშვათ Serialემულაციის ბრძანება
            byte[] baReadioctl = new byte[6]; { 4, 0, 0, 0, 4, 0, 0, for0 (int};
i = 0; i < 4 * 16; i++) {   byte[] resp = card.transmitControlCommand(
      if (i % 4 == 0) {        CM_IOCTL_SET_RFID_CONTROL_FLAGS, ioctl);
            System.out.println("IOCTL Sent");
                System.out.println("Sector " + (i / 4));
                byte[] authData = new byte[] { (byte) 0x01, 0, (byte) (i + 3),new BigInteger(1, resp).toString(16));
			// ბრძანების გაშვების შემდეგ წამკითხველი გაითიშება და თავიდან ჩაირთვება
			// ამიტომ თავიდან უნდა დავუკავშირდეთ მას
            terminal.waitForCardPresent(0);
            card = terminal.connect("T=0");
        }
        return card;
    }

  (byte) 0x60, (byte) 0x07 }; /**
     * ბარათის წამკითხველის არჩევა. მეთოდი იყენებს მარტივ გზას 
    CommandAPDU c1* =მკითხველს new CommandAPDU(0xFF, 0x86, 0x00, 0x00,
  შეუძლია გაცილებით უფრო დახვეწილი გზა გამოიყენოს. მაგალითად შეეკითხოს
     * მომხმარებელს ან აიღოს კონფიგურაციის ფაილიდან
     */	
    authData)private static CardTerminal selectCardTerminal() throws CardException {
        CardTerminal terminal = null;
        // show the list of available terminals
 System.out.println("AUTHENTICATE: "       TerminalFactory factory = TerminalFactory.getDefault();
        List<CardTerminal> terminals = factory.terminals().list();
        
    + send(c1.getBytes(), channel, true)   for (int i = 0; i < terminals.size(); i++) {
            String terminalFull = terminals.get(i).toString();
            }
            baRead = new byte[] { (byte) 0xFF, (byte) 0xB0, (byte) 0x00,System.out.println("Terminal: " + terminalFull);
            
            if  (byte) (terminalFull.contains("CL")) {
                terminal = terminals.get(i),;
(byte) 0x10 };              System.out.println("READSELECTED Terminal: " + send(baRead, channel, false))terminalFull);
            //}
If successfull, the output will end with 9000 }
       } return terminal;
    }

    /**
     * ბრძანების გაგზავნა წამკითხველთან და პასუხის მიღება 
     * @param cmd ბრძანება ბაიტების მასივში
     * @param channel კავშირის არხი ბარათთან
     * @param echo გამოვიდეს თუ არა ბრძანება ეკრანზე?
     */	
    public static String send(byte[] cmd, CardChannel channel, boolean echo) {
        String res = "";
        for (int i = 0; i < cmd.length; i++) {
            res += String.format("%02X", cmd[i]);
            // The result is formatted as a hexadecimal integer
        }
        if (echo) {
            System.out.println("Sending " + res);
        }
        byte[] baResp = new byte[258];
        ByteBuffer bufCmd = ByteBuffer.wrap(cmd);
        ByteBuffer bufResp = ByteBuffer.wrap(baResp);
        // output = The length of the received response APDU
        int output = 0;
        try {
            output = channel.transmit(bufCmd, bufResp);
        } catch (CardException ex) {
            ex.printStackTrace();
        }
        res = "";
        for (int i = 0; i < output; i++) {
            res += String.format("%02X", baResp[i]);
            // The result is formatted as a hexadecimal integer
        }
        return res;
    }
}