作者归档:softsim

Java卡:编程准则和最佳实践

本文档旨在为Java Card开发人员提供经过验证的技术指南,重点放在applet的设计和优化上。我们希望这些指南将有助于面对Java Card开发的特殊性,即:
•卡资源极为有限。 EEPROM大小通常为32-64Kb,用于保存卡片发行后小应用程序(Applet)和应用程序数据。 RAM大小通常约为4KB,并且必须容纳应用程序栈,瞬态数据和各种运行时缓冲区。你负担不起任何浪费
•CPU功率非常有限。另外,Java Card applet比本机代码慢得多,因此您不想执行不必要的操作
•即使您的小程序在平台上运行良好,您仍然希望节省尽可能多的资源,以便它也可以在更多受限的平台上运行(较少的堆栈,较小的事务缓冲区等)。还记得吗?编写一次,随处运行;-)

Applet设计

Java Card不是Java。花式的面向对象设计会导致小程序变胖和变慢,这是因为:
•CAP文件中的类/接口开销
•运行时的虚拟方法开销

下面介绍的设计准则是基于我们自己的经验。和往常一样,当然还有改进的余地,但是遵循它们肯定可以防止大量常见(以及一些不是那么常见)的错误。

高级设计

通常在设计时会犯下悲剧性的错误。如果发生这种情况,那简直是只有完整的重新设计才会拯救你……为避免这种情况,请非常仔细地阅读本节并确保您遵循这些准则。从合理的面向对象设计开始。这将帮助您了解applet的工作原理,尤其是当applet具有复杂功能(PKCS,EMV等)时。将问题分解为较小的对象仍然是找出问题的最佳方法。不过,您必须避免以下陷阱:

•类扩散:一切都不是对象。因此请控制 类/抽象类/接口 的数量
•实用工具 类/接口:将这些方法移至实际实例化的类,而不是定义仅包含静态方法的类。对于仅包含常量的接口,也是如此:将常量移至实际类
•较深的类层次结构:类树越深,继承方法的调用就越昂贵。继承还意味着您将嵌套地调用构造函数,这使栈(stack)处于危险之中。简而言之,继承的必要性必须明确:如果没有必要性,就不要继承。
•设计模式:模式在规范中看起来很棒,但是其实现意味着很多抽象类,接口和深层树
•系统化的get/set方法:同样,这在UML图表中看起来不错,但是对于Java Card应用程序来说,它的负担却很大。增加成员可见性将使您可以删除它们,从而减小代码大小并提高执行速度
•程序包扩散:实际上,除非一个程序包将由另一个applet使用,否则实际上无需将应用程序分解为程序包。看起来更好,但是跨包方法调用有一定的成本:如果不是必要,则避免使用它
•C样式错误处理:请勿在您的方法中使用C样式错误处理,即检查返回代码以查看是否发生了错误。这会产生效率低下的代码(更不用说难看的代码了),因为即使没有发生错误,您也会继续运行与错误相关的代码。您的设计必须使用Java异常:错误仅在发生后才需要处理!

设计完成后,您的应用程序应包括一个或两个软件包,一到十个类和不超过三个层级(level )的类。任何较大的问题都需要进行彻底调查和论证。一旦实现并测试了applet,您就可以确信已正确理解并实现了功能要求。在这个阶段,您可能会开始打破面向对象(OO)设计并进行优化以减小代码大小和/或提高性能。

数据存储

您的设计必须考虑以下约束:

•快速写入RAM
•写入EEPROM的速度非常慢
•分配对象甚至更糟
•垃圾收集不是Java Card 2.1.1的一部分

内存分配

关键的规则是, 在安装时创建所有实例数据,即在javacard.framework.Applet.install()调用的applet构造函数中创建:使用新的临时数组分配,所有显式初始化的对象构造。这样,在applet的使用期间就不会面临内存不足的风险:在安装时所有内容都已保留。此外,内存分配不会降低applet命令的速度。

内存写入

