參考Java Card & STK Applet Development Guidelines文檔。
遵從以下規則能夠讓你的程序更加標准化和減少出現問題的可能性。
1.盡可能使用API,而不是造輪子。
使用API可以減少重復代碼,而且API可以提供的強大功能。
2.為了確保卡的安全性,PIN碼以及秘鑰不能存儲在基本數組中。可以采取以下方法存儲。
OwnerPIN pin; Pin = new OwnerPIN (tryLimitApplet, maxPINSizeApplet); Pin.update( pinApplet offset, length ) Key rsaPrivateKey; rsaPrivateKey = (RSAPPrivateKey) KeyBuilder.buildKey (KeyBuilder.TYPE_RSA_PRIVATE, KeyBuilder.LENGTH_RSA_512, False); rsaPrivateKey.setExponent(bufferExp, offsetExp, lengthExp); rsaPrivateKey.setModulus(bufferMod, offsetMod, lengthMod);
3.敏感的數據應該在會話開始初始化,會話結束后清除,(該規則適用於全局數組,秘鑰更新和會話對象)。
注意:要重置一個key,需要使用clear()方法,而不是使用0或者其他值覆蓋。因為使用clear()方法,秘鑰的初始化狀態重置為false。
sessionflags = JCSystem.makeTransientByteArray ((short)13,JCSystem.CLEAR_ON_RESET); sessionflags = JCSystem.makeTransientByteArray ((short)13,JCSystem.CLEAR_ON_DESELECT); sessionKey = (DESKey)KeyBuilder.buildKey( KeyBuilder.TYPE_DES_TRANSIENT_RESET, KeyBuilder.LENGTH_DES3_2KEY, false); sessionKey = (DESKey)KeyBuilder.buildKey( KeyBuilder.TYPE_DES_TRANSIENT_DESELECT, KeyBuilder.LENGTH_DES3_2KEY, false);
藍色的代碼表示數據會在卡復位的時候清除,即卡斷電或者重啟的時候。而紅色的代碼表示數據會在該應用斷開選擇的時候清除數據。
4.敏感的數據應該存儲在臨時數據類型中,並且避免存儲在APDU的buffer中。
sessionflags = JCSystem.makeTransientByteArray ((short)13,JCSystem.CLEAR_ON_DESELECT);
5.保護你的敏感數據,防止回滾攻擊(Rollback attack)
回滾(Rollback)指的是程序或數據處理錯誤,將程序或數據恢復到上一次正確狀態的行為。回滾包括程序回滾和數據回滾等類型。
當你的代碼正在處於transaction(事務,程序執行單元)中突然斷電,卡狀態將會回滾到舊值,攻擊者可以利用這點進行攻擊。當處理敏感數據的時候,一定要引入計數管理器,以應對處理敏感數據時的回滾攻擊。
規則:假如預防機制需要依靠計數器的話,並且該機制需要被管理,需要按照以下行為進行執行。
(1)檢查計數器的值,如果被置為0的話,返回錯誤;
(2)原子操作(不可打斷)遞減計數器的值;
(3)驗證這個代碼不處於transaction(事務,程序執行單元);
(4)運行“try”函數;
(5)假如“try”運行成功,原子操作增加計數器的值。
6.所有初始化的buffer,一定要定義為靜態數據。
初始化數組定義為靜態數據有以下好處:
(1)節省執行時間,在初始化的過程中;
(2)節省代碼空間;
(3)在初始化的過程中防止錯誤(transction 緩存滿了)。
7.全局變量和局部變量
在javacard應用程序中有許多變量:實例變量(全局變量),在對象初始化的時候創建;局部變量,他只能在申明他們的函數或者快中使用。
一些基本的規則:
(1)當能夠使用靜態變量的時候,盡量不要使用全局變量。他可以減少類實例的冗余。
(2)盡量少使用全局變量。
(3)不可變的常量必須申明為static final。
(4)不要申明太多參數,在一個方法中。非常消耗內存(RAM)。
(5)局部變量的訪問速度比全局變量快:
Working variables(像循環計數器,臨時存儲變量),一定要在方法中申明為局部變量而不是類中的全局變量,這樣可以防止超過NVM的容量;
Working buffers應該盡可能申明為transient 數組(可以鎖定在RAM區域),這樣可以避免超過NVM容量限制。假如缺少RAM資源,一定要注意 Working buffers的更新頻率(更新時間點以及更新次數)。
RAM空間應該需要時才使用,因為RAM是一個稀缺資源。
(6)方法參數分解,使用全局變量代替參數,並且傳遞一個對象而不是一個參數列表。
(7)最大化局部變量使用效率,盡量復用局部變量;
盡量減少類和對象的數量以及大小,可以有助於解決bug,增強程序可讀性。
(8)禁止存儲臨時入口對象的引用。
在javacard標准中指定了一些JCRE的entry point objects(EPO)入口對象。在這些EPOS中,這些臨時的EPO是禁止存儲在類變量、全局變量或則數組陣列中。JCRE檢測和禁止將這些對象的引用存儲在程序中,作為應用防火牆的一部分。如果這么做的話系統會拋出安全異常。
一些主要的臨時EPO:APDU, EnvelopeHandler, ProactiveHandler, EnvelopeResponseHandler, ProactiveResponseHandler 。
紅色標注的代碼有問題,因為它使用全局變量來存儲EPO。
public class exampleD8 extends Applet implements ToolkitInterface { byte[] apduBuffer; byte[] envHandler; void process(APDU apdu){ byte[] buffer; apduBuffer = apdu.getBuffer(); buffer = apdu.getBuffer(); } void processToolkit(byte event){ byte[] handler; envHandler = EnvelopeHandler.getTheHandler(); handler = EnvelopeHandler.getTheHandler(); }
(9)方法的代碼不要太復雜。
盡量保持方法的代碼簡單,最大不要超過300個字節。較復雜的方法最好拆分成幾個較小的方法。方法的代碼少可以提高可讀性,並且更容易調試bug。
(10)確保每個類方法的數量不要超過256個。
每個類的方法數量限制在256個之內,更多的方法意味着更大的常量池。