第二章:JavaCard开发环境搭建

好,咱们正式开始动手了。

上一章聊了JavaCard是什么、能干什么。这一章,咱们把开发环境搭起来,写一个真正的Applet,然后把它装到卡片上。

我个人习惯先把环境搞利索再写代码。你想想看,如果环境配不好,后面调试起来会非常痛苦。我在项目中遇到过好几次,明明代码没问题,结果是因为SDK版本不对,折腾了一整天。

2.1 安装JavaCard SDK

JavaCard SDK是Oracle官方提供的开发工具包。目前最新的稳定版本是3.x,但很多实际项目还在用2.2.2或3.0.4。为什么?因为卡片厂商的底层支持往往滞后。

下载地址:Oracle官网(需要注册账号,免费)

安装步骤

  1. 下载 java_card_kit-3_0_5-win32-bin.zip(Windows为例)
  2. 解压到 C:\JavaCardKit\(路径不要有中文和空格)
  3. 设置环境变量 JC_HOME=C:\JavaCardKit\java_card_kit-3_0_5
  4. %JC_HOME%\bin 加入 PATH
⚠️ 注意:我曾经见过有人把SDK解压到桌面,结果编译时路径解析失败。建议放在盘符根目录下,比如 C:\jcD:\jc

2.2 配置开发工具

说实话,JavaCard开发不需要什么花哨的IDE。记事本都能写,但效率太低。

我推荐的工具组合

工具用途我的建议
Eclipse / IntelliJ IDEA代码编辑用你熟悉的就行,我习惯Eclipse
Ant / Maven构建工具Ant在JavaCard圈更常见
GPJ(GlobalPlatform Pro)卡片管理装Applet必备
JCDK自带的工具模拟器、转换器SDK自带,不用额外装

Eclipse配置步骤

  1. 安装Eclipse IDE for Java Developers
  2. 新建Java项目,取消勾选“Use default location”,选择SDK下的 samples 目录
  3. 添加 jc.jar 到Build Path(在 %JC_HOME%\lib\ 下)
  4. 安装 JCDE 插件(可选,但能提供语法高亮)
💡 小技巧:如果你用IntelliJ,记得把SDK的 api 目录设为源码根目录,不然很多类会报红。嗯,这里要注意。

2.3 编写第一个Applet

好了,环境搭好了。咱们写一个最简单的Applet——它什么都不做,只是响应一个“Hello”命令。

import javacard.framework.*;

public class HelloApplet extends Applet {

    private static final byte[] HELLO_MSG = {'H', 'e', 'l', 'l', 'o'};
    private static final byte CLA = (byte) 0x00;
    private static final byte INS_HELLO = (byte) 0x01;

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new HelloApplet().register();
    }

    public void process(APDU apdu) {
        if (selectingApplet()) {
            return;
        }

        byte[] buffer = apdu.getBuffer();
        // 检查CLA和INS
        if (buffer[ISO7816.OFFSET_CLA] != CLA) {
            ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
        }
        if (buffer[ISO7816.OFFSET_INS] != INS_HELLO) {
            ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }

        // 返回"Hello"
        apdu.setOutgoingAndSend((short) 0, (short) HELLO_MSG.length);
        Util.arrayCopyNonAtomic(HELLO_MSG, (short) 0, buffer, (short) 0, (short) HELLO_MSG.length);
    }
}

这段代码我解释一下:

  • install() 是入口,每个Applet必须实现。说白了就是卡片的构造函数。
  • process() 处理APDU命令。APDU是卡片和终端通信的协议单元。
  • selectingApplet() 判断当前是不是选择命令。如果是,直接返回,不处理。

核心要点

  • 所有Applet必须继承 javacard.framework.Applet
  • 必须实现 install()process()
  • APDU缓冲区是共享的,操作要小心

2.4 编译与安装

写完了代码,怎么把它弄到卡片上?

第一步:编译