对象存储在EEPROM中。这意味着每次执行
myObject.myByte =(byte)5
时,您都在写入EEPROM。必须尽可能避免这种情况。显然,您需要存储持久性数据,但是在许多情况下,您只需要存储临时数据(中间结果,发送回终端的数据等)。这是APDU缓冲区派上用场的地方!毕竟,它是一个“普通”字节数组,因此请尽可能多地使用它。

安全

如果将小程序提交给安全评估(通用标准等),则可以采取一些特定步骤:
•将所有安全功能(密钥/ PIN处理等)隔离在单独的类中。这样,只需要记录和评估这些类
•尽可能使用Java Card API(PIN,密钥等)。如果平台提供了您需要的安全服务,则applet实施这些服务是无用且不安全的

固定大小的PIN

使用固定大小的PIN,即,将PIN长度存储在PIN本身中,并填充其余字节。这有助于防止定时攻击,还简化了PIN处理。

反DFA RSA签名

在将结果发送到终端之前,请验证所有RSA签名。这将减慢签名操作的速度,但可以防止DFA攻击。

其他需要注意的事项

注意平台错误:它们可能会对您的设计产生影响。
如果小程序必须可互操作,请不要使用任何专有的API。


小程序优化

本节列出了许多实际的(即经过测试和验证的)优化。在应用它们之前,请记住优化的黄金法则:
•专注于最常运行的代码:在这里您将有所作为
•您通常必须在大小和速度之间进行选择
•每次优化通过后运行基准测试
•每次优化通过后运行验证测试

减少代码大小

避免复杂的面向对象设计

如果您阅读了上一节,这应该很明显。简而言之:
•使类/接口的数量最少。组合“相似”类并在实例化时使用构造函数参数对其进行区分
•尽量减少方法数量。尤其要摆脱所有get/set方法:提高数据成员的可见性(例如,从私有到可见的包),以减少get/set方法的数量。由于您保存了方法调用,因此这也将提高性能

权衡:封装被削弱。如果您控制程序包中的所有类,那么这不成问题。

删除无效代码

查找未使用的变量和代码。听起来很明显,但您会感到惊讶…
另外,请尝试使初始化/个性化设置尽可能短。它只需要运行一次,因此非常重。也许个性化系统可以执行更复杂的操作?

分解重复代码

查找所有冗余代码并将其分解。如果这意味着分解类并将其替换为私有/静态方法,请执行此操作。

权衡:如果经常调用该代码,则可能会减慢速度。

限制参数的个数

对于虚拟方法,尝试使用不超过3个参数,对于静态方法,尝试使用不超过4个参数。这样,编译器将使用数字或字节码快捷方式,这有助于减小代码大小:aload_x(1字节)而不是aload x(2字节),依此类推。
权衡:如果这意味着从对象实例保存/读取数据,则速度会明显下降。

减少EEPROM消耗

回收所有对象。

Java Card标准不需要垃圾收集。因此,除非您坚持要浪费内存,否则必须跟踪旧对象并重用它们。切记:Java Card虚拟机“永远”运行,因此,如果某个对象变得不可访问,则其内存将”永远”消失(或者至少在删除小程序之前)。即使您的平台提供了专有的垃圾回收,您也最好重用对象。分配新对象很慢,垃圾回收也很慢。你明白了…

仔细分配数组

操作系统以32字节的块(称为clusters)分配内存。一个cluster无法在两个对象之间共享,因此任何对象都将至少吞噬一个群集,无论它多么小。此外,虚拟机将标头附加到所有对象:
•6个字节用于“普通”对象
•8个字节用于基本类型数组
•对象数组12个字节

这意味着您不必创建多个相同类型的小数组,而应将它们组合成一个数组,并使用固定偏移量访问后者。

折衷方案:代码复杂度略有增加。


减少内存消耗

重用局部变量

与其在需要时分配新的局部变量,不如尝试重用先前声明的局部变量。
折衷:滥用此技术会产生无法读取的代码。

