如何通过 Java 从 EMV 智能卡读取 PAN

发布于 2024-07-08 22:45:50 字数 181 浏览 16 评论 0原文

我需要使用智能卡读卡器读取 Maestro/Mastercard 的帐号。 我正在使用 Java 1.6 及其 javax.smartcardio 包。 我需要发送 APDU 命令,该命令将向卡芯片上存储的 EMV 应用程序询问 PAN 号码。 问题是,我找不到常规字节数组来构造 APDU 命令,该命令将在任何地方返回所需的数据......

I need to read account number from Maestro/Mastercard with smart card reader. I am using Java 1.6 and its javax.smartcardio package. I need to send APDU command which will ask EMV application stored on card's chip for PAN number. Problem is, I cannot find regular byte array to construct APDU command which will return needed data anywhere...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

远山浅 2024-07-15 22:45:50

您不需要进一步包装 APDU。 API 层应该处理这个问题。

看起来 0x6D00 响应只是意味着应用程序不支持 INS。

现在只是进行故障排除,但您确实是从选择 MasterCard 应用程序开始的,对吧?

即像这样:

void selectApplication(CardChannel channel) throws CardException {
  byte[] masterCardRid = new byte[]{0xA0, 0x00, 0x00, 0x00, 0x04};
  CommandAPDU command = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, masterCardRid);
  ResponseAPDU response = channel.transmit(command);
  return response.getData();
}

You shouldn't need to wrap the APDU further. The API layer should take care of that.

It looks like the 0x6D00 response just means that the application did not support the INS.

Just troubleshooting now, but you did start out by selecting the MasterCard application, right?

I.e. something like this:

void selectApplication(CardChannel channel) throws CardException {
  byte[] masterCardRid = new byte[]{0xA0, 0x00, 0x00, 0x00, 0x04};
  CommandAPDU command = new CommandAPDU(0x00, 0xA4, 0x04, 0x00, masterCardRid);
  ResponseAPDU response = channel.transmit(command);
  return response.getData();
}
吃颗糖壮壮胆 2024-07-15 22:45:50

这是一些工作示例:

