如何自锁 Javacard Applet

How to Self-Lock a Javacard Applet

我的问题是是否可以从小程序本身的代码中锁定小程序,作为从代码中检测到的操作的对策。

显而易见的选择是使用 GPSystem.lockCard(); 并且它有效,但是我想知道是否可以只锁定小程序。我还可以从相关安全域的经过身份验证的会话中锁定小程序本身。但是有没有可能从小程序代码本身。看来,鉴于 GPSystem.setCardContentState(); 方法与 GPSystem.APPLICATION_LOCKED 一起使用,所以我也测试了它,但它不起作用。

重新阅读 GP 卡规范 2.2 PDF 的说明:

The OPEN shall reject any transition request from the Life Cycle State LOCKED;

在我的 Eclipse 中,JavaDoc 说:

The OPEN shall reject any transition request to the Life Cycle State LOCKED

这是怎么回事?

是的,很简单:使用 private static boolean 标志并在 process(APDU apdu) 方法的开头检查它:

public class MiniApplet extends Applet {

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new MiniApplet();
    }

    protected MiniApplet() {
        register();
    }

    private static final short SW_APPLET_IS_LOCKED = (short) 0x9199; //any error SW
    private static boolean appletLocked = false; //static -> faster access, this flag is checked each call! "private" modifier is VERY important!

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return; //it is a good practice not to throw any exceptions on SELECT command
        }

        if (appletLocked) { 
            ISOException.throwIt(SW_APPLET_IS_LOCKED);
        }

        if (attackDetected()) { //implement your attack detection
            appletLocked = true;
        }
    }   
}

是的。这是 GlobalPlatform 应用程序的常见和预期操作。您必须以正确的权限 (gp -install -privs CardLock) 安装您的应用程序,执行此操作的代码是:

GPSystem.setCardContentState(GPSystem.APPLICATION_LOCKED);

您稍后可以使用

解锁应用程序
gp -unlock-applet <aid>