仔细分配临时数组 (重复了,上面提到过)

与其创建多个相同类型的小型临时数组,不如将它们组合成一个数组并使用固定偏移量对其进行访问。
折衷方案:代码复杂度略有增加。

限制参数的个数 ( 重复了)

对于虚拟方法,尝试使用不超过3个参数,对于静态方法,尝试使用不超过4个参数。这样,编译器将使用短字节码指令,这有助于减小代码大小:aload_x(1字节)而不是aload x(2字节),依此类推。
权衡:如果这意味着从对象实例保存/读取数据,则速度会明显下降。

避免深层嵌套的方法调用

危险的嵌套调用通常发生在深层类树(基类方法调用)和递归上。后者产生了花哨且紧凑的代码,但它也往往会很快粉碎stack……三遍检查最终条件,并确保测试实际上触发了最坏的情况。
权衡:可能增加代码大小。
当心开关/案例结构中的局部变量

不必在每种情况下分配新的局部变量,而仅在切换之前声明一个。


提高执行速度

开启编译器优化

如果您的编译器提供了优化标志,请使用它们(对于Javac,为-O)。
权衡:可能的代码大小增加。

避免复杂的面向对象设计

有关详细信息,请参见Applet设计和Applet优化。
权衡:封装被削弱。如果您控制程序包中的所有类,那么这不成问题。

支持静态,私有和最终方法

所有公共/受保护的Java方法都是隐式虚拟方法(与C++不同,在C++中,必须将它们声明为虚拟)。这意味着始终进行动态绑定,即,虚拟机在调用公共/受保护的方法之前必须始终确定对象的实际类型。这种查找成本很高,尤其是如果要调用的方法是从基类继承的。这是不欢迎使用深层树的主要原因。

要解决此问题:
1.支持静态方法,因为它们不受动态绑定的约束,这使得它们调用起来更快
2.如果方法不是静态的,请尝试将其设为私有。私有方法不能在派生类中被覆盖,因此虚拟机更容易找到它们
3.如果某个方法不能私有,请尽快将其定型。这将有助于虚拟机

使用本机API

只要平台提供本机代码来执行操作,就使用它!本机方法比您能想到的任何聪明的Java代码要快得多。
尤其是:
•使用
Util.arrayFillNonAtomic()
Util.arrayCopy()
Util.arrayCopyNonAtomic()
初始化或修改数组

•Util.arrayFillNonAtomic()对于在加密操作期间填充缓冲区特别有用
•使用Util.getShort(),Util.setShort()处理byte [] / short转换


避免不必要的初始化

Java Card虚拟机保证将新分配的数据成员和局部变量设置为0 / false / null。

不要将异常用于流程控制

使用异常进行流控制从来都不是一个好主意,Java Card也不例外。异常处理非常慢,除异常处理外,不应将其用于其他任何用途。换句话说,如果您正在考虑使用throw模拟goto,只需加倍努力,寻找更好的解决方案。

使用RAM缓冲区

写入EEPROM的速度大约比写入RAM的速度慢1,000倍,因此,执行的次数越少越好。您可以使用APDU缓冲区或瞬态数组来存储会话数据和临时结果。这对于中间解密解密操作特别有效,并且也更安全。

清理循环

访问实例成员比访问局部变量要昂贵。如果必须执行重复访问,则在第一次访问时将实例成员存储在本地变量中,然后仅使用该本地变量。数组访问(array [i],array.length)和方法调用也是如此。这将大大提高性能。

使用逻辑表达式的求值顺序

逻辑表达式(测试等)从左到右评估。如果条件失败,则不会评估其余测试。如果其中一种情况几乎总是错误,请首先进行检查:您将避免执行不必要的代码。
尽可能经常使用返回值

许多API的返回值都是有意义的:使用它可以节省不必要的操作。

谨慎使用事务(transactions)

