通过 OTA 加载 USIM 卡中的方法组件失败 SMS-DELIVER
LOAD fails for Method component in USIM card via OTA SMS-DELIVER
我需要将一个简单的小程序加载到我的 USIM 卡中。身份验证没问题。
我正在通过智能卡使用 OTA SMS-DELIVER 安装 CAP 文件。
使用这些:
我在 Header.cap 中找不到解释“C482XXYY”的正确规范,应该如何计算,
下面的这个答案帮助我导航,但是对于 Method.cap 加载 - 它仍然失败
https://community.oracle.com/tech/developers/discussion/1753814/globalpaltform-load-command-data-field
我写了这个加载程序:
https://pastebin.com/pSXeDYyS
CAP 文件中的每个组件都在带有 LOAD 指令的单独 APDU 中。
使用 DATA=00,SW=9000 成功加载前 5 个,但是在 Method 上加载失败。
从我的脚本中可以看出,我已将目录字段中的描述符大小固定为 0000。
在 Header C482xxyy 中,正确计算了 xxyy,它是
目录中的所有大小字段,例如
016a = 0011+001f+000c+001e+0042+0018+006d+0032+0017+0000+0000
问题:无法弄清楚方法 LOAD 失败的原因?通过检查 GP 2.1 规范,它没有
帮助我理解问题。连接组件到 < 255 块大小也失败,
只有单独的组件适用于 LOAD。
[+] Install for load
>> 80e602001207d07002ca449001000006ef04c60201850000c0000000
<< 009000
[+] LOAD - Header
>> 80e8000018c482016a010011decaffed010204000107d07002ca44900100c0000000
<< 009000
[+] LOAD - Directory
>> 80e800012202001f0011001f000c001e00420018006d003200170000000000040002002202010000c0000000
<< 009000
[+] LOAD - Import
>> 80e800022104001e02000107a0000000620101060210a0000000090003ffffffff891071000200c0000000
<< 009000
[+] LOAD - Applet
>> 80e800030f03000c0108d07002ca44900101002000c0000000
<< 009000
[+] LOAD - Class
>> 80e800041b06001843800301ff0007020000002f00398002008101010881000000c0000000
<< 009000
[!!!!] LOAD - Method (FAILED)
>> 80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000
<< 9000 (should be 009000)
[-] LOAD - StaticField (FAILED due to last failed LOAD)
>> 80e80006350800.....
<< 6A86 (because prev. p2=05 is not loaded)
小程序源代码:
package com.github.mrlnc.HelloSTK2;
import javacard.framework.*;
import sim.toolkit.*;
/*
Originally from: https://git.osmocom.org/sim/hello-stk/tree/hello-stk/src/org/toorcamp/HelloSTK/HelloSTK.java
*/
public class HelloSTK2 extends Applet implements ToolkitInterface, ToolkitConstants {
// DON'T DECLARE USELESS INSTANCE VARIABLES! They get saved to the EEPROM,
// which has a limited number of write cycles.
private byte helloMenuItem;
private HelloSTK2() {
// This is the interface to the STK applet registry (which is separate
// from the JavaCard applet registry!)
ToolkitRegistry reg = ToolkitRegistry.getEntry();
byte[] menuItemText = new byte[] { 'H', 'e', 'l', 'l', 'o'};
// Define the applet Menu Entry
helloMenuItem = reg.initMenuEntry(menuItemText, (short)0, (short)menuItemText.length,
PRO_CMD_SELECT_ITEM, false, (byte)0, (short)0);
}
// This method is called by the card when the applet is installed. You must
// instantiate your applet and register it here.
public static void install(byte[] bArray, short bOffset, byte bLength) {
HelloSTK2 applet = new HelloSTK2();
applet.register();
}
// This processes APDUs sent directly to the applet. For STK applets, this
// interface isn't really used.
public void process(APDU arg0) throws ISOException {
// ignore the applet select command dispached to the process
if (selectingApplet())
return;
}
// This processes STK events.
public void processToolkit(byte event) throws ToolkitException {
EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
if (event == EVENT_MENU_SELECTION) {
byte selectedItemId = envHdlr.getItemIdentifier();
if (selectedItemId == helloMenuItem) {
byte[] welcomeMsg = new byte[] { 'W', 'e', 'l', 'c', 'o', 'm', 'e' };
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
proHdlr.initDisplayText((byte)0, DCS_8_BIT_DATA, welcomeMsg, (short)0,
(short)(welcomeMsg.length));
proHdlr.send();
}
}
}
}
更新:
按顺序排列的原始 CAP 字节(Header、目录、导入、Applet、Class、方法、静态字段、ConstantPool、RefLocation、描述符)
['01000fdecaffed010204000105d07002ca44',
'02001f000f001f000c00280036001800aa000a001200000003000000000000030100', '04002803030107a0000000620101060210a0000000090003ffffffff8910710002000107a0000000620001',
'03000c0108d07002ca449001010039',
'06001843800301ff00070200000048005280020081010108810000', '0700aa000912188c00038d00012c08900b3d031048383d041065383d05106c383d06106c383d07106f382d18191a031a9210240303038b000288007a02318f00043d8c00052e1b8b00067a0120188b000760037a7a06248d00082d1d10076b4e1a8b0009321fae006b441007900b3d031057383d041065383d05106c383d061063383d07106f383d08106d383d100610653828048d000a2805150503071504031504928b000b15058b000c3b7a',
'08000a00000000000000000000', '050036000d02000000068109000381090b0680030001000000060000010380030103800303068108000381080c06810a0003810a1303810a16',
'0900120002372d000c05032c08040507090a330f05',
'0b0003000000']
更新 2:
我通过 ENVELOPE 命令发送。使用 KIC1、KID1 加密。它们使用 RFM 和一些 RAM 命令,如 GET STATUS 等。这是 INSTALL(用于加载)命令的代码:
def install_for_load(self, caps, exe_aid):
# 11.5.2.3.7 INSTALL Command Parameters, page 174
cap_data = "".join(caps)
apdu = "".join([
"80", # CLS
"e6", # INSTR
"02", # p1 0b000000_1_0 ; for load
"00", # p2
"%02x", # p3 ; Lc
])
# sec_aid = "a000000003000000" # GP211 Security Domain AID
data = "".join([
"%02x" % int(len(exe_aid)/2),
exe_aid,
"00", # security domain aid (same domain)
# "%02x" % int(len(sec_aid)/2), # security domain aid (same domain)
# sec_aid,
"00", # load file data block hash
"%02x", # load params length
])
load_params_tl = "".join([
"ef", # Load Parameters: System Specific Parameters tag
"%02x", # length of load_params
])
load_params_v = "".join([
"c6", # Non-volatile code Minimum Memory requirement. load params (mandatory)
"02", # length
"%04x" % int(len(cap_data)/2), # code size HelloSTK2.cap size.
# "c7", # volatile data minimum memory required (ram) - optional
# "02", # length
# "00ff",
# "c8", # non-volatile data minimum memory required - optional
# "02",
# "00ff"
])
load_params = load_params_tl % int(len(load_params_v)/2) + load_params_v
data = data % int(len(load_params)/2) + load_params
data += "00" # load token
apdu = apdu % int(len(data)/2) + data
apdu += "00c0000000" # C-MAC + Le
return apdu
你可以看看GlobalPlatform project loader。
它是 LPGL,因此无法在其他非 LGPL 项目中直接将其用作 C 代码。
[!!!!] LOAD - Method (FAILED)
80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000
<< 9000(应该是009000)
我假设日志是在为 SCP80 准备命令之前未包装的日志。 OTA 中的 9000 响应通常意味着“我收到了命令,但我不知道如何处理它”。这个 9000 命令来自卡上的 ENVELOPE 响应吗?您是否通过 SMPP 连接发送数据?
您可以尝试使用不带任何 STK 的简单 HelloWorld Applet 加载代码吗?可能是您的卡不支持您链接的版本,例如不同的 ETSI 版本。那么可能有些方法不支持导致加载失败
我需要将一个简单的小程序加载到我的 USIM 卡中。身份验证没问题。 我正在通过智能卡使用 OTA SMS-DELIVER 安装 CAP 文件。
使用这些:
我在 Header.cap 中找不到解释“C482XXYY”的正确规范,应该如何计算,
下面的这个答案帮助我导航,但是对于 Method.cap 加载 - 它仍然失败 https://community.oracle.com/tech/developers/discussion/1753814/globalpaltform-load-command-data-field
我写了这个加载程序: https://pastebin.com/pSXeDYyS
CAP 文件中的每个组件都在带有 LOAD 指令的单独 APDU 中。 使用 DATA=00,SW=9000 成功加载前 5 个,但是在 Method 上加载失败。 从我的脚本中可以看出,我已将目录字段中的描述符大小固定为 0000。 在 Header C482xxyy 中,正确计算了 xxyy,它是 目录中的所有大小字段,例如
016a = 0011+001f+000c+001e+0042+0018+006d+0032+0017+0000+0000
问题:无法弄清楚方法 LOAD 失败的原因?通过检查 GP 2.1 规范,它没有
帮助我理解问题。连接组件到 < 255 块大小也失败, 只有单独的组件适用于 LOAD。
[+] Install for load
>> 80e602001207d07002ca449001000006ef04c60201850000c0000000
<< 009000
[+] LOAD - Header
>> 80e8000018c482016a010011decaffed010204000107d07002ca44900100c0000000
<< 009000
[+] LOAD - Directory
>> 80e800012202001f0011001f000c001e00420018006d003200170000000000040002002202010000c0000000
<< 009000
[+] LOAD - Import
>> 80e800022104001e02000107a0000000620101060210a0000000090003ffffffff891071000200c0000000
<< 009000
[+] LOAD - Applet
>> 80e800030f03000c0108d07002ca44900101002000c0000000
<< 009000
[+] LOAD - Class
>> 80e800041b06001843800301ff0007020000002f00398002008101010881000000c0000000
<< 009000
[!!!!] LOAD - Method (FAILED)
>> 80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000
<< 9000 (should be 009000)
[-] LOAD - StaticField (FAILED due to last failed LOAD)
>> 80e80006350800.....
<< 6A86 (because prev. p2=05 is not loaded)
小程序源代码:
package com.github.mrlnc.HelloSTK2;
import javacard.framework.*;
import sim.toolkit.*;
/*
Originally from: https://git.osmocom.org/sim/hello-stk/tree/hello-stk/src/org/toorcamp/HelloSTK/HelloSTK.java
*/
public class HelloSTK2 extends Applet implements ToolkitInterface, ToolkitConstants {
// DON'T DECLARE USELESS INSTANCE VARIABLES! They get saved to the EEPROM,
// which has a limited number of write cycles.
private byte helloMenuItem;
private HelloSTK2() {
// This is the interface to the STK applet registry (which is separate
// from the JavaCard applet registry!)
ToolkitRegistry reg = ToolkitRegistry.getEntry();
byte[] menuItemText = new byte[] { 'H', 'e', 'l', 'l', 'o'};
// Define the applet Menu Entry
helloMenuItem = reg.initMenuEntry(menuItemText, (short)0, (short)menuItemText.length,
PRO_CMD_SELECT_ITEM, false, (byte)0, (short)0);
}
// This method is called by the card when the applet is installed. You must
// instantiate your applet and register it here.
public static void install(byte[] bArray, short bOffset, byte bLength) {
HelloSTK2 applet = new HelloSTK2();
applet.register();
}
// This processes APDUs sent directly to the applet. For STK applets, this
// interface isn't really used.
public void process(APDU arg0) throws ISOException {
// ignore the applet select command dispached to the process
if (selectingApplet())
return;
}
// This processes STK events.
public void processToolkit(byte event) throws ToolkitException {
EnvelopeHandler envHdlr = EnvelopeHandler.getTheHandler();
if (event == EVENT_MENU_SELECTION) {
byte selectedItemId = envHdlr.getItemIdentifier();
if (selectedItemId == helloMenuItem) {
byte[] welcomeMsg = new byte[] { 'W', 'e', 'l', 'c', 'o', 'm', 'e' };
ProactiveHandler proHdlr = ProactiveHandler.getTheHandler();
proHdlr.initDisplayText((byte)0, DCS_8_BIT_DATA, welcomeMsg, (short)0,
(short)(welcomeMsg.length));
proHdlr.send();
}
}
}
}
更新: 按顺序排列的原始 CAP 字节(Header、目录、导入、Applet、Class、方法、静态字段、ConstantPool、RefLocation、描述符)
['01000fdecaffed010204000105d07002ca44',
'02001f000f001f000c00280036001800aa000a001200000003000000000000030100', '04002803030107a0000000620101060210a0000000090003ffffffff8910710002000107a0000000620001',
'03000c0108d07002ca449001010039',
'06001843800301ff00070200000048005280020081010108810000', '0700aa000912188c00038d00012c08900b3d031048383d041065383d05106c383d06106c383d07106f382d18191a031a9210240303038b000288007a02318f00043d8c00052e1b8b00067a0120188b000760037a7a06248d00082d1d10076b4e1a8b0009321fae006b441007900b3d031057383d041065383d05106c383d061063383d07106f383d08106d383d100610653828048d000a2805150503071504031504928b000b15058b000c3b7a',
'08000a00000000000000000000', '050036000d02000000068109000381090b0680030001000000060000010380030103800303068108000381080c06810a0003810a1303810a16',
'0900120002372d000c05032c08040507090a330f05',
'0b0003000000']
更新 2: 我通过 ENVELOPE 命令发送。使用 KIC1、KID1 加密。它们使用 RFM 和一些 RAM 命令,如 GET STATUS 等。这是 INSTALL(用于加载)命令的代码:
def install_for_load(self, caps, exe_aid):
# 11.5.2.3.7 INSTALL Command Parameters, page 174
cap_data = "".join(caps)
apdu = "".join([
"80", # CLS
"e6", # INSTR
"02", # p1 0b000000_1_0 ; for load
"00", # p2
"%02x", # p3 ; Lc
])
# sec_aid = "a000000003000000" # GP211 Security Domain AID
data = "".join([
"%02x" % int(len(exe_aid)/2),
exe_aid,
"00", # security domain aid (same domain)
# "%02x" % int(len(sec_aid)/2), # security domain aid (same domain)
# sec_aid,
"00", # load file data block hash
"%02x", # load params length
])
load_params_tl = "".join([
"ef", # Load Parameters: System Specific Parameters tag
"%02x", # length of load_params
])
load_params_v = "".join([
"c6", # Non-volatile code Minimum Memory requirement. load params (mandatory)
"02", # length
"%04x" % int(len(cap_data)/2), # code size HelloSTK2.cap size.
# "c7", # volatile data minimum memory required (ram) - optional
# "02", # length
# "00ff",
# "c8", # non-volatile data minimum memory required - optional
# "02",
# "00ff"
])
load_params = load_params_tl % int(len(load_params_v)/2) + load_params_v
data = data % int(len(load_params)/2) + load_params
data += "00" # load token
apdu = apdu % int(len(data)/2) + data
apdu += "00c0000000" # C-MAC + Le
return apdu
你可以看看GlobalPlatform project loader。 它是 LPGL,因此无法在其他非 LGPL 项目中直接将其用作 C 代码。
[!!!!] LOAD - Method (FAILED)
80e800057007006d000911188c00048d00012c18197b0002037b00029210240303038b000388007a02318f00053d8c00062e1b8b00077a0120188b000860037a7a02228d00092d1d10076b101a8b000a321fae006b06188c000b7a06118d000c2c1903077b000d037b000d928b000e198b000f3b7a00c0000000
<< 9000(应该是009000)
我假设日志是在为 SCP80 准备命令之前未包装的日志。 OTA 中的 9000 响应通常意味着“我收到了命令,但我不知道如何处理它”。这个 9000 命令来自卡上的 ENVELOPE 响应吗?您是否通过 SMPP 连接发送数据?
您可以尝试使用不带任何 STK 的简单 HelloWorld Applet 加载代码吗?可能是您的卡不支持您链接的版本,例如不同的 ETSI 版本。那么可能有些方法不支持导致加载失败