【JAVACard】智能卡 電子錢包

一、需求分析

        設計一個電子錢包小程序,要求至少實現電子錢包安裝、選擇與撤銷選擇、存款、借款、獲取身錢包餘額、身份驗證的功能。

身份驗證可通過PIN碼來設置。

        存款、借款、消費可以通過設置一餘額變量Balance,通過讀取相應操作指令,對變量Balance進行加、減、讀取值來實現存款、借款、消費功能。

        對不符合規定的操作,拋出異常來中斷操作。

                                       

二、APDU

C-APDU

 

CLA

INS

P1

P2

Lc

數據

Le

Verify

90

20

00

00

04

01020304

00

Credit

90

30

00

00

01

存款數值

00

Debit

90

40

00

00

01

借款數值

00

Get Balance

90

50

00

00

00

——

02 

 

 

 

 

 

 

R-APDU

指令

說明

數據

SW1

SW2

Verify

驗證成功

——

90

00

 

PIN值錯誤

——

63

00

Credit

存款成功

——

90

00

 

需要驗證身份

——

63

01

 

單次存款超出最高限制

——

6A

83

 

存款數值超過最大數值

——

6A

84

Debit

借款成功

——

90

00

 

需要身份驗證

——

63

01

 

單次借款超出最高限制

——

6A

83

 

餘額爲負值

——

6A

85

Get Balance

返回餘額數值

(餘額數據)

90

00

注:

  • 初始時設定PIN驗證碼爲01020304,身份驗證時發送數據01020304正確驗證身份。
  • 存款、借款時,設定單次交易數值不超過0x7F。
  • 餘額總量限定爲0x7530,且餘額不能爲負值。

 

三、測試

        採用源碼爲《JAVA智能卡原理與應用開發》一書中示例

  • 電子錢包安裝與選擇:

       電子錢包安裝選擇成功。

  • 未驗證身份就存款:

       拋出異常:0x6301,pin碼身份驗證未通過。

 

  • 身份驗證時輸入錯誤PIN碼:

       拋出異常:0x6300,身份驗證失敗。

 

  • 身份驗證時輸入正確PIN碼:

       身份驗證成功。

 

  • 查詢此時餘額:

       餘額此時爲0。

 

  • 單次存款超0x7F:

拋出異常:0x6A83,超出單次交易額最大值。

 

  • 查詢此時餘額:

       餘額仍爲0。

 

  • 存款,金額0x70,並查詢餘額:

       餘額爲存款金額0x70 = 112。

 

  • 借款,金額大於餘額0x70,並查詢餘額:

       拋出異常:0x6A85,無效餘額,借款失敗;

       餘額不變,仍爲0x70。

 

  • 借款,金額0x20,並查詢餘額:

       借款成功,餘額0x50。

 

  • 存款,金額0x75,並查詢餘額:

       存款成功,餘額0xC5

 

  • 存款,金額0x70,並查詢餘額:

       存款成功,餘額0x0135

 

四、源代碼

/**
 * 
 */
package wallet;

import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import javacard.framework.APDU;


public class WALLET extends Applet {
	
	final static byte Wallet_CLA = (byte)0x80;
	//final static byte VERIFY = (byte)0x20;
	final static byte CREDIT = (byte)0x30;
	final static byte DEBIT = (byte)0x40;
	final static byte GET_BALANCE = (byte)0x50;
	final static short MAX_BALANCE = 0x7530;
	final static byte MAX_TRANSACTION_AMOUNT = 127;
	
	//final static byte PIN_TRY_LIMIT = (byte)0x03;
	//final static byte MAX_PIN_SIZE = (byte)0x08;
	//final static short SW_VERIFICATION_FAILED = 0x6300;
	//final static short SW_PIN_VERIFICATION_REQUIRED = 0x6301;
	final static short SW_INVALID_TRANSACTION_AMOOUNT = 0x6A83;
	final static short SW_EXCEED_MAXIMUM_BALANCE = 0x6A84;
	final static short SW_NEGATIVE_BALANCE = 0x6A85;
	
	//OwnerPIN pin;
	short balance;
	