由于许多本机API已经进行事务写操作,因此请确保必须在applet中明确使用事务。特别是,如果您不需要交易,请不要使用Util.arrayCopy(),请改用Util.arrayCopyNonAtomic()。

同时检查APDU的类(CLA)和指令(INS)

当使用OP Secure Messaging时,这特别有效,因为所有安全命令将使用两个不同的类(CLA)值。

同时检查P1和P2

尽可能同时检查P1和P2。您的代码将更快,更紧凑。

块加密的几种padding方案

PKCS5Padding和PKCS7Padding唯一不同的地方在于块的大小

在PKCS5Padding中, 明确定义Block的大小是8个字节
而在PKCS7Padding定义中,块的大小是不确定的,可以在1-255字节之间

PKCS5, PKCS7 有如下相同的特点:
1)填充的字节都是一个相同的字节
2)该字节的值,就是要填充的字节的个数

如果要填充8个字节,那么填充的字节的值就是0×8;
要填充7个字节,那么填入的值就是0×7;

如果只填充1个字节,那么填入的值就是0×1;

正是这种即使恰好是8个字节也需要再填充字节的规定,可以让解密的数据很确定无误的移除多余的字节。
因为解密恢复的明文的最后一个字节,就能告诉你 存在多少个填充字节

举例:假定块长度为 8,数据长度为 9,

数据: FF FF FF FF FF FF FF FF FF

PKCS7 填充后: FF FF FF FF FF FF FF FF FF 07 07 07 07 07 07 07

一种python 实现

    pad_len = 8 - (len(data) % self.block_size)
    if _pythonMajorVersion < 3:
        data += pad_len * chr(pad_len)
    else:
        data += bytes([pad_len] * pad_len)



    if _pythonMajorVersion < 3:
        pad_len = ord(data[-1])
    else:
        pad_len = data[-1]
    data = data[:-pad_len]

另外一种实现

BS = 8
pad = lambda s: s +  (BS - len(s) % BS) * bytes([BS - len(s) % BS])
unpad = lambda s : s[0:-(s[-1])]
#############
pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS)
unpad = lambda s : s[0:-ord(s[-1])]

PKCS5 pad/unpad的 PHP实现

function pkcs5_pad ($text, $blocksize)
{
    $pad = $blocksize - (strlen($text) % $blocksize);
    return $text . str_repeat(chr($pad), $pad);
}

function pkcs5_unpad($text)
{
    $pad = ord($text{strlen($text)-1});

    if ($pad > strlen($text))
       return false;
    if (strspn($text, chr($pad), strlen($text) - $pad) != $pad)
       return false;
    return substr($text, 0, -1 * $pad);
}

还有一点需要说明的是:
块大小是 加密方案(加密算法)的一种固有属性,对于AES来说,块大小是128位(16字节)

填充是为了 让加密的输入, 匹配 输入块(一般是块加密算法中矩阵)的长度, 而不是 Key的长度。 输入块是 64位, Key可以是128位,256位。

严格来讲, PKCS5Padding 是不能用于 AES的,因为
PKCS5Padding的块大小,是8字节
AES的块大小,是16字节。
如果有人说 AES算法,使用 PKCS5Padding填充时, 实践上,他使用的是 PKCS7Padding

SSL3Padding跟PKCS5Padding只是稍有不同:PKCS5 Padding填充的值是 要填充的字节数, 而SSL 3 Padding 是要填充的字节数 减去1

所以,很容易得到 ssl3padding的php实现

function ssl3_pad ($text, $blocksize)
{
     $pad = $blocksize - (strlen($text) % $blocksize);
     return $text . str_repeat(chr($pad-1), $pad);
}


function ssl3_unpad($text)
{
     $text_len = strlen($text);
     $pad = ord($text{$text_len-1});
     $pad_len = $pad + 1;

     if ($pad_len > $text_len) {
         return false;
     }

     if (strspn($text, chr($pad), $text_len - $pad_len) != $pad_len) {
          return false;
     }

     return substr($text, 0, -1 * $pad_len);
}

