摘要:
從本文開始,我們陸續刊登一系列在智能卡上進行Java開發的文章,以此把智能卡向您作一個基本的介紹。只要有一塊智能卡、一個讀卡機和一套可與智能卡通信的軟件,你就可以開始進行實用應用程序的開發了。本文介紹的內容包括:遵循ISO7816標准的智能卡管理軟件;如何使用Gemplus閱讀器和Gemplus智能卡從智能卡中讀寫內存;一些利用智能卡的存儲功能的應用程序。以後的文章將介紹不同廠家生產的智能卡,並且還將涉及智能卡的標准。
作為專題系列的第一篇,本文將主要為以後的討論作一個鋪墊工作。在此,我們將討論一個稱為OpenCard的新標准。以後的文章將涉及安全智能卡和電子錢包。最後,我們將向您介紹智能卡軟件的基本框架結構。
無論是在四月份的JavaOne大會上(與該技術有關的四次演講),還是在巨大的網絡新聞站或是CNN,智能卡掀起了軒然大波。在本文裡,我們將以實用智能卡實例向您展示真正的智能卡。這裡的技術將允許你開發智能卡Java應用程序。
我們的討論焦點主要集中在兩種智能卡之上:存儲智能卡,它可以被看作帶有可選安全級別的小型可讀寫磁盤;處理器卡,它可以被看作帶有一個輸入輸出端口的迷你型計算機。以後的文章將更為深入的介紹處理器卡。
本文的精華之處在於,我們將為讀寫智能卡創立一個簡單的原形。我們要討論一種醫藥處方卡,它將替你保存所有的藥方清單,並檢查保險、處方計劃等等有用的信息。並且我們將圍繞處方卡擴展開去。
在這一系列的文章之中,你會注意到伴隨智能卡的一個常見主題,那就是安全性問題。這裡所說的安全,主要是防止通過卡的濫插入或ActiveX組件等手段非法獲取數據。為此目的,本文中關於讀寫智能卡中數據的范例將給你提供一個安全、一致且具可移植性的存儲。
智能卡是什麼
你可以把智能卡當作一個帶有大腦的信用卡。其大腦就是一個小的嵌入式計算機芯片。這種芯片可以被編程執行某項任務或是存儲一些數據,但應時刻牢記:該種芯片只不過是小型的,它的處理能力絕對比不上你的桌面計算機。
目前,智能卡僅僅用於電話、運輸、銀行和保健等行業,但是感謝眾多的開發者,在不久的將來,我們就會看到智能卡應用於Internet應用程序之中。智能卡已經在日本和歐洲廣為應用,並且在美國受到了歡迎。事實上,在美國的智能卡業界最近共發生了三件頗具意義的事件:
PC/SC
Microsoft聯合其它幾家公司推出了稱為PC/SC的智能卡應用程序標准,用於Win32平台的個人計算機與智能卡之間實現互通信。PC/SC目前不支持非Win32的系統平台,或許Microsoft 永遠也不會那樣做。稍後我們將對此詳加討論。
OpenCard框架
OpenCard是一個開放式的標准,它支持智能卡應用程序在網絡計算機、POS、桌面和膝上計算機等平台之上實現互操作。OpenCard許諾提供100%純Java的智能卡應用程序。通常智能卡應用程序不能完全借助於純Java,因為它們必需與外設互通信或是利用客戶之上的程序庫。(當然,沒有OpenCard,我們仍然能夠使用100%的純Java,但是,智能卡的開發者必需從頭定制一個接口。)OpenCard還提供了一個到PC/SC的接口,使得開發者得以使用現有Win32 平台上的設備。
JavaCard
JavaCard最初由Schlumberger提出,目前已被JavaSoft制定為一項標准。Schlumberger 目前在市場上只提供Java智能卡,而且該公司是第一個獲得JavaCard許可的廠商。作為一項極有潛力占踞統治地位的智能卡標准,JavaCard包括了標准的類庫和API,使得Java小應用能夠直接運行在遵循ISO7816標准的智能卡之上。JavaCard對不同的應用程序提供安全和與芯片無關的運行環境。
注意:
盡管本文集中於智能卡主題,但更為重要的是你決不應囿於這一種設備之上。從我個 人角度將,我更為偏愛Dallas Semiconductor生產的“Ibutton”設備。它象一個小型便攜的 信用卡一樣,但是更為易用。為什麼呢?因為你不再需要拿出錢包從幾張卡中選出要用的 那一張,Ibutton就在你的手中。對了,它是一枚指環。
盡管存在無接觸型的智能卡(關於這方面的信息見下文),我認為Ibutton這樣象珠寶 飾品一樣的產品一定會有利可圖。關於Ibutton詳情請見參考資料。順便說一句,八月份在 紐約的Java Internet Business Expo(JIBE)展會上,Java Commerce Team展示了“JavaRing”。
為什麼使用智能卡?
使用智能卡有何好處呢?好,下面回答這個問題:
1.智能卡比磁卡更為可靠
2.智能卡能夠存儲數百倍於磁卡的數據
3.智能卡比磁卡更難於被破壞
4.智能卡可以被處理或是回收
5.智能卡在工業上可用的范圍廣闊,並可提供多種功能
6.智能卡與便攜的電子設備兼容,比如說電話、PC或是個人數字助手(PDA)
7.智能卡在不斷的發展(畢竟它內部包含了一塊計算機芯片)
智能卡的種類
正如前面所述,本文將集中討論兩種類型的智能卡:存儲和處理器型。但是目前共有五個類型的智能卡:
存儲智能卡
處理器智能卡
電子錢包
安全卡
JavaCard
智能卡是硬件的便攜部分,它必須借助於其它設備才能獲取對某種顯示設備或是網絡的訪問。可以將卡插入讀卡器,這通常稱為智能卡終端;也可通過射頻無線電波來實現。
智能卡以下面兩種方式與閱讀器或是接收器互通信:
接觸智能卡:當智能卡前端的芯片與閱讀器相接觸時,兩者之間才傳遞信息。
無接觸智能卡:這種信息傳遞通過天線來進行,省去了手工插入或拔出智能卡的動作。有了無接觸型卡,你僅須走近閱讀器,然後的信息傳遞將自動進行。這種類型的智能卡可用於對速度要求較高或是插入拔出並不可行的應用環境。
一些廠商對這兩種類型的智能卡均已開始了生產。
為智能卡應用程序創建開發環境
為了開發智能卡應用程序,你只需要這幾件東西:一個智能卡閱讀器、與閱讀器通信的軟件、與插入閱讀器的卡通信的軟件,當然還要有智能卡及相關硬件設備。
智能卡閱讀器
為了與智能卡相互傳遞信息,或是要開發一套在智能卡上運行的應用程序,你必須擁有一個閱讀器。這個閱讀器使應用程序能夠從智能卡接收或是發出命令。在市場上有許多種類的閱讀器,其中最為流行的是serial、PCCard和keyboard模型。(Keyboard模型總是不斷湧現,我們期望大規模的PC廠商能在1998年六月之前直接提供這種產品。)
本文之中使用serial(串行)閱讀器支持設備。一個串行閱讀器與計算機的串口連接。請注意這裡提供的代碼同樣適用於PCCard型的閱讀器;許多的膝上設備內置了PCCard的端口。
每一家廠商都提供了自己的協議用來向閱讀器輸出數據。一旦你可以和閱讀器交換信 息,你就可以用一種協議與智能卡進行通信:即借助於APDU格式與智能卡互通。(關於APDU 格式稍後討論。)如果你想自己選購閱讀器,請參閱參考資料中的“Gemplus smart card readers”。
與閱讀器交換信息的軟件
本文中所列舉的智能卡需要配備一些面向對象的類。它們是:
遵循7816協議通信的ISO命令類
與閱讀器通信的類
將數據轉換為廠商特定格式的類
用於測試應用程序的軟件
智能卡及相關硬件設備
正如本文前面所述,為了創建一個類似於下面例子的應用程序,你必須擁有智能卡的配套硬件和幾塊智能卡。你可以從Gemplus和Schlumberger等公司購買智能卡開發工具。
如果你已經擁有了閱讀器,要想使用它,還要配備下面將要談到的接口類。前面已經講過,在與智能卡通信之前,我們必須首先和閱讀器打交道。而且就象現存的許多種類的智能卡一樣,如今已經有了許許多多的閱讀器。
重要的智能卡標准
智能卡應用程序開發中容易使人迷惑的一點是標准協議問題。在我們的例子中基本上是應用程序與閱讀器通信,然後由閱讀器以一種標准協議與智能卡通信。而這種標准是國際標准化組織的7816協議。
象其它許多新技術一樣,關於智能卡有許許多多令人眼花缭亂的技術標准。對於下面這些標准形成初步的了解之後,你就會大體上掌握智能卡應用程序設計的基本技術要點。當然對於一些系統的特殊標准還須另外掌握。我把這一向、整套標准分成“橫向的”和“縱向的”兩個部分:橫向的標准可以被所有的應用程序所用,而縱向的標准僅僅適用於特定的系統。
橫向的標准
ISO7816--描述到智能卡底層接口標准。這種標准定義智能卡閱讀器和智能卡之間如何傳遞字節流。
PC/SC--定義運行Win3.1/Win95/NT的機器與智能卡之間通信的標准。
OCF--定義從Java應用環境和智能卡之間的通信標准,該標准完全是Java接口。(很快,OCF 將允許開發者向OCF輸出,並執行轉換,這樣開發者再無必要使用PC/SC了。)
JavaCard--描述JavaCard和它所支持的標准。
縱向的標准
Mondex--以智能卡形式實現的數據現金。Mondex不允許存在於卡片之外的現金。
VisaCash--這種借貸卡可以用於跟蹤服務器上的卡。
Proton--另外一種形式的電子貨幣卡
MPCOS-EMV--這是一種通用的智能卡,它允許你實現自己的貨幣或是令牌。
我自己常常感覺到疑惑不解:對於這樣一塊小小的塑料卡片,為什麼會有如此之多的文檔描述其標准,而且開發者又要掌握大量的知識才能去開發它?
因為進行智能卡的開發要求高度的專業知識,所以市場上需要支持Beans的產品,這種產品應該用橫向的標准去實現縱向標准的。這意味著你可以使用各式各樣的橫向標准組合開發出Beans產品來,就象OpenCard一樣,為了實現特定的一個應用程序而采用其它幾家商用標准或是其它的應用程序。
Java小應用或是Java應用程序與智能卡之間的通信
你知道了如何將所有硬件連接在一起。現在我們需要如何使用一些API,這些API可以從應用程序向智能卡閱讀器發出命令。(閱讀器然後與智能卡打交道,作為一個應用程序到智能卡之間的信息傳遞媒介。)智能卡閱讀器移動其與智能卡接觸的金屬尖端傳遞數據。智能卡對數據做出處理之後反還給閱讀器,而閱讀器再將之傳會應用程序。下面的問題是,在這些數據從應用程序流向智能卡之時,它們究竟處於何處?
正如前所述,應用程序與閱讀器通信,而閱讀器將使用上面介紹的標准再與智能卡通信。基本上,隨著智能卡技術的發展,ISO推出了一套智能卡標准。該標准定義了智能卡的機械和電器特性以及與智能卡通信的標准。與ISO該標准相關的文檔列在參考資料當中。不幸的是,ISO沒有能夠提供與閱讀器相互通信的標准。因此,為了向智能卡發出一條命令,首先你要找出智能卡支持的命令集合,將該命令用ISO命令包封裝,然後將這個包再以適合於閱讀器的格式封裝。下面的例程正是完成所有這些瑣事。
Application Procotols Data Units(APDUs)
與智能卡交換信息的基本單元就是APDU包。從應用程序層傳出的命令消息,加上從智能卡返回到應用程序的回應消息均稱為ApplicationProcotolsDataUnits(APDU)。與智能卡和閱讀器的通信以APDU形式實現。一個APDU包可以看作包含完整指令或是回應的數據包。為了提供這樣的功能,在ISO7816規范家族裡有一部分為APDU定義了一個良好的結構。
APDU包含如下域:
命令APDU格式
CLA INS P1 P2 Lc Data Le
回應APDU格式
Data SW1 SW2
下面是一些支持APDU傳輸的類及其功能描述:
Command--封裝命令APDU
Response--封裝回應APDU
ISOCardReader--規定一個接口。每一種設備必須實現該接口
ISOCommand--構成一個ISOCommand並從ISOCardReader接口執行該命令與智能卡通信
Sun開發了JavaElectronicCommerceFramework(JECF),這是對核心Java平台的擴展,它允許開發者輕松快速的開發商用電子應用程序。JECF提供幾種與智能卡通信的類。
我們這裡討論的智能卡分別有一條讀取數據和寫入數據的命令。它是由GemPlus提供的名為GFM卡。你也可以使用其它類型的智能卡,只要它們支持ISO7816標准並且你了解它們的APDU命令格式。當然還要做一點程序工作。GFM卡的內存是以64個比特或是8個字節為單位的。你必須用模8的算法讀寫數據。換句話說,你不能向GFM卡做一次長度為1k連續的寫入。我們這裡提供的Java代碼完成這項功能。一些新的智能卡支持更大單位的讀寫單位。因此,為了寫入字符串“0123456789”,你必須發出兩條適當編址的命令。(是的,智能卡就是這樣難於編程。)當存儲型卡和處理器型卡相互融合時,這種限制也許會消失。
為了讀取上面那條字符串,你應該發出“read”命令。這兩種命令按照APDU的術語被格式化的寫在了下面。在我們的例子中利用了Java讀寫智能卡。下面表中的值示出如何組成一個APDU。在GCM編程指南中定義了APDU的結構。
Location of data Upper Lower
256 0x00 0x00
1023 0x00 0x00
3093 0x00 0x00
“upper”和“lower”是地址的高位和低位字節。舉幾個例子可能會有助於明晰概念。這張 表的upper和lower值提供了存儲數據的確定地址。我們討論過的向GPM896智能卡通信的兩 種方法是:
ISOCommand(0,0xD0,0,upper,lower,8);//Write 8 bytes to the address
ISOCommand(0,0xB0,0,upper,lower,8);//Read 8 bytes from the address
浏覽器與智能卡之間的通信
三個本地接口的存在表明對主要的開發者集團缺乏了解,沒有能夠充分考慮如何向處 於Java開發環境的開發者們提供簡單易於記憶的API。如果所有的銷售商均支持JNI,至少 接口可保持一致,你不必化大量的時間去把接口綁定。當然你必須書寫少量的本地代碼,但 擁有統一的接口還是有價值的。我嘗試了所有三種API,最終發現JNI要比其它的更為一致,並且也最為簡單、易於實現。聯合HotJava一塊使用是最佳的,這樣你可以對與串口通信的 類簽名,然後安全的使用它們,比起另外兩種浏覽器來講麻煩要少的多。Sun最近還宣布幫 助浏覽器公司實現新版JDK/JVM的建議。
前面的討論圍繞如何與一個不支持JDK的硬件設備通信。在下面的幾篇文章裡我們將不再使用“本地接口”,而是使用一個工業標准與智能卡通信。我所選擇的這項標准就是帶有PC/SC橋的OpenCard標准。我將用OpenCard而不是PC/SC書寫應用程序,為什麼呢?
作為開發者,你擁有多種選擇。通常來講這是一件好事,但這也可能導致成本升高和功 能的不一致性,尤其是當你選擇的API並不被多種平台所支持時。例如,你決定用PC/SC標准 書寫支持Win32系列平台的智能卡應用程序。如果你的應用程序是一個顧客使用的應用並 且將用在WebTV之上,你的選擇就是完全錯誤的。顯示器上將會閃動一條信息:“等待WebTV 的Pentium版本。”智能卡是用在Win32桌面和CE單元以外的市場之上的。那麼你應該如何呢 ?使用OpenCard標准,拋棄缺乏一致JNI綁定的Internet Explorer。事實上,我認為將所有API 抽象至單一平台是一個極為明智的選擇。
實用的應用程序
現在應該開始編寫一個更具實用價值的智能卡應用程序了。將來,我們去看醫生,他建議采取特定的治療,我們可能處於如下場面:
醫生要求你的處方卡。
卡被插入閱讀器,醫生查看你以前的處方單。(對於擁有復雜醫療歷史的人來說,可能需要一個專家系統。)
醫生注意到與此同時另外一位醫生在為你治療,而他們倆人開出的藥方有不良反應。因此目前這位醫生就會向智能卡輸入另外一種藥方。(理想化的結果是:智能卡可以將藥方傳給藥房。)
你現在可以將卡取出送到藥房並插入那裡的閱讀器。
藥劑師讀出你的藥房單。
設想藥劑師比醫生更為了解藥性,他認為醫生應該重新考慮所開出的藥方。藥劑師給醫生打電話,而電話號碼包括在智能卡的記錄之中。經過短暫的討論之後,兩個人達成了一致並且更新卡中的記錄。
藥劑師填寫你的藥方單,將院方的計劃信息從卡中取出,並用加密的協議與之通信。
院方驗證你是真正的成員,藥方出自授權醫生之手,並且適時更新卡上的數據。
藥劑師向你收取5美元。
這聽起來是不是一個更為安全的系統?當然是比較目前的系統而言,現在基本上是紙和筆的時代,人充當彼此相分離的計算機系統的連接點。事實上在德國,醫療智能卡已經在使用了。
智能處方卡的優點
比較傳統的處方計劃卡片而言,智能卡到底帶給我們那些好處呢?下面將它們一一羅列出來:
當你要改變卡中的數據、出門旅行或是到新的醫療機構之時,可以獲得以前接受治療時開具的各種處方數據。
在緊急救護或是ER工作人員需要時及時的提供過去的醫療和護理歷史。
詳盡清晰的醫療歷史信息,包括時間、地點和程度等具體信息。
可以有所選擇的將數據提供給特定的人員,當然也可選擇從何處接受數據更新。
為了對以上的優點提供支持,我們需要開發一個應用程序,允許我們用一種安全的方式向智能卡讀寫數據。如果擁有了硬件設備,我們需要將特定的字符串寫入或從智能卡讀出。這些要通過調用CardStrings.java中提供的方法來完成。這些原形類的提供大大便利了對智能卡的編程工作。我們還添加了一個Beans風格的事件處理器,用以通知用戶象智能卡的插入這樣的事件。(這最後一點的改進要感謝JECF的高級高級開發人員DanGuinab。)
考慮如下來自RWString.java的代碼片段:
import java.commerce.smartcards.*; Packages form JECF to support smart cards
import java.commerce.gemplus.*;
import java.commerce.DeviceManager.*;
import java.awt.event.*;
/**
* Read and write Gemplus Memory cards. The following cards
* are supported:
* GFM 4k
*/
public class RWString {
public static void main( String args[]) {
WriteString ws = new WriteString(args);
}
}
class WriteString implements ActionListener {
ISOCardReader isoReader = null;
int portNumber;
String deviceName;
public WriteString(String args[]) {
////////////////////////////////////
// Process the arguments
////////////////////////////////////
for(int i = 0; i
CardStrings類提供了一些用於向智能卡讀寫字符串的方法。這些字符串在智能卡中的 存儲開始於兩個字節的長度域,後面跟著六個空的字節,再後面是字符串數據。
對於那些真正開始智能卡應用程序開發的人來說,必須面對一項挑戰,那就是修改Cardstring 的方法,完成Java對象的讀寫。這種工作要比讀寫字符串來得更為靈活。如果實現了Java對象的寫入,你就不必考慮所要存儲數據的格式。我想從一個大家都說得出的簡單問題入手,即將字符串存儲到智能卡中。
下面的例子是一個完整的數據讀寫應用程序,它適用於GCR400閱讀器和GemPlusGFM 智能卡。僅需一點點的附加工作它就可以支持其它類型的閱讀器。但你不必費心去做那樣的工作,因為下個月裡,我們將提供一個用OpenCard完成這種工作的例子。這種OpenCard將迅速成為這個行業中的標准。
package java.commerce.MemoryCards;
import java.io.IOException;
import java.commerce.smartcards.*;
import java.commerce.gemplus.*;
public class CardStrings {
/**
* Write a String, since the card is modulo 8 and we are
* not using serialized objects -- the first two bytes are the
* length followed by six spare bytes. Strings longer than 4096 - 48
* bits will be truncated.
*/
public static void writeGFMString(String s ,
ISOCardReader isoreader) {
ISOCommand wcmd;
ISOCardinputStream winput;
int upper,lower;
short length = (short)s.length();
// Length of the input string
System.out.println("Length is " + length );
try {
// Write the control section out
wcmd = new ISOCommand(0, 0xd0, 0, 0, 8,0);
// Save the length
wcmd.data.writeShort( length );
System.out.println("Write out the Length");
winput = wcmd.execute(isoreader,
new GemplusReaderFailureHandler());
// Write the String out
int wholeAmount = length/8;
// Groups of 8
int remainder = length % 8;
// Remainder
// Write the String out groups of 8
for ( int l = 1; l <= wholeAmount; l++ ) { System.out.println("Writing 8 bytes at " + (l*8)); upper="(l" * 8 )>> 8;
lower = ( l * 8 ) & 0xff;
wcmd = new ISOCommand(0, 0xd0, upper, lower, 8,0);
int index = ( (l-1) * 8 );
wcmd.data.writeString(s.substring(index),8);
System.out.println("Write out bytes at " + index);
winput = wcmd.execute(isoreader,
new GemplusReaderFailureHandler());
}
// Write the remainder out
upper = ((wholeAmount+1) * 8 ) >> 8;
lower = ((wholeAmount+1) * 8 ) & 0xff;
wcmd = new ISOCommand(0, 0xd0, upper,
lower, remainder,0);
int index = ( wholeAmount * 8 );
wcmd.data.writeString(s.substring(index),remainder);
winput = wcmd.execute(isoreader,
new GemplusReaderFailureHandler());
} catch ( Exception e ) {
System.out.println( "Exception " + e);
e.printStackTrace();
}
}
/**
* Read a String, since the card is modulo 8 and we are
* not using serialized objects -- the first two bytes are the
* length followed by six spare bytes. Strings longer than 4096 - 48
* bits will be truncated.
*/
public static String readGFMString(ISOCardReader isoreader ) {
ISOCommand rcmd;
ISOCardinputStream rinput;
int upper,lower;
short length;
StringBuffer sb = new StringBuffer();
try {
// Read the control section
rcmd = new ISOCommand(0, 0xb0, 0, 0, 0, 8);
// Read the length
rinput = rcmd.execute(isoreader,new GemplusReaderFailureHandler());
length = (short)rinput.readShort();
System.out.println("The length is: " + length);
// Read the String
int wholeAmount = length/8; // Groups of 8
int remainder = length % 8; // Remainder
// Read the String in groups of 8
for ( int l = 1; l <= wholeAmount; l++ ) { System.out.println("Reading 8 bytes at " + (l*8)); upper="(l" * 8 )>> 8;
lower = ( l * 8 ) & 0xff;
rcmd = new ISOCommand(0, 0xb0, upper, lower, 0, 8);
rinput = rcmd.execute(isoreader,new GemplusReaderFailureHandler());
sb.append ( rinput.readString(8) );
System.out.println("String to this point:" + sb.toString());
}
// Read the remainder
upper = ( (wholeAmount+1) * 8 ) >> 8;
lower = ( (wholeAmount+1) * 8 ) & 0xff;
rcmd = new ISOCommand(0, 0xb0, upper, lower, 0, 8);
rinput = rcmd.execute(isoreader,new GemplusReaderFailureHandler());
sb.append ( rinput.readString(remainder) );
System.out.println("String to this point:" + sb.toString());
} catch ( Exception e ) {
System.out.println( "Exception " + e);
e.printStackTrace();
return ( null );
}
return ( sb.toString() );
}
}
結論
本文為這一系列關於智能卡的文章做了一個鋪墊工作。在JavaWorld中的這一系列專題 包括四篇文章。均將由本文作者提供,希望對於你在智能卡方面的應用程序開發有所裨益。 我將利用Java Electronic Commerce Framework(JECF)作為構築使用程序的基礎結構。JECF 為簡化與智能卡的通信提供了幾個類。程序中的大部分代碼和例子來自JECF。你可以從參 考資料中找到下載JECF的方法,那之中已經包括了對智能卡的支持。