如何在更新 Java Card / GlobalPlatform 小程序时保留数据?

How to preserve data when updating Java Card / GlobalPlatform applet?

如何更新包含需要跨版本保留的数据的 Java 卡片小程序?据我所知,更新小程序是通过删除它然后安装新版本来完成的,这似乎也会删除与应用程序关联的任何持久数据。

作为一个具体的例子,假设我正在编写一个身份验证和加密小程序。该小程序的第 1 版将在安装时生成一个受硬件保护的密钥,并支持对消息进行签名,但不对它们进行加密。假设我想发布一个也支持加密的版本 2,并且可以使用版本 1 创建的密钥。我需要在版本 1 和 2 中做什么才能实现这一点?

除了纯 Java 卡片之外,我对使用 GlobalPlatform 机制的解决方案持开放态度。

您需要第二个小程序,它拥有 您希望在重新安装第一个小程序时保留的所有对象。我们称它们为 Storage 小程序和 Worker 小程序。

这意味着每次Worker applet 需要使用Storage applet 中的资源时,它都必须调用Shareable 接口。代码大小、代码可维护性和速度都会受到影响。我想不出在 Java Card 或 Global Platform 中执行此操作的另一种方法。

你不能。

在当前的 JavaCard 智能卡上不可能在更新应用程序包的同时保留与从该程序包实例化的任何小程序关联的数据。

一方面,这有一个相当简单的技术原因:

Applet 是 Java(卡片)对象,从应用程序包的代码库中的 classes 实例化。更新代码库会改变 classes 的结构,因此,已经实例化的小程序对象将不再匹配它们的 class 定义。因此,每当应用程序包中的小程序 class 发生变化时,这也意味着需要重新创建相应的小程序实例。

由于所有小程序数据本身都组织为 Java (Card) 对象(例如从用户定义的 classes、数组或原始类型字段实例化的对象,这些字段存储为小程序实例的直接或间接属性,小程序实例是其所有关联数据的根元素1。因此,删除并重新创建该根实例的必要性也意味着来自该根元素的数据被删除并重新初始化。

当然,这可以通过使小程序(以及存储小程序数据的所有子对象)可序列化来克服。这样,小应用程序可以在更新其代码库之前序列化到辅助存储区域。更新之后,可以通过对该数据进行反序列化来重新创建 applet 实例(及其对象层次结构)。但是,允许这样做会产生严重的安全隐患:任何管理实例都可以序列化(并且在最坏的情况下,提取)该数据。

这让我想到了第二个原因:在我看来(虽然我无法找到任何权威资源),Java 卡平台和智能卡的核心设计原则通常是防止提取敏感数据。

考虑以下示例:您有一个用于数字签名的小程序(例如 PIV、OpenPGP,或者只是您在问题中描述的小程序)。这个小程序的目的是将密钥安全地存储在智能卡芯片上(具有保护功能的专用硬件禁止物理提取密钥 material,即使攻击者获得了对物理 card/chip 的访问权限)。因此,小程序安全地在芯片上生成其私钥 material。然后可以使用秘密私钥进行签名(and/or 解密),但绝不允许它离开卡(因为这会导致 key/card 复制、密钥泄露给攻击者等。 )

现在想象一下,智能卡运行时环境允许对包括秘密私钥在内的小应用程序数据进行序列化。即使卡片不提供任何直接提取序列化数据的方法,只需考虑攻击者编写一个与您自己的小程序具有完全相同结构的新小程序,只是它提供了一种额外的方法来提取卡片 中的钥匙 material。攻击者现在创建一个应用程序包,表明它是对现有小程序的更新。进一步假设攻击者能够在卡上加载该应用程序包。现在攻击者能够更新您的小程序并破坏您最初的设计目标(使其无法从卡中提取密钥)。

覆盖现有 application/applet 的设计原则需要擦除现有小程序及其所有数据也(某种程度上)体现在 GlobalPlatform Card 规范和 Java Card Runtime 规范中:

GlobalPlatform 卡规范,版本 2.3,2015 年 10 月:

  • 当使用 INSTALL [for load] 命令加载任何应用程序包时,OPEN 需要

    "Check that the AID of the Load File is not already present in the GlobalPlatform Registry as an Executable Load File or Application."

  • 使用INSTALL [for install]命令安装小程序实例时,OPEN需要

    "Check that the Application AID [...] is not already present in the GlobalPlatform Registry as an Application or Executable Load File"

  • Java Card 3 平台,运行时环境规范,经典版,版本 3.0.4,2011 年 9 月:

    "The Java Card RE shall guarantee that an applet will not be deemed successfully installed in the following cases:

    • The applet package as identified by the package AID is already resident on the card.
    • The applet package contains an applet with the same Java Card platform name as that of another applet already resident on the card."

    此外,规范明确指出小程序删除必须删除小程序实例拥有的所有对象:

    "Applet instance deletion involves the removal of the applet object instance and the objects owned by the applet instance and associated Java Card RE structures."

然而,有时可能有充分的理由使应用程序的某些部分可更新而不擦除密钥等信息,并且有一些方法可以克服这个问题:

  • 芯片和卡 OS 制造商可能确实有办法为现有卡上的卡操作系统和运行时环境的某些功能打补丁。我无法通过此假设引用任何权威来源。
  • 如果您从一开始就将 patch-/upgradability 视为设计目标,那么您肯定能够将应用程序的某些部分设计为可以在不丢失相关数据的情况下升级的方式。您通常会将应用程序拆分为多个应用程序包,其中包含应用程序功能的不同部分(另请参阅 )。在最简单的形式中,一个小程序将包含和管理所有业务逻辑,而另一个小程序(它需要在一个单独的应用程序包中通过)负责存储敏感的持久数据。然后,您可以使用可共享接口从业务逻辑小程序访问存储在数据存储小程序中的数据。

    但是,您肯定希望仔细设计两个实例之间的接口。例如,您绝对不想直接传递秘密私钥。相反,您只想共享一个接口来对从业务逻辑小程序传递到数据存储小程序的数据执行 signing/decryption 操作。否则,将出现与上述相同的提取问题。


1) 这不一定总是正确的,因为数据可能(技术上)存储在静态字段中。在那种情况下,应用程序包将是这些元素的根。但是,这样做会对安全性产生严重影响,因为对静态字段的访问不受 applet isolation/the applet 防火墙的保护。比照。 Java Card 3 平台,运行时环境规范,经典版,版本 3.0.4,2011 年 9 月,节。 6.1.6:

"There is no runtime context check that can be performed when a class static field is accessed."

GlobalPlatform 修正案 H 卡可执行加载文件更新提供了针对此问题的解决方案。 (https://globalplatform.org/wp-content/uploads/2018/03/GPC_2.3_H_ELF_Upgrade_v1.1_PublicRelease.pdf)。不过不知道市面上有没有实现这个规范的产品。