看看这种机制如何从 GlobalPlatform Card 规范 2.1.1 演变到 2.2.1(在 2.3 中仍然相同)很有趣:

  1. 在 GP 2.1.1 中,只允许发卡机构(卡外实体)或 OPEN(作为 "exceptions" 的结果)启动应用程序锁定:

    The Card Issuer has a mechanism to disable the continued execution status of an on-card Application. This mechanism may be invoked from within the OPEN based on exceptions handled by the OPEN or from the use of externally invoked commands. The Card Issuer is the only entity that may initiate the locking of an Application.

    方法 GPSystem.setCardContentState() 明确定义为仅允许对应用程序特定的生命周期状态进行状态更改(值在 0x070x7F 之间并设置了最低的 3 位)。因为在后面的规范中 APPLICATION_LOCKED 的常量是 0x80 设置这个状态是不允许的。这个方法的注释里也说的很清楚:

    • The OPEN shall reject any transition request to the Life Cycle States INSTALLED or LOCKED.

    因此,在实现 GP 2.1.1 的卡上尝试从应用程序本身将应用程序状态设置为锁定一定会失败。

    更新(2016-05-20):我在一些 NXP JCOP 卡上测试了这个(声称符合 GP 2.1.1)设置高位或清除任何低 3 位的设置值确实失败。

  2. 这在 GP 2.2 中有所改变。现在,允许应用程序锁定自身:

    The card has a mechanism to disable and subsequently re-enable the continued execution status of an on-card Application. This mechanism may be invoked from within the OPEN based on exceptions handled by the OPEN or from the use of externally invoked commands. An Application with Global Lock privilege, the Application itself or a directly or indirectly associated Security Domain are the only entities that may initiate the locking of an Application.

    GP 卡规范不要求应用程序持有任何特定权限来锁定自身。

    不幸的是,方法 GPSystem.setCardContentState() 的 API 规范仍然不是很清楚。首先,该方法的描述仍然指出,必须只允许设置最低 3 位的 0x070x7F 之间的值:

    This method sets the Application specific Life Cycle State of the current applet context. Application specific Life Cycle States range from 0x07 to 0x7F as long as the 3 low order bits are set.

    其次,GP 卡规范 2.2 文档附录 A 的 API 文档和 API 导出文件中的 JavaDoc 中有不同的注释。而规范中的注释改为:

    • The OPEN shall reject any transition request to the Life Cycle State INSTALLED;
    • The OPEN shall reject any transition request from the Life Cycle State LOCKED;

    API 导出文件(GPSystem.java 和 JavaDoc)中的注释与 GP 2.1.1 中的相同。

    因此,如果根据规范实现此方法,它仍应拒绝将应用程序生命周期状态设置为 APPLICATION_LOCKED

    更新 (2016-05-20): 我在 NXP J3D081 (JCOP v2.4.2 R2) 卡上测试了这个(声称符合GP 2.2)。不幸的是,设置高位或清除任何低 3 位的值会失败。

    不过,还有方法GPRegistryEntry.setState()。此方法的文档指出:

    • A transition request to Life Cycle state other than APPLICATION_LOCKED and APPLICATION_UNLOCKED shall be accepted only if the invoking Application corresponds to this GPRegistryEntry;
    • An Application shall be able to lock and shall not be able to unlock itself;

    因此,看看以下是否适用于使用 setCardContentState() 失败的同一张卡会很有趣:

    GPSystem.getRegistryEntry(null).setState(GPSystem.APPLICATION_LOCKED);
    

    更新 (2016-05-20): 我在 NXP J3D081 (JCOP v2.4.2 R2) 卡上测试了这个(声称符合GP 2.2)。不幸的是,这也失败了。顺便提一句。如果 nullJCSystem.getAID() 用作 getRegistryEntry().

    的参数,似乎没有什么区别

    更新 (2016-06-14): 根据 Paul Bastian,恩智浦代表已确认应用程序无法将自己设置为锁定状态在 JCOP v2.4.x 卡片上。

    更新(2016-06-06): 我在英飞凌 SLE97CNFX 卡上测试了这个(据称符合 GP 2.2.1)和有效。我可以使用 APPLICATION_LOCKED (0x80) 成功地将状态设置为锁定。然后将状态设置为 previous_state | 0x80。尝试设置设置了高位的其他状态值(例如 0x8F)不起作用(正如我所料)。

  3. 在 GP 2.2.1 中,方法 GPSystem.setCardContentState() 的文档已(再次)更改。更改说明清楚地表明该方法已更新为现在允许应用程序锁定自身(导出文件版本 1.5。映射到 GP 2.2.1):

    • export file version 1.5: this method now allows the application associated with the current applet context to lock itself.

    方法定义更改为:

    This method allows the application associated with the current applet context to change its state to an application specific life cycle state or to lock itself. An application cannot unlock itself using this method.

    传递给方法的状态参数的值范围现在明确包括 APPLICATION_LOCKED:

    的值

    bState - an application specific life cycle state (0x07 to 0x7F with 3 low order bits set), or APPLICATION_LOCKED (0x80).

    因此,实现 GP 2.2.1 或更高版本的卡最终应该允许应用程序使用方法 GPSystem.setCardContentState().

    将自己的生命周期状态更改为锁定

    更新(2016-06-06): 我在英飞凌 SLE97CNFX 卡上测试了这个(据称符合 GP 2.2.1(或是 2.3 吗?))并且有效。我可以使用 APPLICATION_LOCKED (0x80) 成功地将状态设置为锁定。然后将状态设置为 previous_state | 0x80。尝试设置设置了高位的其他状态值(例如 0x8F)不起作用(正如我所料)。

另一种解决方案

在无法将应用程序生命周期设置为状态 APPLICATION_LOCKED 的情况下,您可以使用特定于应用程序的生命周期状态来解决问题:

public class LockableApplet extends Applet {

