Java Card Applet Development——环境搭建及基础概念


利用Eclipse Java Card Development Environment (EclipseJCDE)

windows platform

Edited by Hui — 2013.09.05


Java Card技术基本概念

Java Card是基于硬件与应用的要求所开发的技术,目的是使得Java技术能够再如同smart card芯片般大小的有限资源下执行,是能运行Java程序的智能卡。Java Card技术提供了一种开放式开发构架,现在的智能卡开发者可以开发出在多种智能卡上应用的程序,不同的应用程序可以在同一个卡片上运行。

从一个 applet 的编写、编译、转换、部署、使用的过程看,会是这样的与脚本相关的过程:

  1. javac – 编译 *.java
  2. converter – 生成 cap, jca, exp
  3. genscript – 由 cap 文件产生 scr 脚本
  4. apdutool – 利用 scr 脚本,将 applet 部署到卡(虚拟机的 eeprom文件)中
  5. apdutool – 利用虚拟机测试 applet

Java Card Development Tools

目前Oracle已经提供了所有开发Java Card Applet所需要的tools、libraries和一系列的APIs,最多使用的是2.2.1和2.2.2版本。

Note: 目前还不需要用到JCDK 3.x,因为还没有实体java卡支持其开发,而在没有可以实时部署和测试的环境的情况下,去学习这个版本试没有意义的。

JCDK中包含Converter、APDUTool、ScriptGen等重要的工具,还提供两个Java智能卡运行环境的仿真工具Cref和JCWDE,通过这两个工具,无需读/写器和智能卡卡片,开发人员可以在PC软件平台上模拟Java智能卡的运行,进行Applet的调试运行。


Java Card Applet Structure

几个比较重要的functions:

public static void install(byte[] bArray, short bOffset, byte bLength){}
public void process(APDU apdu){}
protected final void register(){}
//or protected final void register(byte[] bArray, short bOffset, byte bLength){} throws SystemException public boolean select(){}

  • install( ):构建了Applet子类的实例,JCRE将会最先调用这个;所有的初始化和分配内存的操作应该在这个里面实现;可以获取卡外实体传进来的一些应用初始化参数。
  • process( ):类似于正常java class的main,在安装后,APDU的执行将在这里实现。
  • register( ):applet用来在JCRE中注册该applet实例。
  • register(parameters):和register( )功能一样,增加了可以分配其特定的AID的功能。
  • select( ):JCRE一旦接收到SELECT[by name]命令时,将寻找命令中指示的AID对应的Applet,使之处于活跃状态,接收并处理接下来的APDU命令;在选择新的Applet前,JCRE先调用当前Applet的 deselect 方法;Applet可以拒绝被选择,此时 select 方法返回false;SELECT[by name]命令本身也将传递给applet处理,此时通过 selectingApplet 用以判断当前状态。

EclipseJCDE