RSA

有OpenSSL生成RSA私钥 (其实应该叫 keypair,里面也有公钥的modulus)

openssl genrsa -out private.pem 512

由私钥 生成 公钥

openssl rsa -in private.pem  -pubout -out public.pem

默认格式是pem,所以不必要加 -outform PEM 选项>

从公钥中查看 modulus

openssl rsa -pubin -in public.pem -modulus -noout

列出modulus和Exponent

openssl rsa -pubin -in public.pem -text -noout

exponent一般为 65537 (0x10001)<

查看密钥对信息

openssl rsa -in private.pem -text -noout

privateExponent是由 prime1 prime2 exponent1 exponent2 生成,都应该保密

EF_SMS短消息文件

在UST(USIM Service Table)中 10号服务可用,那么EF_SMS文件应该存在。 文件标识 6F3C 结构: 线性定长 记录长度: 176字节 每条记录的第1个字节 状态 This data item commences with the TS-Service-Centre-Address as specified in TS 24.011 The bytes
immediately following the TS-Service-Centre-Address contain an appropriate short message TPDU as specified in
TS 23.040 [6], with identical coding and ordering of parameter

simtrace2使用记录

USB 转UART 连接 simtrace的debug接口(管脚1为 地, 4为发送, 5 为接收)

在PC上配置串口 (921600 8N1)
波特率 921600
8位数据位
无奇偶校验
停止位 1

重启板子(按 RESET 按钮, 或者 重新拔插 usb线缆)
在串口会看到输出


DFU模式(Device Firmware Upgrade)

firemware编译出 几种应用:
dfu: USB DFU 启动加载器,用来 升级其他应用的
ccid: USB CCID读卡器
cardem: 卡模拟,提供本地cos,以及远程sim卡功能
trace: 监听sim卡和设备间的通信
triple_play: 支持 ccid, cardem, trace三种功能,通过usb来配置。


应用可以放在设备的不同位置
flash
bootloader区域内(第1个16k区域保留为bootloader所用,dfu放这里)
ram (通过 jtag/swd 直接下载到ram)

iPhone debugserver lldb IDA调试

越狱后iPhone(iPad或iPod)

获取debugserver

  1. xcode新建一个singleview的简单项目,在iPhone设备上跑一遍。这样在设备的/Developer/usr/bin 下就会有debugserver
  2. 第二种方法,在 /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/DeviceSupport/ 里找到对应版本的DeveloperDiskImage.dmg, 从里面提取

添加权限

分离fat binary
lipo -thin armv7s ~/debugserver -output ~/debugserver_7s
lipo -thin arm64 ~/debugserver -output ~/debugserver_64  (iPhone 6之后是 64位的)

强制添加权限
codesign -s - --entitlements entitlements.xml -f debugserver_7s

查看权限
codesign -d --entitlements :-  debugserver_7s
codesign -d --entitlements -   debugserver_7s


权限文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.backboardd.debugapplications</key>
    <true/>
    <key>com.apple.backboardd.launchapplications</key>
    <true/>
    <key>com.apple.springboard.debugapplications</key>
    <true/>
    <key>run-unsigned-code</key>
    <true/>
    <key>get-task-allow</key>
    <true/>
    <key>task_for_pid-allow</key>
    <true/>
</dict>
</plist>

0000000

开启端口转发
./tcprelay.py -i 192.168.0.119 -b 8192 -t 22:2222 8341:8341

ssh -v root@192.168.0.119 -p 2222
附加到被调试进程

debugserver *:8341 --attach Preferences

开启lldb

(lldb) platform select remote-ios
(lldb) process connect connect://192.168.0.119:1234
(lldb) po [[UIApp keyWindow] recursiveDescription]