    [... applet installation / instantiation code ...]

    private static final byte APPLICATION_STATE_UNLOCKED = (byte)0x07;
    private static final byte APPLICATION_STATE_LOCKED = (byte)0x7F;

    public boolean select() {
        if (GPSystem.getCardContentState() == APPLICATION_STATE_LOCKED) {
            return false;
        }

        return true;
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }

        if (GPSystem.getCardContentState() == APPLICATION_STATE_LOCKED) {
            ISOException.throwIt(ISO7816.SW_SECURITY_STATUS_NOT_SATISFIED);
        }

        [... applet logic code ...]

    }   
}

检测到应导致您的应用程序被锁定的问题后,您可以通过以下调用锁定小程序:

GPSystem.setCardContentState(APPLICATION_STATE_LOCKED);

您稍后可以通过安全域使用 SET STATUS 命令再次解锁应用程序。

(买家注意:看来这种方式根本行不通——见评论)

(在 GlobalPlatform 卡规范 2.2.1 的上下文中)

您必须遵守图 5-2 中描述的应用程序生命周期状态规则(标有“5”的箭头适用于此处)。

正确的方法应该是:

GPSystem.setCardContentState((byte)(GPSystem.getCardContentState() | GPSystem.APPLICATION_LOCKED));

GPSystem.getRegistryEntry(JCSystem.getAID()).setState((byte)(GPSystem.getCardCo‌​ntentState() | GPSystem.APPLICATION_LOCKED))

0x80 应用程序的生命周期状态无效。请参阅 table 11-4(至少必须设置 b1b2 位,b3 位可能也是如此)。

编辑>

(我承认写这个答案完全是基于 OPEN 保持实体被锁定的原始状态这一事实的记忆)

我对此很好奇,所以我使用以下小程序(摘录)做了一些测试:

public void process(APDU apdu) {
    byte[] buffer = apdu.getBuffer();

    if(selectingApplet()) {
        return;
    }

    short claIns = Util.getShort(buffer, ISO7816.OFFSET_CLA);
    switch(claIns) {
        case (short) 0x8007:
            buffer[0]=GPSystem.getCardContentState();
            if(buffer[0]==buffer[ISO7816.OFFSET_P1]) {
                if(GPSystem.setCardContentState(buffer[ISO7816.OFFSET_P2])) {
                    buffer[1]=0x01;
                } else {
                    buffer[1]=0x00;
                }
            } else {
                buffer[1]=(byte)0xFF;
            }
            buffer[2]=GPSystem.getCardContentState();
            apdu.setOutgoingAndSend((short)0, (short)3);
            return;
        default: {
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
            return;
        }
    }
}

以及以下 APDU:

8007070F03 // To test transition into Application Specific State
80070F8F03 // To test my theory
80070F8003 // To test the GPSystem.APPLICATION_LOCKED constant directly

我的一组卡片(Gemalto、Morpho、JCOP——不幸的是它们都是 GP 2.1.1)的结果符合 Michael Roland 的出色回答和 GP 规范——应用程序试图阻止自己是拒绝了。

收到所有 GP 2.1.1 卡的响应 APDU:

8007070F03 -> 07010F9000 // Succeeded in transition from `07` to `0F`
80070F8F03 -> 0F000F9000 // Failed transition from `0F` to `8F`
80070F8003 -> 0F000F9000 // Failed transition from `0F` to `80`

请注意:This tool 在解析卡识别数据时对于确定实现的 GP 版本非常有用。

GP 核心规范本身不符合要求。产品符合 GP 配置。 GP配置不是免费的。 JCOP 2.4.x 产品符合 GP 2.2.x 'Mapping Guidelines of Existing GP 2.1.1 Implementation on v2.2.1' 配置。顾名思义,此配置用于向后兼容映射。基本上 JCOP 2.4.x 产品只是 GP 2.1.1 兼容产品(具有 GP 2.2.x 的一些功能)。小程序的全局锁定权限是可选的。