Java 卡片小程序、安全数据传输和安全通道
Java Card applets, secure data transmission and Secure Channel
我想以一种方式编写我的小程序,使其 APDU 命令和状态字在我的卡和我的 reader 之间的传输通道中不清晰。我的意思是我不想将 APDU 命令和响应以纯文本形式发送给第三方。
我想我有两个选择:
- 在卡上选择我的小程序后,对于所有其他命令,对APDU命令的数据部分进行加密功能并在卡上对其进行解密,然后进行分析他们。请注意,我无法使用此方法加密整个命令,因为结果可能与另一个
SELECT
APDU 命令冲突,并且卡的 SD 将其错误地识别为 SELECT
命令。是吗?
其示意图:
- 使用 SD 安全通道:据我所知,安全通道意味着:整个 APDU 命令 和 responses 以加密形式传输(即它们在源 (Security Domain/Card reader) 中加密并在目标 (Secutity Domain/Card Reader) 中解密。是这样吗?据我所知,SD 在此机制中执行加密方法角色,我的小程序和 SD 之间的通信是简单的(下图),对吗?
其示意图:
还有其他方法吗?
看来第一个解决方案不够好,因为:
- 我必须自己实现! :)
- 我们无法对第三方隐藏命令和响应的所有部分。(我们只能隐藏数据)
我说得对吗?
现在,假设我想确保我的小程序仅适用于使用安全通道传输的 APDU 命令。我想我又有两个选择:
将卡片置于SECURED
状态。由于用户无法在此状态下使用纯文本 APDU 命令与卡通信(对吗?),因此他必须使用安全通道将命令发送到我的小程序。正确的?如果不正确,有没有办法强制 SD 仅与安全通道一起工作?
将卡保持在任何生命周期(例如OP_READY),但是,在接收到任何 APDU 命令时,检查 CLA段看是不是安全传输的! (这可能吗?来自安全通道的 APDU 命令的 CLA
部分和其他部分有什么区别吗?我说的对吗?)
还有其他方法吗?
最后是主要问题:
如何使用 SD 与我的小程序进行安全通信?因为我认为我必须使用 GlobalPlatform 类(是吗?),所以我看了一下它的 API-s。我在名为 org.globalplatform.GPSystem
的包中找到了一个名为 getSecureChannel
的方法。我走对路了吗?我必须使用这种方法吗?
我知道回答这个问题可能太长了,但我相信它不仅为我而且为其他未来的观众澄清了很多问题。
感谢任何人在这个问题上为我提供任何帮助。
还有一个示例小程序更有价值。
我按顺序回答:
- 是的,对于 ISO/IEC 7816-4,只有数据部分是加密的。 header 仅受身份验证标记保护。
- 不,全球平台安全通道也只是(可选)加密数据。完整性超过 header 和命令数据。
- 不,安全状态仅适用于全球平台,您必须使用卡上的 GP API 自行编程。 GP API 具有执行身份验证、请求安全通道和检索当前状态的访问方法。
- 正确,CLA 字节决定 APDU 是否加密(不是 如何 它是加密的)。如果 CLA 的第一位为零,那么您的安全通道必须符合 ISO/IEC 7816-4.
不用担心通过小程序进行安全通道通信。如果您在小程序中使用 Global Platform API,则非常简单。
您无需考虑很多问题,只需尝试编写一个安全通道小程序,它会根据命令数据中定义的安全级别处理您的小程序。
参考 GP 安全通道 API:
http://www.win.tue.nl/pinpasjc/docs/apis/gp22/
并且您应该将卡保持在 SECURED 状态。
这是安全通道 scp02 的示例小程序:
package secureChannel;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
public class Scp02 extends Applet
{
final static byte INIT_UPDATE = (byte) 0x50;
final static byte EXT_AUTHENTICATE = (byte) 0x82;
final static byte STORE_DATA = (byte) 0xE2;
public static void install(byte[] bArray, short sOffset, byte bLength)
{
new Scp02().register(bArray, sOffset, bLength);
}
public void process(APDU apdu) throws ISOException
{
SecureChannel sc = GPSystem.getSecureChannel();
byte[] buffer = apdu.getBuffer();
short inlength = 0;
switch (ISO7816.OFFSET_INS)
{
case INIT_UPDATE:
case EXT_AUTHENTICATE:
inlength = apdu.setIncomingAndReceive();
sc.processSecurity(apdu);
break;
case STORE_DATA:
//Receive command data
inlength = apdu.setIncomingAndReceive();
inlength = sc.unwrap(buffer, (short) 0, inlength);
apdu.setOutgoingAndSend((short)0, inlength);
//Process data
break;
}
}
}
为了 Google 搜索,来自 Anurag Bajpai 的代码如果不稍加修改就无法工作,因为如 GP 安全通道 API 中所述,小程序应输出最终响应数据:
If response data is present, this data will be placed in the APDU buffer at offset ISO7816.OFFSET_CDATA. The return value indicates the length and the applet is responsible for outputting this data if necessary.
因此,更正后的代码是:
package secureChannel;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
public class Scp02 extends Applet
{
final static byte INIT_UPDATE = (byte) 0x50;
final static byte EXT_AUTHENTICATE = (byte) 0x82;
final static byte STORE_DATA = (byte) 0xE2;
public static void install(byte[] bArray, short sOffset, byte bLength)
{
new Scp02().register(bArray, sOffset, bLength);
}
public void process(APDU apdu) throws ISOException
{
SecureChannel sc = GPSystem.getSecureChannel();
byte[] buffer = apdu.getBuffer();
short inlength = 0;
switch (ISO7816.OFFSET_INS)
{
case INIT_UPDATE:
case EXT_AUTHENTICATE:
inlength = apdu.setIncomingAndReceive();
short respLen = sc.processSecurity(apdu);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, respLen);
break;
case STORE_DATA:
//Receive command data
inlength = apdu.setIncomingAndReceive();
inlength = sc.unwrap(buffer, (short) 0, inlength);
apdu.setOutgoingAndSend((short)0, inlength);
//Process data
break;
}
}
}
请注意,在调用 sc.processSecurity(apdu)
之前调用 apdu.setIncomingAndReceive()
是不正确的,因为 processSecurity()“负责接收已识别命令的数据字段”。
我想以一种方式编写我的小程序,使其 APDU 命令和状态字在我的卡和我的 reader 之间的传输通道中不清晰。我的意思是我不想将 APDU 命令和响应以纯文本形式发送给第三方。
我想我有两个选择:
- 在卡上选择我的小程序后,对于所有其他命令,对APDU命令的数据部分进行加密功能并在卡上对其进行解密,然后进行分析他们。请注意,我无法使用此方法加密整个命令,因为结果可能与另一个
SELECT
APDU 命令冲突,并且卡的 SD 将其错误地识别为SELECT
命令。是吗?
其示意图:
- 使用 SD 安全通道:据我所知,安全通道意味着:整个 APDU 命令 和 responses 以加密形式传输(即它们在源 (Security Domain/Card reader) 中加密并在目标 (Secutity Domain/Card Reader) 中解密。是这样吗?据我所知,SD 在此机制中执行加密方法角色,我的小程序和 SD 之间的通信是简单的(下图),对吗?
其示意图:
还有其他方法吗?
看来第一个解决方案不够好,因为:
- 我必须自己实现! :)
- 我们无法对第三方隐藏命令和响应的所有部分。(我们只能隐藏数据)
我说得对吗?
现在,假设我想确保我的小程序仅适用于使用安全通道传输的 APDU 命令。我想我又有两个选择:
将卡片置于
SECURED
状态。由于用户无法在此状态下使用纯文本 APDU 命令与卡通信(对吗?),因此他必须使用安全通道将命令发送到我的小程序。正确的?如果不正确,有没有办法强制 SD 仅与安全通道一起工作?将卡保持在任何生命周期(例如OP_READY),但是,在接收到任何 APDU 命令时,检查 CLA段看是不是安全传输的! (这可能吗?来自安全通道的 APDU 命令的
CLA
部分和其他部分有什么区别吗?我说的对吗?)
还有其他方法吗?
最后是主要问题:
如何使用 SD 与我的小程序进行安全通信?因为我认为我必须使用 GlobalPlatform 类(是吗?),所以我看了一下它的 API-s。我在名为 org.globalplatform.GPSystem
的包中找到了一个名为 getSecureChannel
的方法。我走对路了吗?我必须使用这种方法吗?
我知道回答这个问题可能太长了,但我相信它不仅为我而且为其他未来的观众澄清了很多问题。
感谢任何人在这个问题上为我提供任何帮助。
还有一个示例小程序更有价值。
我按顺序回答:
- 是的,对于 ISO/IEC 7816-4,只有数据部分是加密的。 header 仅受身份验证标记保护。
- 不,全球平台安全通道也只是(可选)加密数据。完整性超过 header 和命令数据。
- 不,安全状态仅适用于全球平台,您必须使用卡上的 GP API 自行编程。 GP API 具有执行身份验证、请求安全通道和检索当前状态的访问方法。
- 正确,CLA 字节决定 APDU 是否加密(不是 如何 它是加密的)。如果 CLA 的第一位为零,那么您的安全通道必须符合 ISO/IEC 7816-4.
不用担心通过小程序进行安全通道通信。如果您在小程序中使用 Global Platform API,则非常简单。
您无需考虑很多问题,只需尝试编写一个安全通道小程序,它会根据命令数据中定义的安全级别处理您的小程序。
参考 GP 安全通道 API: http://www.win.tue.nl/pinpasjc/docs/apis/gp22/
并且您应该将卡保持在 SECURED 状态。
这是安全通道 scp02 的示例小程序:
package secureChannel;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
public class Scp02 extends Applet
{
final static byte INIT_UPDATE = (byte) 0x50;
final static byte EXT_AUTHENTICATE = (byte) 0x82;
final static byte STORE_DATA = (byte) 0xE2;
public static void install(byte[] bArray, short sOffset, byte bLength)
{
new Scp02().register(bArray, sOffset, bLength);
}
public void process(APDU apdu) throws ISOException
{
SecureChannel sc = GPSystem.getSecureChannel();
byte[] buffer = apdu.getBuffer();
short inlength = 0;
switch (ISO7816.OFFSET_INS)
{
case INIT_UPDATE:
case EXT_AUTHENTICATE:
inlength = apdu.setIncomingAndReceive();
sc.processSecurity(apdu);
break;
case STORE_DATA:
//Receive command data
inlength = apdu.setIncomingAndReceive();
inlength = sc.unwrap(buffer, (short) 0, inlength);
apdu.setOutgoingAndSend((short)0, inlength);
//Process data
break;
}
}
}
为了 Google 搜索,来自 Anurag Bajpai 的代码如果不稍加修改就无法工作,因为如 GP 安全通道 API 中所述,小程序应输出最终响应数据:
If response data is present, this data will be placed in the APDU buffer at offset ISO7816.OFFSET_CDATA. The return value indicates the length and the applet is responsible for outputting this data if necessary.
因此,更正后的代码是:
package secureChannel;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
public class Scp02 extends Applet
{
final static byte INIT_UPDATE = (byte) 0x50;
final static byte EXT_AUTHENTICATE = (byte) 0x82;
final static byte STORE_DATA = (byte) 0xE2;
public static void install(byte[] bArray, short sOffset, byte bLength)
{
new Scp02().register(bArray, sOffset, bLength);
}
public void process(APDU apdu) throws ISOException
{
SecureChannel sc = GPSystem.getSecureChannel();
byte[] buffer = apdu.getBuffer();
short inlength = 0;
switch (ISO7816.OFFSET_INS)
{
case INIT_UPDATE:
case EXT_AUTHENTICATE:
inlength = apdu.setIncomingAndReceive();
short respLen = sc.processSecurity(apdu);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, respLen);
break;
case STORE_DATA:
//Receive command data
inlength = apdu.setIncomingAndReceive();
inlength = sc.unwrap(buffer, (short) 0, inlength);
apdu.setOutgoingAndSend((short)0, inlength);
//Process data
break;
}
}
}
请注意,在调用 sc.processSecurity(apdu)
之前调用 apdu.setIncomingAndReceive()
是不正确的,因为 processSecurity()“负责接收已识别命令的数据字段”。