从ipsw中提取iPhone的动态连接库

  • 下载 ipsw
  • ipsw iphone XR 有完整iPhone XR软件恢复包的下载地址
    选择下载了 http://updates-http.cdn-apple.com/2018FallFCS/fullrestores/041-19415/F69DC39C-DEBF-11E8-BA95-89533F25C8D2/iPhone11,8_12.1_16B94_Restore.ipsw

    iOS 12.1版本

  • 从dmg中提取文件
  •  
    unzip iPhone11,8_12.1_16B94_Restore.ipsw
    dmg2img  048-32857-105.dmg system.img
    mount -t hfsplus system.img /mnt  (mount -o loop -t hfsplus system.img /mnt)
    

    如果没有dmg2img,需要先安装

    apt install dmg2img
    

    用 P7ZIP 也可提取

  • 提取 动态链接库
  • /System/Library/Caches/com.apple.dyld/dyld_shared_cache_arm64e

    https://github.com/macmade/dyld_cache_extract

    参考资料:
    https://iphonedevwiki.net/index.php/Dyld_shared_cache

    https://github.com/malus-security/iExtractor
    https://github.com/malus-security/iExtractor/tree/master/tools/dyld

    对于 iOS 10之后的img, 用的是APFS

    iOs开发命令行程序

    Virtualbox虚拟机导入 macOS Catalina Final Version by Geekrar的vmdk
    app store下载安装XCode

    xcrun --sdk iphoneos --find clang
    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang
    
    
    
    xcrun --sdk iphoneos --show-sdk-path
    /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk
    

    编译命令

    clang -arch armv7s -mios-version-min=7.0   imei.im -o imei \
    -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk  \
    -framework Foundation  -framework CoreTelephony
    

    签名

    codesign -s 'iPhone Develoer' ./path/to/binary
    

    Android CarrierConfigManager和CarrierService

    将运营商 ID 与 CarrierConfig 集成
    从 Android 10 开始,运营商配置支持将运营商 ID 用作密钥,从 CarrierService 获取运营商专属配置。

    将运营商 ID 与 CarrierConfig 集成具有以下优势:

    将每个运营商的所有 MCC/MNC 对整合到一个位置,从而移除重复或不一致的数据。
    为每个运营商创建规范标识符并消除歧义。

    允许使用单个 ID 标识移动虚拟网络运营商 (MVNO),而不是将配置作为移动网络运营商 (MNO) 的一部分。

    final CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);

    PersistableBundle b = configManager.getConfig();
    b.putBoolean(KEY_PREFER_2G_BOOL, false);

    b.putBoolean(KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL, true);

    v4.putBoolean(“prefer_2g_bool”, false);
    v4.putBoolean(“carrier_settings_enable_bool”, true);
    v4.putBoolean(“carrier_allow_turnoff_ims_bool”, false);
    v4.putBoolean(“carrier_wfc_ims_available_bool”, true);
    v4.putBoolean(“display_hd_audio_property_bool”, true);
    v4.putBoolean(“editable_enhanced_4g_lte_bool”, true);
    v4.putBoolean(“carrier_volte_available_bool”, true);
    v4.putBoolean(“carrier_volte_provisioning_required_bool”, false);
    v4.putBoolean(“carrier_volte_provisioned_bool”, true);
    v4.putBoolean(“carrier_vt_available_bool”, true);
    v4.putBoolean(“carrier_volte_tty_supported_bool”, false);
    v4.putBoolean(“hide_enhanced_4g_lte_bool”, false);
    v4.putBoolean(“carrier_wfc_supports_wifi_only_bool”, true);
    v4.putBoolean(“allow_adding_apns_bool”, true);
    v4.putBoolean(“apn_expand_bool”, true);
    v4.putBoolean(“carrier_ims_gba_required_bool”, false);
    v4.putBoolean(“require_entitlement_checks_bool”, false);
    v4.putBoolean(“hide_ims_apn_bool”, false);
    v4.putInt(“volte_replacement_rat_int”, 0);
    v4.putBoolean(“carrier_use_ims_first_for_emergency_bool”, true);
    v4.putBoolean(“auto_retry_enabled_bool”, true);
    v4.putBoolean(“world_phone_bool”, true);
    v4.putBoolean(“carrier_ut_provisioning_required_bool”, false);
    v4.putBoolean(“carrier_supports_ss_over_ut_bool”, false);
    v4.putBoolean(“carrier_volte_default_enabled_bool”, true);
    v4.putBoolean(“show_ims_registration_status_bool”, true);
    v4.putBoolean(“support_manage_ims_conference_call_bool”, true);
    v4.putBoolean(“support_ims_conference_call_bool”, true);
    v4.putBoolean(“support_video_conference_call_bool”, true);
    v4.putBoolean(“enhanced_4g_lte_on_by_default_bool”, true);
    v4.putBoolean(“editable_wfc_mode_bool”, true);
    v4.putBoolean(“emergency_sms_support_bool”, false);
    v4.putBoolean(“notify_handover_video_from_wifi_to_lte_bool”, true);
    v4.putBoolean(“notify_handover_video_from_lte_to_wifi_bool”, true);
    v4.putBoolean(“support_downgrade_vt_to_audio_bool”, true);
    v4.putInt(“carrier_default_wfc_ims_mode_int”, 1);
    v4.putInt(“carrier_default_wfc_ims_roaming_mode_int”, 1);
    v4.putBoolean(“carrier_default_wfc_ims_enabled_bool”, true);
    v4.putBoolean(“carrier_default_wfc_ims_roaming_enabled_bool”, true);
    v4.putBoolean(“carrier_promote_wfc_on_call_fail_bool”, true);
    v4.putBoolean(“carrier_volte_override_wfc_provisioning_bool”, true);
    v4.putBoolean(“allow_emergency_video_calls_bool”, false);
    v4.putBoolean(“editable_wfc_roaming_mode_bool”, true);
    v4.putBoolean(“carrier_config_applied_bool”, true);
    v4.putStringArray(“carrier_wlan_disallowed_apn_types_string_array”, new String[]{“”});
    v4.putStringArray(“carrier_wwan_disallowed_apn_types_string_array”, new String[]{“”});
    v4.putBoolean(“hide_preset_apn_details_bool”, false);
    v4.putStringArray(“read_only_apn_types_string_array”, new String[]{“”});
    v4.putStringArray(“read_only_apn_fields_string_array”, new String[]{“”});
    v4.putStringArray(“apn_settings_default_apn_types_string_array”, new String[]{“”});

    Toolkit Applet

    普通Java Card applet和Tookit applet的区别是,后者不直接处理APDU. 它只处理高层级的消息(事件)。

    另外,后者一个方法(java method)的执行,能够跨越多个APDU(Fetch, Terminal Resposne等主动命令)。

    并且,Tookiet applet 的触发,不会调用 select()方法,只会调用processTookit()

    EVENT_EVENT_DOWNLOAD_CALL_DISCONNECTED
    EVENT_EVENT_DOWNLOAD_LOCATION_STATUS
    EVENT_EVENT_DOWNLOAD_DATA_AVAILABLE
    EVENT_EVENT_DOWNLOAD_ACCESS_TECHNOLOGY_CHANGE
    EVENT_UNRECOGNIZED_ENVELOPE
    

    Java Card Tookit相关对象
    1. ProactiveHandler
    1) 在 Terminal Profile 命令被CAT运行环境处理之前, ProactiveHandler对象不可用。
    2) 如果 ProactiveHandler对象 可用,它将一直保持可用,直到processToolkit()方法结束
    3)ProactiveHandler不可用, 主动命令将被挂起
    4) 调用 proHdlr.init()方法,将会清除 主动命令的内容,然后再初始化

    1. EnvelopeHandler
      1) 在 processToolkit()方法的调用 到结束的过程中,如果 EnvelopeHandler 可用,将在整个过程中保持可用。
      2) 它的TLV列表,是用 ENVELOPE 命令的APDU里的简单TLV数据对象填充的,并且保持原有的顺序。