/** * Write all the blocks in a Tag to the Mifare tag. * * @param t * @throws IOException */ public void write(Tag t) throws IOException { if (t.getSectorCount() > mKeys.getSectorCount()) throw new IOException("Too few keys"); mTag.connect(); int sectors = mTag.getSectorCount(); try { for (int s = 0; s < sectors; ++s) { // Authenticate for each sector (try B key, then A key) if (!mTag.authenticateSectorWithKeyB(s, mKeys.getKeyB(s)) && !mTag.authenticateSectorWithKeyA(s, mKeys.getKeyA(s))) throw new IOException("Auth error"); // Write to tag. Skip block 0 and the trailer of each sector int blockOffset = mTag.sectorToBlock(s); int lastBlock = blockOffset + mTag.getBlockCountInSector(s); // Skip block 0 blockOffset = blockOffset == 0 ? 1 : blockOffset; for (int b = blockOffset; b < lastBlock; ++b) { mTag.writeBlock(b, t.getBlock(b)); if (mProgressListener != null) mProgressListener.publishProgress((100 * b) / t.getBlockCount()); } } } finally { mTag.close(); } }
/** * Authenticate to given sector of the tag. * * @param sectorIndex The sector to authenticate to. * @param key Key for the authentication. * @param useAsKeyB If true, key will be treated as key B for authentication. * @return True if authentication was successful. False otherwise. */ private boolean authenticate(int sectorIndex, byte[] key, boolean useAsKeyB) { try { if (!useAsKeyB) { // Key A. return mMFC.authenticateSectorWithKeyA(sectorIndex, key); } else { // Key B. return mMFC.authenticateSectorWithKeyB(sectorIndex, key); } } catch (IOException e) { Log.d(LOG_TAG, "Error while authenticate with tag."); } return false; }
/** * Read all blocks from the Mifare tag and return the data in a Tag object. * * @return A Tag object containing the data of the Mifare tag. * @throws IOException */ public Tag read() throws IOException { int sectors = mTag.getSectorCount(); Tag t = new Tag(TagType.getType(sectors)); if (t.getSectorCount() > mKeys.getSectorCount()) throw new IOException("Too few keys"); mTag.connect(); try { for (int s = 0; s < sectors; ++s) { byte[] aKey = mKeys.getKeyA(s); byte[] bKey = mKeys.getKeyB(s); // Authenticate for each sector (try A key, then B key) if (!mTag.authenticateSectorWithKeyA(s, aKey) && !mTag.authenticateSectorWithKeyB(s, bKey)) throw new IOException("Auth error"); // Read every block of the sector int blockOffset = mTag.sectorToBlock(s); int lastBlock = blockOffset + mTag.getBlockCountInSector(s); for (int b = blockOffset; b < lastBlock; ++b) { byte[] readBuffer = mTag.readBlock(b); // Manually transfer key data to tag since it is usually not // readable if (b == lastBlock - 1) { for (int i = 0; i < Tag.KEY_SIZE; ++i) { readBuffer[i] = aKey[i]; readBuffer[Tag.BLOCK_SIZE - Tag.KEY_SIZE + i] = bKey[i]; } } t.setBlock(b, readBuffer); if (mProgressListener != null) mProgressListener.publishProgress((100 * b) / t.getBlockCount()); } } return t; } finally { mTag.close(); } }
/** * Test if all the keys in the KeyChain are valid, i.e. can be used for authenticating. * * @return true if all A and B keys can be used to authenticate, false otherwise. * @throws IOException */ public boolean testKeys() throws IOException { // Tag and key sector count mismatch. // Need at least as many keys as there are sectors. if (mTag.getSectorCount() > mKeys.getSectorCount()) return false; mTag.connect(); try { for (int i = 0; i < mTag.getSectorCount(); ++i) { if (!mTag.authenticateSectorWithKeyA(i, mKeys.getKeyA(i)) || !mTag.authenticateSectorWithKeyB(i, mKeys.getKeyB(i))) { return false; } } } finally { mTag.close(); } return true; }
/** * Build Key-Value Pairs in which keys represent the sector and values are one or both of the * Mifare keys (A/B). The Mifare key information must be set before calling this method (use * {@link #setKeyFile(File[], Context)}). Also the mapping range must be specified before calling * this method (use {@link #setMappingRange(int, int)}).<br> * <br> * The mapping works like some kind of dictionary attack. All keys are checked against the next * sector with both authentication methods (A/B). If at least one key was found for a sector, the * map will be extended with an entry, containing the key(s) and the information for what sector * the key(s) are. You can get this Key-Value Pairs by calling {@link #getKeyMap()}. A full key * map can be gained by calling this method as often as there are sectors on the tag (See {@link * #getSectorCount()}). If you call this method once more after a full key map was created, it * resets the key map an starts all over. * * @return The sector that was checked at the moment. On error it returns "-1" and resets the key * map to "null". * @see #getKeyMap() * @see #setKeyFile(File[], Context) * @see #setMappingRange(int, int) * @see #readAsMuchAsPossible(SparseArray) */ public int buildNextKeyMapPart() { // Clear status and key map before new walk through sectors. boolean error = false; if (mKeysWithOrder != null && mLastSector != -1) { if (mKeyMapStatus == mLastSector + 1) { mKeyMapStatus = mFirstSector; mKeyMap = new SparseArray<byte[][]>(); } // Get auto reconnect setting. boolean autoReconnect = Common.getPreferences().getBoolean(Preference.AutoReconnect.toString(), false); byte[][] keys = new byte[2][]; boolean[] foundKeys = new boolean[] {false, false}; // Check next sector against all keys (lines) with // authentication method A and B. for (int i = 0; i < mKeysWithOrder.size(); ) { byte[] key = mKeysWithOrder.get(i); try { if (!foundKeys[0] && mMFC.authenticateSectorWithKeyA(mKeyMapStatus, key)) { keys[0] = key; foundKeys[0] = true; } if (!foundKeys[1] && mMFC.authenticateSectorWithKeyB(mKeyMapStatus, key)) { keys[1] = key; foundKeys[1] = true; } } catch (Exception e) { Log.d(LOG_TAG, "Error while building next key map part"); // Is auto reconnect enabled? if (autoReconnect) { Log.d(LOG_TAG, "Auto recconect is enabled"); while (!isConnected()) { // Sleep for 500ms. try { Thread.sleep(500); } catch (InterruptedException ex) { // Do nothing. } // Try to reconnect. try { connect(); } catch (IOException ex) { // Do nothing. } } // Repeat last loop (do not incr. i). continue; } else { error = true; break; } } if (foundKeys[0] && foundKeys[1]) { // Both keys found. Continue with next sector. break; } i++; } if (!error && (foundKeys[0] || foundKeys[1])) { // At least one key found. Add key(s). mKeyMap.put(mKeyMapStatus, keys); // Key reuse is very likely, so try these first // for the next sector. if (foundKeys[0]) { mKeysWithOrder.remove(keys[0]); mKeysWithOrder.add(0, keys[0]); } if (foundKeys[1]) { mKeysWithOrder.remove(keys[1]); mKeysWithOrder.add(0, keys[1]); } } mKeyMapStatus++; } else { error = true; } if (error) { mKeyMapStatus = 0; mKeyMap = null; return -1; } return mKeyMapStatus - 1; }
private void resolveIntent(Intent intent) { // 1) Parse the intent and get the action that triggered this intent String action = intent.getAction(); // 2) Check if it was triggered by a tag discovered interruption. if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) { mydialog = ProgressDialog.show(ReadCardActivity.this, "loading", "loading"); // 3) Get an instance of the TAG from the NfcAdapter Tag tagFromIntent = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); // 4) Get an instance of the Mifare classic card from this TAG // intent MifareClassic mfc = MifareClassic.get(tagFromIntent); MifareClassCard mifareClassCard = null; try { // 5.1) Connect to card mfc.connect(); // 5.2) and get the number of sectors this card has..and loop // thru these sectors int secCount = mfc.getSectorCount(); mifareClassCard = new MifareClassCard(secCount); // init information String techtemp = ""; for (String tech : mfc.getTag().getTechList()) { techtemp = techtemp + tech + "\n"; } mifareClassCard.addValue("UID", Converter.getHexString(mfc.getTag().getId())); mifareClassCard.addValue("TechList", techtemp); // mifareClassCard.addValue("MemorySize", mfc.get); int bCount = 0; int bIndex = 0; for (int j = 0; j < secCount; j++) { boolean authKeyA = false; boolean authKeyB = false; MifareSector mifareSector = new MifareSector(); mifareSector.sectorIndex = j; // 6.1) authenticate the sector for (String key : defaultKeys) { if (!authKeyA) { authKeyA = mfc.authenticateSectorWithKeyA(j, Converter.hexStringToByteArray(key)); mifareSector.keyA = new MifareKey(Converter.hexStringToByteArray(key)); } if (!authKeyB) { authKeyB = mfc.authenticateSectorWithKeyB(j, Converter.hexStringToByteArray(key)); mifareSector.keyB = new MifareKey(Converter.hexStringToByteArray(key)); } // 都满足则break if (authKeyA && authKeyB) { break; } } if ((authKeyA && authKeyB) || authKeyA || authKeyB) { mifareSector.authorized = true; // 6.2) In each sector - get the block count bCount = mfc.getBlockCountInSector(j); bCount = Math.min(bCount, MifareSector.BLOCKCOUNT); bIndex = mfc.sectorToBlock(j); // set access condition byte[] acdata = mfc.readBlock(bIndex + 3); String hexString = Converter.getHexString(acdata, 16); mifareSector.accessCondition = hexString.substring(12, 20); for (int i = 0; i < bCount; i++) { // 6.3) Read the block byte[] data = mfc.readBlock(bIndex); // System.out.println(Converter.getHexString(data, 16)); MifareBlock mifareBlock = new MifareBlock(mifareSector); mifareBlock.setData(data); mifareBlock.setIndex(bIndex); // 7) Convert the data into a string from Hex // format. bIndex++; mifareSector.blocks[i] = mifareBlock; } mifareClassCard.setSector(mifareSector.sectorIndex, mifareSector); } else { // Authentication failed - Handle it mifareSector.authorized = false; } } this.mydialog.dismiss(); mfc.close(); Intent dataIntent = new Intent(this, ViewCardActivity.class); Bundle bundle = new Bundle(); bundle.putSerializable("mifare", mifareClassCard); dataIntent.putExtras(bundle); this.startActivityForResult(dataIntent, 0); this.finish(); } catch (IOException e) { Log.e(TAG, e.getLocalizedMessage()); showAlert(3); this.mydialog.dismiss(); } finally { if (mifareClassCard != null) { // mifareClassCard.debugPrint(); } } } // End of IF } // End of method