开发环境搭建
  1. 在Sun/Oracle官网上下载 Java Platform 1.5(JDK) (http://www.oracle.com/technetwork/java/javase/downloads/index.html)
  2. 在Sun/Oracle官网上下载 Java Card Development Kit v2.2.2 (JCDK)(http://www.oracle.com/technetwork/java/javame/javacard/download/devkit/index.html),解压到 “java-card-dev-kit-root”,例如“C:\java_card_kit-2_2_2”
  3. 解压“java-card-dev-kit-root/java_card_kit-2_2_2/java_card_kit-2_2_2-rr-bin-windows-do.zip”到“java-card-dev-kit-home”,例如“C:\java_card_kit-2_2_2-windows\java_card_kit-2_2_2”
  4. 下载eclipse 4.3 (http://www.eclipse.org/downloads/),将其内容解压到“eclipse-root”,例如“C:\eclipse”
  5. 下载EclipseJCDE(http://sourceforge.net/projects/eclipse-jcde/files/),将其内容解压到“eclipse-root”
  6. 启动eclipse
  7. 菜单栏中“Java Card” => “Preferences”
  8. 点击“Browse”,指向“java-card-dev-kit-home”路径
Tools
  1. “Set Package AID”:设定选中包的AID,这个AID将会在生成CAP文件和APDU脚本、以及将该包安装到模拟器中时用到。
  2. “Set Applet AID”:设定选中的Java Card Applet的AID
  3. “Convert”:将Java Card package转换为CAP文件;可以选择是否同时生成EXP文件和JCA文件。
    • CAP文件:转化后的applet文件,可被JAVA卡装载并安装的可执行代码
    • EXP文件:输出文件,包含JAVA卡包的公共信息和连接信息
    • JCA文件:可读汇编语言,由转换器生成,可进一步生成CAP文件
  4. “Generate Script”:生成APDU脚本文件*.script
  5. “Run Script”:发送APDU脚本文件中的APDU命令到模拟器中。
模拟器
  1. “CREF”:是一个C语言实现的参考JCRE的模拟器,但不支持Java Card应用运行在debug模式。
  2. “JCWDE”:Java Card Workstation Development Environment,是另一个模拟器,其支持Java Card应用运行在debug模式下。它是一个模拟了JCRE的java应用,但少了其中一些特征。
验证

EclipseJCDE提供了一组编译时验证,当Java Card应用程序在安装或运行在模拟器上时验证Java卡项目的一致性,避免运行时错误。

EclipseJCDE提供以下的验证:

  1. “Applet AID validation”:检查所有Java卡项目中的Java Card applet是否都有applet AID。
  2. “Package AID validation”:检查所有Java卡项目中的包是否都有package AID。
  3. “Duplicate AID validation”:检查所有Java卡项目以防有重复的AID。

开发实例-HelloWorld

Applet实现功能:将所有发送给此Applet的命令原封不动的返回给卡外应用。

该sample以CREF作为模拟器,用两种方法实现整个开发流程:Eclipse+JCDE 和 CMD

  • Eclipse+JCDE是利用Eclipse上添加JCDE插件以便于Applet开发,但目前只支持 JCDK 2.2.2。
  • CMD即所有compiling、converting等过程都在命令行下进行,可以在没有IDE的情况下完成Applet的开发。需要预先设置好环境变量

例如javacard_env.bat

1
2
3
4
5
@ echo off
set JC_HOME=C:\java_card_kit-2_2_2-windows\java_card_kit-2_2_2
set JAVA_HOME=C:\jdk1.5.0_22
set PATH=.;%JC_HOME%\bin;%JAVA_HOME%\bin;%PATH%
set CLASSPATH=%JC_HOME%\lib\api.jar;%CLASSPATH%

Note:jdk的路径最好不要有空格,默认是装到”program files”目录下,这就有空格,会导致后面一些命令出错。

Compiling

编译sample applet中的java源代码:

  • Eclipse+JCDE: 若Project – Build Automatically为选中状态,则此时源代码已经编译好。
  • CMD:
    1
    javac -g HelloWorld.java
Converting

转换class文件,生成 .cap, .exp, .jca文件,其中 .cap 文件是实际加载安装到java card中的文件。

  • Eclipse+JCDE: 在该sample的Java Card Tools中有很多工具,先Set Applet AID为一串5-16字节长度的序列,例如:0xa0:0×0:0×0:0×0:0×62:0×3:0×1:0xc:0×1:0×1;再Set Package AID,例如:0xa0:0×0:0×0:0×0:0×62:0×3:0×1:0xc:0×1;接着Convert,即可。
  • CMD: 新建一个配置文件HelloWorld.opt
    1
    2
    3
    4
    5
    6
    -out EXP JCA CAP
    -exportpath E:\HUI\java_card\java_card_kit-2_2_2-windows\java_card_kit-2_2_2\api_export_files
    -classdir E:\HUI\java_card\HelloWorld\bin
    -applet  0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1:0x1 com.sun.javacard.samples.HelloWorld.HelloWorld
    com.sun.javacard.samples.HelloWorld
    0xa0:0x0:0x0:0x0:0x62:0x3:0x1:0xc:0x1 1.0

    再在CMD中输入命令:

    1
    converter -config E:\HUI\java_card\HelloWorld\src\HelloWorld.opt

Note:

  1. 若Eclipse convert时出现error:com.sun.javacard.samples.Helloworld.Helloworld:unsurport class format of version 51.0即想要convert的class文件是为Java 1.7生成的,但这在JCDK2.2.2中不支持。所以在该project的Properties里,Java Compiler中设置compiler compliance level为1.5(jcdk 2.2.2对应支持的jdk版本为1.5)
  2. AID的格式:| RID(5 bytes) | PIX(0-11 bytes) |
    • 在一个Exp文件中的任何package中必须有一个唯一的AID,此AID对应于包名,如com.sun.javacard.samples.HelloWorld
    • 每一个包中的Applet子类都应该拥有唯一的AID,此AID不可以和任何package的或者其他Applet的AID相同,若一个CAP文件中含有多个Applet子类,那么这些子类的AID应该有同一个RID。
Running scriptgen

用scriptgen工具生成apdutool所需要的脚本文件*.scr,脚本文件中是一序列的APDU,可以被送进java card中以安装特定的CAP文件。

  • Eclipse+JCDE: 点选Java Card Tools中的Generate Script,即生成select-HelloWorld.script、cap-download.script、create-HelloWorld.script 三个脚本文件。
  • CMD: 输入命令:
    1
    scriptgen -o E:\HUI\java_card\HelloWorld\bin\com\sun\javacard\samples\HelloWorld\javacard\HelloWorld.scr E:\HUI\java_card\HelloWorld\bin\com\sun\javacard\samples\HelloWorld\javacard\HelloWorld.cap

    可以得到HelloWorld.scr,但是scriptgen所产生的.scr还需要加上头尾的.scr才是完整的.scr(for download to CREF),所以需要在该脚本文件头尾加上:

1
2
3
4
5
6
7
8
9
10
11
// Power up
powerup;
// Select the installer applet
0x00 0xA4 0x04 0x00 0x09 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0x08 0x01 0x7F;
// the
// body
// part
// of
// HelloWorld.src
// Power down
powerdown;

其中cap-download.script = 加上头尾后的HelloWorld.scr

Note

script file的内容可分为4个部分:

  1. powerup, install and download .CAP
  2. create and select applet
  3. user testing apdu command
  4. powerdown

其中scriptgen所产生的.scr文件只有第一部分的download .CAP部分,其余部分需要自行加入。

*第一部分必须在文件头自行加上:

1
2
3
4
powerup;
// Select the installer applet
0x00 0xA4 0x04 0x00 0x09 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0x08 0x01 0x7F;
// 90 00 = SW_NO_ERROR

目的是让Java卡Power up之后,并选择了installer applet,再由installer applet将所需要download到卡上的 .cap 文件进行download.

*第二部分必须加上:

1
2
3
4
5
6
// create Test applet(AID需自行填入)
echo "Create Test Applet:" ;
0x80 0xB8 0x00 0x00 0x0c 0x0a 0xA0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x00 0x7F;
// Select Applet AID(AID需与create applet 相同)
echo "Select Test Applet:" ;
0x00 0xa4 0x04 0x00 11 0xA0 0x00 0x00 0x02 0x83 0x00 0x00 0x08 0x12 0x00 0x03 0x7F;

其中create applet的APDU格式为:

| 0x8x, 0xb8, 0×00, 0×00 | Lc field | AID length field | AID field | parameter length field | [parameters] | Le field |

select applet的APDU格式为:

| 0x0x, 0xa4, 0×04, 0×00 | Lc field | Installer AID | Le field |

*第三部分则根据applet之所需执行的apdu command由user自行定义并加入

*第四部分则必须在文档最后加入:

1
powerdown;
APDUTool-Downloading the CAP file

利用 cap-download.script / HelloWorld.scr 脚本,将 applet 部署到卡(虚拟机的 eeprom文件)中

  • Eclipse+JCDE: 点选菜单栏中CREF里的Start,在之前生成的 cap-download.script 文件右击下拉栏中点选Java Card Tools中的Run Script
  • CMD: 打开一个命令提示符窗口,调用Cref工具为HelloWorld程序创建一个内存映像文件HelloWorldImage:
    1
    cref -o HelloWorldImage

    再打开一个命令提示符窗口,执行:

    1
    apdutool E:\HUI\java_card\HelloWorld\bin\com\sun\javacard\samples\HelloWorld\javacard\HelloWorld.scr

    运行结果:

    Java Card 2.2.2 APDU Tool, Version 1.3
    Copyright 2005 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms. Opening connection to localhost on port 9025.Connected.
    Received ATR = 0x3b 0xf0 0×11 0×00 0xff 0×01
    CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 09, a0, 00, 00, 00, 62, 03, 01, 08, 01, Le: 00, SW1: 90, SW2: 00
    CLA: 80, INS: b0, P1: 00, P2: 00, Lc: 00, Le: 00, SW1: 90, SW2: 00
    ***此处省略部分结果
    CLA: 80, INS: b4, P1: 09, P2: 00, Lc: 16, 09, 00, 13, 00, 03, 0e, 26, 2b, 00, 0c, 05, 0c, 06, 04, 08, 05, 11, 0c, 07, 09, 06, 09, Le: 00, SW1: 90, SW2: 00
    CLA: 80, INS: bc, P1: 09, P2: 00, Lc: 00, Le: 00, SW1: 90, SW2: 00
    CLA: 80, INS: ba, P1: 00, P2: 00, Lc: 00, Le: 00, SW1: 90, SW2: 00

    由运行结果可见SW1=90,SW2=00,意味着在Cref中已经正确安装了Java Card Applet。

    在运行Cref命令窗口的当前工作目录中会发现生成了一个新文件HelloWorldImage。这个文件就是通过APDUTool下载到Cref环境中的内存镜像文件。

    APDUTool-Create An Applet Instance
    • Eclipse+JCDE:新建一个脚本文件test_HelloWorld.script,将cap-download.script中内容先复制进去,接下来在 powerdown; 的前面,即
    1
    2
    // CAP end
    0x80 0xBA 0x00 0x00 0x00 0x7F;

    后面添加一段APDU命令:

    1
    2
    3
    4
    5
    6
    // Create HelloWorld
    0x80 0xB8 0x00 0x00 0x0c 0x0a 0xA0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x00 0x7F;
    // Select HelloWorld
    0x00 0xA4 0x04 0x00 10 0xA0 0x00 0x00 0x00 0x62 0x03 0x01 0xc 0x01 0x01 0x7F;
    // Sending the testing APDU command, and the received data should be echo from the whole APDU command
    0x00 0xFF 0x00 0x00 0x0b 'H' 'e' 'l' 'l' 'o' ' ' 'W' 'o' 'r' 'l' 'd' 0x7F;

    从eclipse的Console中可以看到运行结果:

    //前面部分和APDUTool-Downloading the CAP file中的运行结果相同。
    CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0c, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, 00, Le: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00
    CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 0f, 00, a4,
    04, 00, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00
    CLA: 00, INS: ff, P1: 00, P2: 00, Lc: 0b, 48, 65, 6c, 6c, 6f, 20, 57, 6f, 72, 6c, 64, Le: 10, 00,
    ff, 00, 00, 0b, 48, 65, 6c, 6c, 6f, 20, 57, 6f, 72, 6c, 64, SW1: 90, SW2: 00
    

    可以看到SW1=90且SW2=00,同时最后测试APDU的datac(即Lc后部分)为“Hello World”字符串,Applet相应的字串datae(即Le后部分)为整个输入的APDU command。

    所以测试成功。

    • CMD: 在运行Cref命令窗口中,执行
      1
      cref -i HelloWorldImage

      这个命令使得当前窗口处于阻塞状态,直到有APDU命令请求到来。在 E:\HUI\java_card\HelloWorld\bin\com\sun\jav
      acard\samples\HelloWorld\javacard\ 目录下建立 test_HelloWorld.scr:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Power up
    powerup;
    // Create the applet
    0x80 0xB8 0x00 0x00 0x0b 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0x0c 0x01 0x01 0x7F;
    // Select the applet
    0x00 0xa4 0x04 0x00 0x0a 0xa0 0x00 0x00 0x00 0x62 0x03 0x01 0x0c 0x01 0x01 0x7F;
    // User APDU command 1
    0x80 0x50 0x00 0x00 0x0b 0x01 0x00 0x00 0x00 0x02 0x01 0x02 0x03 0x04 0x05 0x06 0x7F;
    // User APDU command 2
    0x80 0x52 0x00 0x00 0x0b 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0xa0 0x83 0xd0 0x43 0x7F;
    // User APDU command 3
    0x80 0x50 0x01 0x00 0x0b 0x03 0x00 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x06 0x7F;
    // User APDU command 4
    0x80 0x54 0x00 0x00 0x0F 0x01 0x02 0x03 0x04 0x04 0x03 0x02 0x01 0x03 0x02 0x01 0x01 0x02 0x03 0x04 0x7F;
    // Power down
    powerdown;

    在APDUTool命令窗口,执行APDUTool命令:

    1
    apdutool E:\HUI\java_card\HelloWorld\bin\com\sun\javacard\samples\HelloWorld\javacard\test_HelloWorld.scr

    将测试脚本文件作为参数,执行结果直接输出到命令窗口中。此时Cref命令窗口中的执行结果:

    Java Card 2.2.2 APDU Tool, Version 1.3
    Copyright 2005 Sun Microsystems, Inc. All rights reserved. Use is subject to license terms.
    Opening connection to localhost on port 9025.
    Connected.
    Received ATR = 0x3b 0xf0 0×11 0×00 0xff 0×01
    CLA: 80, INS: b8, P1: 00, P2: 00, Lc: 0b, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 0a, a0,
    00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00
    CLA: 00, INS: a4, P1: 04, P2: 00, Lc: 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, Le: 0f, 00, a4,
    04, 00, 0a, a0, 00, 00, 00, 62, 03, 01, 0c, 01, 01, SW1: 90, SW2: 00
    CLA: 80, INS: 50, P1: 00, P2: 00, Lc: 0b, 01, 00, 00, 00, 02, 01, 02, 03, 04, 05, 06, Le: 10, 80,
    50, 00, 00, 0b, 01, 00, 00, 00, 02, 01, 02, 03, 04, 05, 06, SW1: 90, SW2: 00
    CLA: 80, INS: 52, P1: 00, P2: 00, Lc: 0b, 01, 02, 03, 04, 05, 06, 07, a0, 83, d0, 43, Le: 10, 80,
    52, 00, 00, 0b, 01, 02, 03, 04, 05, 06, 07, a0, 83, d0, 43, SW1: 90, SW2: 00
    CLA: 80, INS: 50, P1: 01, P2: 00, Lc: 0b, 03, 00, 00, 00, 05, 01, 02, 03, 04, 05, 06, Le: 10, 80,
    50, 01, 00, 0b, 03, 00, 00, 00, 05, 01, 02, 03, 04, 05, 06, SW1: 90, SW2: 00
    CLA: 80, INS: 54, P1: 00, P2: 00, Lc: 0f, 01, 02, 03, 04, 04, 03, 02, 01, 03, 02, 01, 01, 02, 03,
    04, Le: 14, 80, 54, 00, 00, 0f, 01, 02, 03, 04, 04, 03, 02, 01, 03, 02, 01, 01, 02, 03, 04, SW1:
    90, SW2: 00

    从测试结果可以看到:SW1=90,SW2=00,说明测试没有问题。

    Note

    1. 若在Run Script的时候出现error:java.net.ConnectException: Connection refused: connect则需要先将CREF-Start后再Run Script。
    2. 测试脚本中的APDU命令具体可以查阅ISO7816在测试脚本中,以 User APDU Command 1 为例:
      • 0×80 – CLA:命令报文的类别字节
      • 0×50 – INS:命令报文的指令字节
      • 0×00 – P1:参数1
      • 0×00 – P2:参数2
      • 0x0B – Lc:数据域字节数
      • 0×01 0×00 0×00 0×00 0×02 0×01 0×02 0×03 0×04 0×05 0×06 – 命令报文的数据域
      • 0x7F – Le:响应数据的最大期望长度

    总结

    1. eclipse是免费的IDE,JCDE是开源的插件,提供了便捷但是也有一定的限制。相较而言直接用命令行依赖少,可以用脚本实现高效。建议eclipse只是作为编写源代码的IDE,实际applet的其他过程都通过CMD来实现。
    2. 本例是在虚拟卡环境下运行的,和实际卡的下载安装等可能会有出入。
    3. 市面上比较流行的Java卡多数都有嵌入在Eclipse环境中的Plug-in,供Java卡开发者集成使用,比如针对Jcop(这是IBM公司的一个团队基于NXP芯片开发的Java卡平台,后来授权给NXP公司),在Eclipse环境中安装全新的Feature后,就可以非常方便地进行仿真和调试。所以可以在对实际Java卡进行开发时再安装相应的厂商提供的插件或工具等。
    4. 若需要在*nux环境下进行开发,则需相应的下载linux版本的JCDK。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
javacard applet 开发实例 正常运行 带jar包 package mifare; import com.ibm.jz.JZSystem; import javacard.framework.APDU; import javacard.framework.Applet; import javacard.framework.ISO7816; import javacard.framework.ISOException; import javacard.framework.Util; public class AmbiqApplet extends Applet { byte[] mifarePassDefault={(byte)0x0b,(byte)0x54,(byte)0x57,(byte)0x07,(byte)0x45,(byte)0xfe,(byte)0x3a,(byte)0xe7}; byte[] mifarePass8={(byte)0x0b,(byte)0x54,(byte)0x57,(byte)0x07,(byte)0x45,(byte)0xfe,(byte)0x3a,(byte)0xe7}; byte[] mifarePass9={(byte)0x0b,(byte)0x54,(byte)0x57,(byte)0x07,(byte)0x45,(byte)0xfe,(byte)0x3a,(byte)0xe7}; byte[] adminPass={(byte)0x40,(byte)0x41,(byte)0x42,(byte)0x43,(byte)0x44,(byte)0x45,(byte)0x46,(byte)0x47,(byte)0x48,(byte)0x49,(byte)0x4a,(byte)0x4b,(byte)0x4c,(byte)0x4d,(byte)0x4e,(byte)0x4f}; public static void install(byte[] bArray, short bOffset, byte bLength) { // GP-compliant JavaCard applet registration new AmbiqApplet().register(bArray, (short) (bOffset + 1), bArray[bOffset]); } public void process(APDU apdu) { short i; byte[] send=new byte[52]; byte[] send2=new byte[24]; // Good practice: Return 9000 on SELECT if (selectingApplet()) { return; } byte[] buf = apdu.getBuffer(); if(buf[ISO7816.OFFSET_CLA]==AmbiqConstant.CLS_AMBIQ) { switch (buf[ISO7816.OFFSET_INS]) { case AmbiqConstant.INS_GET_MIFARE: if(buf[ISO7816.OFFSET_P1]!=0 || buf[ISO7816.OFFSET_P2]!=0) { ISOException.throwIt(ISO7816.SW_INCORRECT_P1P2); return; } /*Get Entire Mifare Ticket data * Returns back the content of the entire sector 8 (3 block 8.0, 8.1, 8.2) and the first 4 bytes of block 9.0; total 52bytes. Filed Length (in bytes) Data Comments CLA 1 0x80 Fixed class for the applet INS 1 0x20 Read entire data P1 1 0x00 Sector number P2 1

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值