All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.devnied.emvnfccard.parser.impl.EmvParser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2019 MILLAU Julien
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.github.devnied.emvnfccard.parser.impl;

import com.github.devnied.emvnfccard.enums.CommandEnum;
import com.github.devnied.emvnfccard.enums.EmvCardScheme;
import com.github.devnied.emvnfccard.enums.SwEnum;
import com.github.devnied.emvnfccard.exception.CommunicationException;
import com.github.devnied.emvnfccard.iso7816emv.EmvTags;
import com.github.devnied.emvnfccard.iso7816emv.TagAndLength;
import com.github.devnied.emvnfccard.model.Afl;
import com.github.devnied.emvnfccard.model.Application;
import com.github.devnied.emvnfccard.model.EmvCard;
import com.github.devnied.emvnfccard.model.enums.ApplicationStepEnum;
import com.github.devnied.emvnfccard.model.enums.CardStateEnum;
import com.github.devnied.emvnfccard.parser.EmvTemplate;
import com.github.devnied.emvnfccard.utils.CommandApdu;
import com.github.devnied.emvnfccard.utils.ResponseUtils;
import com.github.devnied.emvnfccard.utils.TlvUtil;
import com.github.devnied.emvnfccard.utils.TrackUtils;
import fr.devnied.bitlib.BytesUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * Emv default Parser 
* * Paypass:
* - https://www.paypass.com/pdf/public_documents/Terminal%20 * Optimization%20v2-0.pdf * * @author julien * */ public class EmvParser extends AbstractParser { /** * Class Logger */ private static final Logger LOGGER = LoggerFactory.getLogger(EmvParser.class); /** * Default EMV pattern */ private static final Pattern PATTERN = Pattern.compile(".*"); /** * Default constructor * * @param pTemplate * parser template */ public EmvParser(EmvTemplate pTemplate) { super(pTemplate); } @Override public Pattern getId() { return PATTERN; } @Override public boolean parse(Application pApplication) throws CommunicationException { return extractPublicData(pApplication); } /** * Read public card data from parameter AID * * @param pApplication * application data * @return true if succeed false otherwise * @throws CommunicationException communication error */ protected boolean extractPublicData(final Application pApplication) throws CommunicationException { boolean ret = false; // Select AID byte[] data = selectAID(pApplication.getAid()); // check response // Add SW_6285 to fix Interact issue if (ResponseUtils.contains(data, SwEnum.SW_9000, SwEnum.SW_6285)) { // Update reading state pApplication.setReadingStep(ApplicationStepEnum.SELECTED); // Parse select response ret = parse(data, pApplication); if (ret) { // Get AID String aid = BytesUtils.bytesToStringNoSpace(TlvUtil.getValue(data, EmvTags.DEDICATED_FILE_NAME)); String applicationLabel = extractApplicationLabel(data); if (applicationLabel == null) { applicationLabel = pApplication.getApplicationLabel(); } if (LOGGER.isDebugEnabled()) { LOGGER.debug("Application label:" + applicationLabel + " with Aid:" + aid); } template.get().getCard().setType(findCardScheme(aid, template.get().getCard().getCardNumber())); pApplication.setAid(BytesUtils.fromString(aid)); pApplication.setApplicationLabel(applicationLabel); pApplication.setLeftPinTry(getLeftPinTry()); pApplication.setTransactionCounter(getTransactionCounter()); template.get().getCard().setState(CardStateEnum.ACTIVE); } } return ret; } /** * Method used to find the real card scheme * * @param pAid * card complete AID * @param pCardNumber * card number * @return card scheme */ protected EmvCardScheme findCardScheme(final String pAid, final String pCardNumber) { EmvCardScheme type = EmvCardScheme.getCardTypeByAid(pAid); // Get real type for french card if (type == EmvCardScheme.CB) { type = EmvCardScheme.getCardTypeByCardNumber(pCardNumber); if (type != null) { LOGGER.debug("Real type:" + type.getName()); } } return type; } /** * Method used to parse EMV card * * @param pSelectResponse * select response data * @param pApplication * application selected * @return true if the parsing succeed false otherwise * @throws CommunicationException communication error */ protected boolean parse(final byte[] pSelectResponse, final Application pApplication) throws CommunicationException { boolean ret = false; // Get TLV log entry byte[] logEntry = getLogEntry(pSelectResponse); // Get PDOL byte[] pdol = TlvUtil.getValue(pSelectResponse, EmvTags.PDOL); // Send GPO Command byte[] gpo = getGetProcessingOptions(pdol); // Extract Bank data extractBankData(pSelectResponse); // Check empty PDOL if (!ResponseUtils.isSucceed(gpo)) { if (pdol != null) { gpo = getGetProcessingOptions(null); } // Check response if (pdol == null || !ResponseUtils.isSucceed(gpo)) { // Try to read EF 1 and record 1 gpo = template.get().getProvider().transceive(new CommandApdu(CommandEnum.READ_RECORD, 1, 0x0C, 0).toBytes()); if (!ResponseUtils.isSucceed(gpo)) { return false; } } } // Update Reading state pApplication.setReadingStep(ApplicationStepEnum.READ); // Extract commons card data (number, expire date, ...) if (extractCommonsCardData(gpo)) { // Extract log entry pApplication.setListTransactions(extractLogEntry(logEntry)); ret = true; } return ret; } /** * Method used to extract commons card data * * @param pGpo * global processing options response * @return true if the extraction succeed * @throws CommunicationException communication error */ protected boolean extractCommonsCardData(final byte[] pGpo) throws CommunicationException { boolean ret = false; // Extract data from Message Template 1 byte data[] = TlvUtil.getValue(pGpo, EmvTags.RESPONSE_MESSAGE_TEMPLATE_1); if (data != null) { data = ArrayUtils.subarray(data, 2, data.length); } else { // Extract AFL data from Message template 2 ret = extractTrackData(template.get().getCard(), pGpo); if (!ret) { data = TlvUtil.getValue(pGpo, EmvTags.APPLICATION_FILE_LOCATOR); } else { extractCardHolderName(pGpo); } } if (data != null) { // Extract Afl List listAfl = extractAfl(data); // for each AFL for (Afl afl : listAfl) { // check all records for (int index = afl.getFirstRecord(); index <= afl.getLastRecord(); index++) { byte[] info = template.get().getProvider() .transceive(new CommandApdu(CommandEnum.READ_RECORD, index, afl.getSfi() << 3 | 4, 0).toBytes()); // Extract card data if (ResponseUtils.isSucceed(info)) { extractCardHolderName(info); if (extractTrackData(template.get().getCard(), info)) { return true; } } } } } return ret; } /** * Extract list of application file locator from Afl response * * @param pAfl * AFL data * @return list of AFL */ protected List extractAfl(final byte[] pAfl) { List list = new ArrayList(); ByteArrayInputStream bai = new ByteArrayInputStream(pAfl); while (bai.available() >= 4) { Afl afl = new Afl(); afl.setSfi(bai.read() >> 3); afl.setFirstRecord(bai.read()); afl.setLastRecord(bai.read()); afl.setOfflineAuthentication(bai.read() == 1); list.add(afl); } return list; } /** * Method used to create GPO command and execute it * * @param pPdol * PDOL raw data * @return return data * @throws CommunicationException communication error */ protected byte[] getGetProcessingOptions(final byte[] pPdol) throws CommunicationException { // List Tag and length from PDOL List list = TlvUtil.parseTagAndLength(pPdol); ByteArrayOutputStream out = new ByteArrayOutputStream(); try { out.write(EmvTags.COMMAND_TEMPLATE.getTagBytes()); // COMMAND // TEMPLATE out.write(TlvUtil.getLength(list)); // ADD total length if (list != null) { for (TagAndLength tl : list) { out.write(template.get().getTerminal().constructValue(tl)); } } } catch (IOException ioe) { LOGGER.error("Construct GPO Command:" + ioe.getMessage(), ioe); } return template.get().getProvider().transceive(new CommandApdu(CommandEnum.GPO, out.toByteArray(), 0).toBytes()); } /** * Method used to extract track data from response * * @param pEmvCard * Card data * @param pData * data send by card * @return true if track 1 or track 2 can be read */ protected boolean extractTrackData(final EmvCard pEmvCard, final byte[] pData) { template.get().getCard().setTrack1(TrackUtils.extractTrack1Data(TlvUtil.getValue(pData, EmvTags.TRACK1_DATA))); template.get().getCard().setTrack2(TrackUtils.extractTrack2EquivalentData(TlvUtil.getValue(pData, EmvTags.TRACK_2_EQV_DATA, EmvTags.TRACK2_DATA))); return pEmvCard.getTrack1() != null || pEmvCard.getTrack2() != null; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy