...
პროგრამა იყენებს PC/SC ინტერფეისს. აქედან გამომდინარე, მისი პორტირება შესაძლებელია ნებისმიერ სხვა პროგრამირების ენაზე, რომლის საშუალებითაც PC/SC ინტერფეისთან მუშაობაა შესაძლებელი.
Code Block | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|
| ||||||||||
package ge.eid.card.mifare.demos; 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 ოჯახის წამკითხველებზე * და მუშაობს მხოლოდ უკონტაქტო ინტერფეისით (თუმცა ზოგიერთ მოდელს შეიძლება 2 * ინტერფეისი ჰქონდეს. კონტაქტურიც და უკონტაქტოც) * * პროგრამა იყენებს PC/SC ინტერფეისს, რომელიც სტანდარტულია როგორც Windows-ში, * ისე სხვა ოპერაციულ სისტემებში. ამ უკანასკნელებში pcsc lite-ს გამოყენებით * * @author mikheil */ public class DumpCard { // 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; /** * 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 = selectCardTerminal(); // დაველოდოთ ბარათს System.out.println("Waiting for a card.."); terminal.waitForCardPresent(0); // დავუკავშირდეთ ბარათს. იხ დოკუმენტაცია თავად მეთოდზე Card card = getCardConnection(terminal, EMULATED); try { // ავიღოთ კავშირის არხი CardChannel channel = card.getBasicChannel(); // შეგვიძლია წავიკითხოთ ბარათის UID byte[] readUIDCommand = new byte[] { (byte) 0xFF, (byte) 0xCA, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; System.out.println("UID: " + send(readUIDCommand, channel, true)); // წარმატებული წაკითხვის შემთხვევაში UID-ს შემდეგ ეწერება 9000 // ჩავტვირთოთ A გასაღები წამკითხველის მეხსიერებაში // მაგალითად, 0x07 ზონაში byte[] loadKeyCommand = createKeyLoadingCommand((byte) 0x07); System.out.println("LOAD KEY: " + send(loadKeyCommand, channel, true)); // წარმატების შემთხვევაში გამოვა 9000 byte[] readCommand; // გადავუაროთ ყველა ბლოკს (16 სექტორი, 4 ბლოკი თითოეულში) for (int blockId = 0; blockId < 4 * 16; blockId++) { // თითოეული სექტორისათვის საკმარისია ერთ ბლოკზე იდენტიფიკაცია // ამიტომაც ჩავატაროთ პროცესი დასაწყისში და ყოველ მეოთხე ბლოკზე if (blockId % 4 == 0) { System.out.println(); System.out.println("Authenticating Sector " + (blockId / 4) + " block 3"); byte authBlockId = (byte) (blockId + 3); byte keyAddress = (byte) 0x07; byte[] authCommand = createBlockAuthenticationCommand( authBlockId, keyAddress); System.out.println("AUTHENTICATE: " + send(authCommand, channel, true)); // წარმატებული აუთენტიფიკაციის შემთხვევაში ეწერება 9000 } // თუ იდენტიფიკაცია გავლილია, შეგვიძლია წავიკითხოთ ბლოკი readCommand = new byte[] { (byte) 0xFF, (byte) 0xB0, (byte) 0x00, (byte) (blockId), (byte) 0x10 }; System.out.println("READ: " + send(readCommand, channel, false)); // წარმატებული წაკითხვის შემთხვევაში მონაცემების შემდეგ ეწერება // 9000 } } finally { card.disconnect(true); } } /** * ამ მეთოდით ხდება იდენტიფიკაციის ბრძანების აწყობა * * @param blockId * ბლოკი, რომლის მიმართაც უნდა გავიაროთ აუთენტიფიკაცია * @param keyAddress * მისამართი, სადაც ჩატვირთული იყო გასაღები */ private static byte[] createBlockAuthenticationCommand(byte blockId, byte keyAddress) { byte keyTypeId = (byte) 0x60; // A გასაღები byte[] authData = new byte[] { (byte) 0x01, 0, blockId, keyTypeId, keyAddress }; // ავაწყოთ აუთენტიფიკაციის ბრძანება CommandAPDU c1 = new CommandAPDU(0xFF, 0x86, 0x00, 0x00, authData); byte[] authDateBytes = c1.getBytes(); return authDateBytes; } /** * ამ მეთოდით ხდება წამკითხველის მეხსიერებაში გასაღების ჩატვირთვის ბრძანების * აწყობა * * @param keyAddress * მისამართი, სადაც ჩატვირთული იყო გასაღები */ private static byte[] createKeyLoadingCommand(byte loadKeyTo) { byte[] loadKeyCommand; if (FACTORY_KEYS) { // მწარმოებლის გასაღებები. მოჰყვება ცარიელ ბარათებს loadKeyCommand = new byte[] { (byte) 0xFF, (byte) 0x82, (byte) 0x20, loadKeyTo, (byte) 0x06, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }; } else { if (ZERO_KEYS) { // ნულოვანი გასაღებები loadKeyCommand = new byte[] { (byte) 0xFF, (byte) 0x82, (byte) 0x20, loadKeyTo, (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00 }; } else { // MAD სტანდარტული A გასაღებები, რომელიც ადევს MAD სექტორს. // ID ბარათზე ყველა ცარიელ საქტორს სექტორს იგივე გასაღები უყენია // გასაღების მნიშვნელობაა A0A1A2A3A4A5 loadKeyCommand = new byte[] { (byte) 0xFF, (byte) 0x82, (byte) 0x20, loadKeyTo, (byte) 0x06, (byte) 0xA0, (byte) 0xA1, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5 }; } } return loadKeyCommand; } /** * ამ მეთოდით ხდება წამკითხველთან დაკავშირება. OMNIKEY 5321 USB ავტომატურად * ააქტიურებს MIFARE ემულატორს თავად წამკითხველში, ამიტომ ის არ მუშაობს ID * ბარათთან. იმისათვის, რომ წამკითხველმა იმუშაოს ID ბარათთან, აუცილებელია * ემულაციის რეჟიმის გამორთვა. MIFARE ემულაციას თვითონ ბარათი აკეთებს. * * ემულაციის გამორთვა ასევე შეიძლება სისტემური რეესტრიდან იხილეთ დოკუმენტი * www.hidglobal.com/documents/ok_contactless_developer_guide_an_en.pdf * * @param terminal * მისამართი, სადაც ჩატვირთული იყო გასაღები * @param onCardEmulation * თუ true-ა, მეთოდი გამორთავს წამკითხველზე MIFARE ემულაციას */ private static Card getCardConnection(CardTerminal terminal, boolean onCardEmulation) throws CardException { Card card = terminal.connect("T=0"); if (onCardEmulation) { // გავუშვათ ემულაციის ბრძანება byte[] ioctl = new byte[] { 4, 0, 0, 0, 4, 0, 0, 0 }; byte[] resp = card.transmitControlCommand( CM_IOCTL_SET_RFID_CONTROL_FLAGS, ioctl); System.out.println("IOCTL Sent"); System.out.println(new BigInteger(1, resp).toString(16)); // ბრძანების გაშვების შემდეგ წამკითხველი გაითიშება და თავიდან // ჩაირთვება. ამიტომ საჭიროა მასთან თავიდან დაკავშირება terminal.waitForCardPresent(0); card = terminal.connect("T=0"); } return card; } /** * ბარათის წამკითხველის არჩევა. მეთოდი იყენებს მარტივ გზას მკითხველს შეუძლია * გაცილებით უფრო დახვეწილი გზა გამოიყენოს. მაგალითად შეეკითხოს მომხმარებელს * ან აიღოს კონფიგურაციის ფაილიდან */ private static CardTerminal selectCardTerminal() throws CardException { CardTerminal terminal = null; // show the list of available terminals TerminalFactory factory = TerminalFactory.getDefault(); List<CardTerminal> terminals = factory.terminals().list(); for (int i = 0; i < terminals.size(); i++) { String terminalFull = terminals.get(i).toString(); System.out.println("Terminal: " + terminalFull); if (terminalFull.contains("CL")) { terminal = terminals.get(i); System.out.println("SELECTED Terminal: " + terminalFull); } } 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; } } |