JavaCard的编译和普通Java不一样。它需要两步:

  1. 先用普通javac编译成.class文件
  2. 再用 converter 工具转换成CAP文件(JavaCard的可执行文件)

我一般写一个Ant脚本来自动化这个过程:

<project name="HelloApplet" default="build">
    <property name="jc.home" value="C:/JavaCardKit/java_card_kit-3_0_5"/>
    
    <target name="build">
        <!-- 第一步:javac编译 -->
        <exec executable="javac">
            <arg value="-cp"/>
            <arg value="${jc.home}/lib/jc.jar"/>
            <arg value="-d"/>
            <arg value="build/classes"/>
            <arg value="src/HelloApplet.java"/>
        </exec>
        
        <!-- 第二步:converter转换 -->
        <exec executable="${jc.home}/bin/converter">
            <arg value="-classdir"/>
            <arg value="build/classes"/>
            <arg value="-d"/>
            <arg value="build/cap"/>
            <arg value="-applet"/>
            <arg value="0x01:0x02:0x03:0x04:0x05:0x06:0x07:0x08:HelloApplet"/>
            <arg value="-out"/>
            <arg value="CAP"/>
            <arg value="HelloApplet"/>
        </exec>
    </target>
</project>

第二步:安装到卡片

安装需要用到GlobalPlatform命令。我常用 gp 工具(GlobalPlatform Pro):

# 加载CAP文件到卡片
gp --install build/cap/HelloApplet.cap

# 列出已安装的Applet
gp --list

# 删除Applet(如果装错了)
gp --delete 0x01:0x02:0x03:0x04:0x05:0x06:0x07:0x08
⚠️ 重要提醒:安装前确保卡片支持JavaCard。不是所有智能卡都能装Applet。我见过有人拿Mifare卡来装,结果卡直接废了。嗯,Mifare是存储卡,不是JavaCard。

2.5 验证安装是否成功

装好了怎么知道它跑起来了?用APDU测试工具发个命令试试。

我推荐 JCIDE 自带的APDU工具,或者用Python写个脚本:

import smartcard.System

# 连接卡片
readers = smartcard.System.readers()
reader = readers[0]
connection = reader.createConnection()
connection.connect()

# 选择Applet
SELECT = [0x00, 0xA4, 0x04, 0x00, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]
data, sw1, sw2 = connection.transmit(SELECT)
print(f"选择结果: {sw1:02X} {sw2:02X}")

# 发送Hello命令
HELLO = [0x00, 0x01, 0x00, 0x00, 0x00]
data, sw1, sw2 = connection.transmit(HELLO)
print(f"返回数据: {bytes(data)}")
print(f"状态字: {sw1:02X} {sw2:02X}")

如果返回 Hello 且状态字是 90 00,恭喜你,第一个Applet跑起来了!

💡 避坑指南:我曾经在测试时发现返回全是0xFF,后来发现是APDU的LC字段写错了。LC表示后续数据长度,如果设为0,卡片会认为没有数据。这个坑我踩过两次。

2.6 本章知识体系

下面这张图总结了开发环境搭建的完整流程:

JavaCard开发环境搭建流程 1. 安装SDK 下载+解压+环境变量 2. 配置工具 Eclipse + Ant + GP 3. 编写Applet 继承Applet类 4. 编译 javac → converter 5. 安装到卡片 gp --install 6. 验证 APDU测试 失败则返回调试 输出:CAP文件 → 安装到JavaCard → 验证APDU响应 准备阶段 构建阶段 验证阶段

这张图把整个流程串起来了。从安装SDK到验证Applet,每一步都有坑。但只要你按这个流程走,基本不会出大问题。

好了,环境搭好了,第一个Applet也跑起来了。接下来你就可以开始折腾更复杂的功能了。记住,JavaCard开发的核心就是APDU通信,把这条线抓住了,后面就顺了。


公众号:蓝海资料掘金营,微信deep3321