CardChannel channel = card.getBasicChannel(); 

 byte[] selectMaestro={(byte)0x00, (byte)0xA4,(byte)0x04,(byte)0x00 ,(byte)0x07 ,(byte)0xA0 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x04 ,(byte)0x30 ,(byte)0x60 ,(byte)0x00};
  byte[] getProcessingOptions={(byte)0x80,(byte)0xA8,(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x83,(byte)0x00,(byte)0x00};
  byte[] readRecord={(byte)0x00,(byte)0xB2,(byte)0x02,(byte)0x0C,(byte)0x00};

  ResponseAPDU r=null;

   try {
     ATR atr = card.getATR(); //reset kartice

      CommandAPDU capdu=new CommandAPDU( selectMaestro   );

       r=card.getBasicChannel().transmit( capdu );

      capdu=new CommandAPDU(getProcessingOptions);
      r=card.getBasicChannel().transmit( capdu );


      capdu=new CommandAPDU(readRecord);
      r=card.getBasicChannel().transmit( capdu );

这适用于 Maestro 卡,我可以读取 PAN 号码,但现在我需要读取 MasterCard 的 PAN 号码。 我不知道应该更改读取记录 APDU 还是选择应用程序 APDU。 有人熟悉 APDU 吗?

here is some working example:

CardChannel channel = card.getBasicChannel(); 

 byte[] selectMaestro={(byte)0x00, (byte)0xA4,(byte)0x04,(byte)0x00 ,(byte)0x07 ,(byte)0xA0 ,(byte)0x00 ,(byte)0x00 ,(byte)0x00 ,(byte)0x04 ,(byte)0x30 ,(byte)0x60 ,(byte)0x00};
  byte[] getProcessingOptions={(byte)0x80,(byte)0xA8,(byte)0x00,(byte)0x00,(byte)0x02,(byte)0x83,(byte)0x00,(byte)0x00};
  byte[] readRecord={(byte)0x00,(byte)0xB2,(byte)0x02,(byte)0x0C,(byte)0x00};

  ResponseAPDU r=null;

   try {
     ATR atr = card.getATR(); //reset kartice

      CommandAPDU capdu=new CommandAPDU( selectMaestro   );

       r=card.getBasicChannel().transmit( capdu );

      capdu=new CommandAPDU(getProcessingOptions);
      r=card.getBasicChannel().transmit( capdu );


      capdu=new CommandAPDU(readRecord);
      r=card.getBasicChannel().transmit( capdu );

This works with Maestro card, I can read PAN number, yet now I need to read MasterCard's PAN number. I do not know should I change the read record APDU or select application APDU. Anyone familiar with APDUs?

默嘫て 2024-07-15 22:45:50
atr = open();
prints(atr);

prints("[Step 1] Select 1PAY.SYS.DDF01 to get the PSE directory");
cmd = new ISOSelect(ISOSelect.SELECT_AID, EMV4_1.AID_1PAY_SYS_DDF01);
card_response = execute(cmd);
prints(card_response);
SFI = NumUtil.hex2String((byte)((1 < < 3) | 4));

// try SFI 1 record 1
prints("[Step 2] Send READ RECORD with 0 to find out where the record is");
read = new EMVReadRecord(SFI, "01", "00");
card_response = execute(read);
prints(card_response);
byte_size = NumUtil.hex2String(card_response.getStatusWord().getSw2());

prints("[Step 3] Send READ RECORD with 1C to get the PSE data");
read = new EMVReadRecord(SFI, "01", byte_size);
card_response = execute(read);
prints(card_response);
// the AID is A0000000031010
prints("[Step 4] Now that we know the AID, select the application");

cmd = new ISOSelect(ISOSelect.SELECT_AID, "A0000000031010");
card_response = execute(cmd);
prints(card_response);
prints("[Step 5] Send GET PROCESSING OPTIONS command");

cmd = new EMVGetProcessingOptions();
card_response = execute(cmd);
prints(card_response);

// SFI for the first group of AFL is 0C

prints("[Step 6] Send READ RECORD with 0 to find out where the record is");
read = new EMVReadRecord("0C", "01", "00");
card_response = execute(read);
prints(card_response);
byte_size = NumUtil.hex2String(card_response.getStatusWord().getSw2());

prints("[Step 7] Use READ RECORD with the given number of bytes to retrieve the data");
read = new EMVReadRecord("0C", "01", byte_size);
card_response = execute(read);
prints(card_response);

data = new TLV(card_response.getData());

close();
atr = open();
prints(atr);

prints("[Step 1] Select 1PAY.SYS.DDF01 to get the PSE directory");
cmd = new ISOSelect(ISOSelect.SELECT_AID, EMV4_1.AID_1PAY_SYS_DDF01);
card_response = execute(cmd);
prints(card_response);
SFI = NumUtil.hex2String((byte)((1 < < 3) | 4));

// try SFI 1 record 1
prints("[Step 2] Send READ RECORD with 0 to find out where the record is");
read = new EMVReadRecord(SFI, "01", "00");
card_response = execute(read);
prints(card_response);
byte_size = NumUtil.hex2String(card_response.getStatusWord().getSw2());

prints("[Step 3] Send READ RECORD with 1C to get the PSE data");
read = new EMVReadRecord(SFI, "01", byte_size);
card_response = execute(read);
prints(card_response);
// the AID is A0000000031010
prints("[Step 4] Now that we know the AID, select the application");

cmd = new ISOSelect(ISOSelect.SELECT_AID, "A0000000031010");
card_response = execute(cmd);
prints(card_response);
prints("[Step 5] Send GET PROCESSING OPTIONS command");

cmd = new EMVGetProcessingOptions();
card_response = execute(cmd);
prints(card_response);

// SFI for the first group of AFL is 0C

prints("[Step 6] Send READ RECORD with 0 to find out where the record is");
read = new EMVReadRecord("0C", "01", "00");
card_response = execute(read);
prints(card_response);
byte_size = NumUtil.hex2String(card_response.getStatusWord().getSw2());

prints("[Step 7] Use READ RECORD with the given number of bytes to retrieve the data");
read = new EMVReadRecord("0C", "01", byte_size);
card_response = execute(read);
prints(card_response);

data = new TLV(card_response.getData());

close();
场罚期间 2024-07-15 22:45:50

您需要构造一个 CommandAPDU 对象并将其传递给 Transmission() 命令。

您应该能够在智能卡的文档中找到精确的命令,但这里有一个示例:

byte[] readFile(CardChannel channel) throws CardException {
  CommandAPDU command = new CommandAPDU(0xB0, 0x60, 0x10, 0x00);
  ResponseAPDU response = channel.transmit(command);
  return response.getData();
}

You need to do construct a CommandAPDU object and pass it to the transmit()-command.

You should be able to find the precise command in the documentation for your smartcard, but here is one example:

byte[] readFile(CardChannel channel) throws CardException {
  CommandAPDU command = new CommandAPDU(0xB0, 0x60, 0x10, 0x00);
  ResponseAPDU response = channel.transmit(command);
  return response.getData();
}
古镇旧梦 2024-07-15 22:45:50

您是否尝试在文档中查找 0x6D00 的含义? 看起来这可能意味着不支持 ENVELOPE 命令。 您是否尝试过使用 T=0 协议而不是 T=1 ?

我不希望我的示例适用于您的卡。 我不知道 Maestro/MasterCard 支持哪些 APDU,因此无法为您提供有效的示例。

尝试为命令指定一个明确的预期长度,如下所示:

byte[] readPan(CardChannel channel) throws CardException {
  CommandAPDU command = new CommandAPDU(0x00, 0xB2, 0x5a, 0x14, 250);
  ResponseAPDU response = channel.transmit(command);
  return response.getData();
}

Did you try looking up in your documentation what 0x6D00 means? It looks like it might mean that the ENVELOPE command is not supported. Have you tried using T=0 protocol instead of T=1?

I would not expect my example to work on your card. I don't know which APDUs the Maestro/MasterCard-supports, so I couldn't give you a working example.

Try giving the command an explicit expected length like this:

byte[] readPan(CardChannel channel) throws CardException {
  CommandAPDU command = new CommandAPDU(0x00, 0xB2, 0x5a, 0x14, 250);
  ResponseAPDU response = channel.transmit(command);
  return response.getData();
}
呢古 2024-07-15 22:45:50

使用扫描仪,获取卡片图片,使用良好的 java ocr 库扫描图片内容(如 http例如:://ocr4j.sourceforge.net/)并搜索(通常)16 位数字序列 XXXX-XXXX-XXXX-XXXX ,然后您将使用 java 从任何 EMV 卡获取 PAN。

what about using a scanner, getting a picture of the card, scanning the content of the picture with a good java ocr library ( like http://ocr4j.sourceforge.net/ for example ) and search for a (usually) 16 digit sequence XXXX-XXXX-XXXX-XXXX , then you will get the PAN from any EMV card using java.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文