通过 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 版本。那么可能有些方法不支持导致加载失败