	private WALLET(byte[] bArray, short bOffset, byte bLength)
	{
		//pin = new OwnerPIN(PIN_TRY_LIMIT, MAX_PIN_SIZE);
		
		byte iLen = bArray[bOffset];
		bOffset = (short)(bOffset + iLen + 1);
		
		byte cLen = bArray[bOffset];
		bOffset = (short)(bOffset + cLen + 1);
		
		byte aLen = bArray[bOffset];
		
		//pin.update(bArray, (short)(bOffset + 1), aLen);
		register();
	}
	
	
	public static void install(byte[] bArray, short bOffset, byte bLength) {
		// GP-compliant JavaCard applet registration
		//new WALLET().register(bArray, (short) (bOffset + 1), bArray[bOffset]);
		new WALLET(bArray, bOffset, bLength);
	}
	/*
	public boolean select()
	{
		if(pin.getTriesRemaining() == 0)
			return false;
			
		return true;
	}
	*/
	/*
	public void deselect()
	{
		pin.reset();
	}
	*/
	public void process(APDU apdu) 
	{
		/*
		// Good practice: Return 9000 on SELECT
		if (selectingApplet()) {
			return;
		}
		*/

		byte[] buffer = apdu.getBuffer();
		
		buffer[ISO7816.OFFSET_CLA] = (byte)(buffer[ISO7816.OFFSET_CLA] & (byte)0xFC);
		
		if((buffer[ISO7816.OFFSET_CLA] == 0) &&
			(buffer[ISO7816.OFFSET_INS] == (byte)(0xA4)))
			return;
		
		if(buffer[ISO7816.OFFSET_CLA] != Wallet_CLA)
			ISOException.throwIt(ISO7816.SW_CLA_NOT_SUPPORTED);
		
		switch (buffer[ISO7816.OFFSET_INS]) {
		case GET_BALANCE:
			getBalance(apdu);
			return;
		
		case DEBIT:
			debit(apdu);
			return;
			
		case CREDIT:
			credit(apdu);
			return;
		/*	
		case VERIFY:
			verify(apdu);
			return;
		*/
		default:
			// good practice: If you don't know the INStruction, say so:
			ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
		}
	}
	
	private void credit(APDU apdu)
	{
		/*
		if(!pin.isValidated())
			ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
		*/
		byte[] buffer = apdu.getBuffer();
		
		byte numBytes = buffer[ISO7816.OFFSET_LC];
		
		byte byteRead = (byte)(apdu.setIncomingAndReceive());
		
		
		if((numBytes != 1) || (byteRead != 1))
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		byte creditAmount = buffer[ISO7816.OFFSET_CDATA];
		
		if((short)(balance + creditAmount) > MAX_BALANCE)
			ISOException.throwIt(SW_EXCEED_MAXIMUM_BALANCE);
		
		balance = (short)(balance + creditAmount);		
		
	}
	
	private void debit(APDU apdu)
	{
		/*
		if(!pin.isValidated())
			ISOException.throwIt(SW_PIN_VERIFICATION_REQUIRED);
		*/
		byte[] buffer = apdu.getBuffer();
		
		byte numBytes = buffer[ISO7816.OFFSET_LC];
		
		byte byteRead = (byte)(apdu.setIncomingAndReceive());
		
		if((numBytes != 1) || (byteRead != 1))
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		byte debitAmount = buffer[ISO7816.OFFSET_CDATA];
		
		if((debitAmount > MAX_TRANSACTION_AMOUNT) || (debitAmount < 0))
			ISOException.throwIt(SW_INVALID_TRANSACTION_AMOOUNT);
		
		if((short)(balance - debitAmount) < (short)0)
			ISOException.throwIt(SW_NEGATIVE_BALANCE);
		
		balance = (short)(balance - debitAmount);
			
	}
	
	private void getBalance(APDU apdu)
	{
		byte[] buffer = apdu.getBuffer();
		
		short le = apdu.setOutgoing();
		
		if(le < 2)
			ISOException.throwIt(ISO7816.SW_WRONG_LENGTH);
		
		apdu.setOutgoingLength((byte)2);
		
		buffer[0] = (byte)(balance >> 8);
		buffer[1] = (byte)(balance & 0xFF);
		
		apdu.sendBytes((short)0, (short)2);
		
	}
	/*
	private void verify(APDU apdu)
	{
		byte[] buffer = apdu.getBuffer();
		
		byte byteRead = (byte)(apdu.setIncomingAndReceive());
		
		if(pin.check(buffer, ISO7816.OFFSET_CDATA, byteRead) == false)
			ISOException.throwIt(SW_VERIFICATION_FAILED);
		
	}
	*/
}

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章