JAVA 智能的可執行文件(CAP 文件)是編譯多個應用程序(Applet)的生成結果,包含了一個包中定義的所有類和接口,與包之間是一一對應的關係。實際髮卡操作時,首先需要將該可執行文件下載至卡片中,並安裝需要的應用實例;用戶使用該安裝的應用實例執行操作功能。
CAP文件包含12個組件:
Component Type |
Value |
COMPONENT_Header |
1 |
COMPONENT_Directory |
2 |
COMPONENT_Applet |
3 |
COMPONENT_Import |
4 |
COMPONENT_ConstantPool |
5 |
COMPONENT_Class |
6 |
COMPONENT_Method |
7 |
COMPONENT_StaticField |
8 |
COMPONENT_ReferenceLocation |
9 |
COMPONENT_Export |
10 |
COMPONENT_Descriptor |
11 |
COMPONENT_Debug |
12 |
注意:
一個完整的CAP文件,除Applet、Export 和Debug組件是可選外,其他均爲必選。每個組件封裝成一個CAP包,包含在Jar包中。最後在卡上只保留了5個組件:COMPONET_Method,COMPONET_Class,COMPONET_ConstantPool,COMPONET_StaticField和 COMPONET_Export。其餘的組件只是安裝時提取有用信息而不在卡中保存。
12個組件中,類class組件保存本應用聲明的所有類和接口的信息; 方法method組件保存本應用聲明的所有方法和接口,method中利用2字節索引index引用類、方法和域;常數池constant pool組件保存method組件引用的所有類、方法和域信息,分爲類、實例域、虛方法、父方法、靜態域和靜態方法6類,每組信息爲4個字節;相關地址reference location組件保存method組件中索引的偏移。
對於JavaCard而言,應用程序的下載過程是即CAP文件寫入到EEPROM的過程,即是對CAP文件的下載過程。在CAP文件的下載過程中,需要將一部分組件進行解析,同時對reference location中指定的位置進行鏈接,能夠鏈接到method組件中的一個索引號,並根據索引號查找constant pool中保存的、與該索引號對應的類、方法或域在 EEPROM中的實際地址,調用實際地址中存儲的數據。也就是說,方法的調用其實是需要兩個步驟來實現的:
- 根據reference location中指定的位置進行鏈接,獲取method組件中的索引號;
- 根據索引號查找constant pool中保存的、與該索引號對應的類、方法或域在EEPROM中的實際地址,調用實際地址中存儲的數據。
查看一個CAP文件的組件,可以通過兩種方法實現:
- 解壓縮軟件直接解壓得到
- 通過JCK中的capdump將其分解成各個組件
下面是加壓cap的java源碼:
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.List;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipFile;
- import android.os.Environment;
- import android.util.Log;
- public class Cap {
- static final String TAG = "cap";
- static public final String CAP_DIRECTORY = "//mnt//sdcard//cap//";
- static final int READ_LENGTH = 1024;
- private List<String> mCapFileList;
- public void loadCapFile(String CapFileName) {
- try {
- if (!Environment.getExternalStorageState().equals(
- Environment.MEDIA_MOUNTED)) {
- Log.e(TAG, "SD card not available!");
- return;
- }
- File file = new File(CAP_DIRECTORY + CapFileName);
- if (!file.exists()) {
- Log.e(TAG, CAP_DIRECTORY + "safe.cap not available!");
- return;
- }
- mCapFileList = new ArrayList<String>();
- mCapFileList.clear();
- BufferedOutputStream dest = null;
- BufferedInputStream is = null;
- ZipEntry entry;
- ZipFile zipfile = new ZipFile(file);
- Enumeration<? extends ZipEntry> e = zipfile.entries();
- while (e.hasMoreElements()) {
- entry = (ZipEntry) e.nextElement();
- String fileName = entry.getName();
- if (fileName.endsWith(".MF"))
- continue;
- if (fileName.contains("/")) {
- int dir = fileName.lastIndexOf("/");
- String root = CAP_DIRECTORY + fileName.substring(0, dir);
- File path = new File(root);
- if (!path.exists()) {
- path.mkdirs();
- }
- }
- int count;
- byte data[] = new byte[READ_LENGTH];
- mCapFileList.add(CAP_DIRECTORY + entry.getName());
- FileOutputStream fos = new FileOutputStream(CAP_DIRECTORY
- + entry.getName());
- dest = new BufferedOutputStream(fos, READ_LENGTH);
- is = new BufferedInputStream(zipfile.getInputStream(entry));
- while ((count = is.read(data, 0, READ_LENGTH)) != -1) {
- dest.write(data, 0, count);
- }
- dest.flush();
- dest.close();
- is.close();
- fos.close();
- }
- } catch (Exception e) {
- Log.e(TAG, "loadCap: " + e.getMessage());
- }
- }
- public List<String> getCapFileList() {
- return mCapFileList;
- }
- }
使用方法
- Cap cap = new Cap();
- cap.loadCapFile("HelloWorld.cap");
所有的組件均有通用結構格式,如下:
- component {
- u1 tag //u1表示無符號單字節類型的數據變量類型;tag爲組件索引號,按照上面組件名稱的順序從1至12排列
- u2 size //u2表示無符號雙字節的數據變量類型;size爲可變長度數組info[]的元素個數
- u1 info[] //數組info[]中含了組件的所有信息,依據各組件屬性不同而各不相同
- }
比如Applet.cap的十六進制數據是:
0300090105c0c1c2c3c40085
第一個03是tag,然後是0009是size,說明info爲9個字符,01標示了這是包中的第一個applet,05爲該Applet的AID的長度,後面的c0c1c2c3c4即爲applet的AID,後面的0085是該Applet的install_method_offset,即當前Applet中的install()在Method組件info[]中的偏移。
推薦的CAP組件安裝順序:
- COMPONENT_Header
- COMPONENT_Directory
- COMPONENT_Import
- COMPONENT_Applet
- COMPONENT_Class
- COMPONENT_Method
- COMPONENT_StaticField
- COMPONENT_Export
- COMPONENT_ConstantPool
- COMPONENT_ReferenceLocation
- COMPONENT_Descriptor (optional)
COMPONENT_Debug組件不需要下載到卡內。
作者:fish