/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package com.sun.midp.io.j2me.jcrmi;
import java.io.*;
import java.rmi.Remote;
import java.rmi.RemoteException;
import javax.microedition.io.Connection;
import javax.microedition.io.StreamConnection;
import javax.microedition.jcrmi.JavaCardRMIConnection;
import javax.microedition.jcrmi.RemoteStub;
import com.sun.cldc.io.ConnectionBaseInterface;
import com.sun.midp.crypto.MessageDigest;
import com.sun.midp.crypto.GeneralSecurityException;
import com.sun.midp.io.j2me.apdu.APDUManager;
import com.sun.midp.io.j2me.apdu.Handle;
import com.sun.midp.midlet.MIDletStateHandler;
import com.sun.midp.midlet.MIDletSuite;
import com.sun.midp.midletsuite.MIDletSuiteImpl;
import com.sun.midp.security.ImplicitlyTrustedClass;
import com.sun.midp.security.SecurityToken;
import com.sun.midp.security.Permissions;
import com.sun.satsa.acl.ACLPermissions;
import com.sun.satsa.acl.AccessControlManager;
import com.sun.satsa.acl.JCRMIPermissions;
import com.sun.satsa.util.Utils;
import com.sun.satsa.security.SecurityInitializer;
import javacard.framework.*;
import javacard.framework.service.ServiceException;
/**
* JCRMI connection to card application.
*/
public class Protocol
implements JavaCardRMIConnection, ConnectionBaseInterface,
StreamConnection {
/**
* Inner class to request security token from SecurityInitializer.
* SecurityInitializer should be able to check this inner class name.
*/
static private class SecurityTrusted
implements ImplicitlyTrustedClass {};
/** This class has a different security domain than the MIDlet suite */
private static SecurityToken classSecurityToken =
SecurityInitializer.requestToken(new SecurityTrusted());
/**
* Size of APDU buffer.
*/
private static final int APDUBufferSize = 255;
/**
* Stub object for initial remote reference.
*/
private Remote initialReference;
/**
* Reference object for initial remote reference.
*/
private Reference internalReference;
/**
* Remote reference uses this buffer to prepare INVOKE APDU command.
*/
private byte[] APDUBuffer = new byte[APDUBufferSize];
/**
* Current offset in <code>APDUBuffer</code> buffer for
* <code>write</code> methods.
*/
private int offset;
/**
* Response APDU data.
*/
private byte[] response;
/**
* Current offset in <code>response</code> buffer for
* <code>read</code> methods.
*/
private int r_offset;
/**
* SHA-1 message digest object used by this connection.
*/
private MessageDigest SHA;
/** The current APDU connection handle. */
private Handle h;
/**
* A flag to indicate if connection is open or not.
*/
private boolean connectionOpen;
/**
* This object verifies access rights of MIDlet.
*/
private JCRMIPermissions verifier;
/**
* Connector uses this method to initialize the connection object.
* This method establishes APDU connection with card application,
* obtains FCI information and creates stub for initial remote
* reference.
* @param name the URL for the connection without protocol name
* @param mode the access mode (Ignored)
* @param timeouts a flag to indicate that the caller wants timeout
* exceptions. Ignored
* @return this connection
* @throws IOException if the connection can not be initialized
* @throws RemoteException if initial remote reference object can not be
* created
* @throws SecurityException if access is restricted by ACL
*/
public Connection openPrim(String name, int mode, boolean timeouts)
throws IOException {
MIDletSuite midletSuite =
MIDletStateHandler.getMidletStateHandler().getMIDletSuite();
try {
midletSuite.checkForPermission(Permissions.JCRMI_CONNECTION, null);
} catch (InterruptedException ie) {
throw new InterruptedIOException(
"Interrupted while trying to ask the user permission");
}
int slotInfo;
// parse URL string for slot number and AID
try {
slotInfo = parseURL(name);
} catch (NullPointerException npe) {
throw new IllegalArgumentException("Invalid URL");
} catch (IndexOutOfBoundsException iobe) {
throw new IllegalArgumentException("Invalid URL");
} catch (IllegalArgumentException iae) {
throw new IllegalArgumentException("Invalid URL");
}
APDUManager.checkSlotNumber(slotInfo);
// get card application selected
APDUManager.initACL(slotInfo, classSecurityToken);
verifier = AccessControlManager
.getJCRMIPermissions(slotInfo,
APDUBuffer,
((MIDletSuiteImpl)midletSuite)
.getInstallInfo().getCA());
h = APDUManager.selectApplication(APDUBuffer, slotInfo,
classSecurityToken);
connectionOpen = true;
// parse FCI
byte[] FCI = h.getFCI();
byte invokeINS;
try {
// fci_tag or application_data_tag
if (FCI[0] != 0x6f || FCI[2] != 0x6e) {
throw new RemoteException("Incorrect FCI format");
}
int index = 4;
// skip unnecessary tag/value pairs
while (FCI[index] != 0x5E) { // jc_rmi_data_tag
index += 2 + (FCI[index] & 0xff);
}
index += 4;
invokeINS = FCI[index++];
if (FCI[index++] != (byte) 0x81) { // normal_tag
throw new RemoteException("Incorrect FCI format");
}
response = FCI;
r_offset = index;
initialReference = createStub();
if (initialReference == null) {
throw new RemoteException();
}
} catch (RemoteException e) {
close();
throw e;
} catch (Throwable e) {
close();
throw new RemoteException("Can't create initial reference");
}
offset = 0;
putByte(0x80 | h.channel);
putByte(invokeINS);
putShort(0x0202);
try {
SHA = MessageDigest.getInstance("SHA-1");
} catch (GeneralSecurityException e) {
// Ignore this exception
}
return this;
}
/**
* Closes the connection.
* @throws IOException If an I/O error occurs
*/
public void close() throws IOException {
synchronized (APDUBuffer) {
if (connectionOpen) {
connectionOpen = false;
APDUManager.closeConnection(h);
}
}
}
/**
* Returns the stub object for an initial remote reference.
* @return the initial remote reference
*/
public java.rmi.Remote getInitialReference() {
return initialReference;
}
/**
* Parses the URL to get the slot number and AID.
* Prepares SELECT APDU in APDUBuffer.
* @param URL contains the URL from which
* the slot information is to be extracted
* @return slot number for this connection
*/
private int parseURL(String URL) {
int slotIndex = URL.indexOf(":") + 1;
int AIDIndex = URL.indexOf(";AID=");
int slotInfo = (AIDIndex == slotIndex) ? 0 :
Integer.parseInt(URL.substring(slotIndex, AIDIndex), 16);
// prepare selection APDU
offset = 0;
// IMPL_NOTE: if the card does not support JC2.2 you must use: putInt(0x00a40400);
putInt(0x00a40410); // selection APDU header
offset++; // length
// parse for AID
int AIDLength = APDUManager.parseDottedBytes(
URL.substring(AIDIndex + 5), APDUBuffer, offset);
if (AIDLength < 5 || AIDLength > 16)
throw new IllegalArgumentException();
APDUBuffer[4] = (byte) AIDLength;
offset += AIDLength;
putByte(255);
return slotInfo;
}
/**
* Writes <code>param</code> value into <code>APDUBuffer</code>.
* @param param the value to be written
*/
private void putByte(int param) {
APDUBuffer[offset++] = (byte) param;
}
/**
* Writes <code>param</code> value into <code>APDUBuffer</code>.
* @param param the value to be written
*/
private void putShort(int param) {
putByte(param >> 8);
putByte(param);
}
/**
* Writes <code>param</code> value into <code>APDUBuffer</code>.
* @param param the value to be written
*/
private void putInt(int param) {
putByte(param >> 24);
putByte(param >> 16);
putByte(param >> 8);
putByte(param);
}
/**
* Reads one byte from response APDU, zero-extends it to type
* <code>int</code>, and returns the result.
* @return the unsigned 8-bit value
*/
private int getByte() {
return response[r_offset++] & 0xff;
}
/**
* Reads <code>short</code> value from response APDU
* @return <code>short</code> value
*/
private short getShort() {
return (short) (getByte() << 8 | getByte());
}
/**
* Reads <code>int</code> value from response APDU.
* @return <code>int</code> value
*/
private int getInt() {
return (getByte() << 24) |
(getByte() << 16) |
(getByte() << 8) |
getByte();
}
/**
* Reads <code>String</code> value from response APDU.
* @return <code>String</code> value
*/
private String getString() throws RemoteException {
int len = getByte();
if (len == 0) {
return null;
}
String S;
try {
S = new String(response, r_offset, len, Utils.utf8);
} catch (UnsupportedEncodingException e) {
throw new RemoteException("UTF-8 encoding is not supported");
}
r_offset += len;
return S;
}
/**
* Creates stub using remote reference descriptor in
* <code>response</code> buffer at <code>r_offset </code> offset.
* @return new stub object
* @throws java.rmi.RemoteException if an error occurs
*/
private Remote createStub() throws RemoteException {
short objectID = getShort();
if (objectID == (short) 0xffff) {
return null; // null reference returned
}
// hash modifier
String hashModifier = getString();
// list of interfaces
int count = getByte();
Class stubClass = null;
String className = null;
String packageName = null;
Class[] classes = new Class[count];
for (int i = 0; i < count; i++) {
String name = getString();
if (name != null) {
packageName = name.replace('/', '.');
}
name = packageName + "." + getString();
try {
classes[i] = Class.forName(name);
} catch (ClassNotFoundException e) {
throw new RemoteException("Class not found", e);
}
if (stubClass == null ||
stubClass.isAssignableFrom(classes[i])) {
stubClass = classes[i];
className = name;
continue;
}
}
for (int i = 0; i < count; i++) {
if (! classes[i].isAssignableFrom(stubClass)) {
throw new RemoteException(
"Incorrect hierarchy in descriptor");
}
}
RemoteStub stub;
try {
stub = (RemoteStub)
(Class.forName(className + "_Stub").newInstance());
} catch (ClassNotFoundException cnfe) {
throw new RemoteException("Can't find stub class", cnfe);
} catch (IllegalAccessException iae) {
throw new RemoteException("Access to stub class denied", iae);
} catch (InstantiationException ie) {
throw new RemoteException("Can't create stub object", ie);
}
Reference r = new Reference(this, objectID, hashModifier, className);
if (internalReference == null) {
internalReference = r;
}
stub.setRef(r);
return (Remote) stub;
}
/**
* Constant for JCRMI protocol.
*/
private static final byte NormalTag = (byte) 0x81;
/**
* Constant for JCRMI protocol.
*/
private static final byte ExactExceptionTag = (byte) 0x82;
/**
* Constant for JCRMI protocol.
*/
private static final byte ExceptionSubclassTag = (byte) 0x83;
/**
* Constant for JCRMI protocol.
*/
private static final byte ErrorTag = (byte) 0x99;
/**
* Invokes a remote method.
*
* The remote method invoked on the card can throw an exception to
* signal that an unexpected condition has been detected.<p>
*
* If the exception thrown on the card is an exception defined in
* the Java Card 2.2 API, then the same exception is thrown to the
* stub method. The client can access the reason code associated
* with Java Card-specific exceptions using the standard
* <code>getReason()</code> method.<p>
*
* If the exception thrown on the card is a subclass of an exception
* defined in the Java Card 2.2 API, then the closest exception defined
* in the API (along with the reason code, if applicable) is
* thrown to the stub method. The detail message string of the
* exception object may indicate that exception subclass was thrown
* on the card.<p>
*
* Apart from the exceptions thrown by the remote method itself,
* errors during communication, marshalling, protocol handling,
* unmarshalling, stub object instantiation, and so on, related
* to the JCRMI method invocation, results in a
* <code>RemoteException</code> being thrown to the stub method.
*
* @param ref handle for remote object
* @param method simple (not fully qualified) name of the method
* followed by the method descriptor. Representation of a
* method descriptor is the same as that described in The
* Java Virtual Machine Specification (§ 4.3.3)
* @param params the parameter list
* @return result of remote method invocation
* @exception java.lang.Exception if any exception occurs during
* the remote method invocation
*/
Object invoke(Reference ref, String method, Object[] params)
throws Exception {
if (! connectionOpen) {
throw new RemoteException("Connection is closed.");
}
verifier.checkPermission(ref.getClassName(), method);
synchronized (APDUBuffer) {
try {
marshal(ref, method, params);
} catch (RuntimeException ai) {
throw new RemoteException("Error marshalling parameters");
}
try {
response = APDUManager.exchangeAPDU(h, APDUBuffer);
} catch (IOException e) {
throw new RemoteException("IO error", e);
}
r_offset = response.length - 2;
if (getShort() != (short) 0x9000) {
throw new RemoteException("Incorrect status word");
}
r_offset = 0;
byte tag = (byte) getByte();
Exception ex = null;
Object result = null;
try {
if (tag == ExactExceptionTag || tag == ExceptionSubclassTag) {
ex = parseException(tag);
} else
if (tag == ErrorTag) {
ex = parseError();
} else
if (tag == NormalTag) {
result = parseResult(method);
} else {
throw new RemoteException("Incorrect tag value: " + tag);
}
getShort(); // status word
} catch (RuntimeException e) {
throw new RemoteException("Incorrect response structure");
}
if (ex != null) {
throw ex;
}
return result;
}
}
/**
* Prepares INVOKE APDU.
* @param ref handle for remote object
* @param method simple (not fully qualified) name of the method
* followed by the method descriptor. Representation of a
* method descriptor is the same as that described in The
* Java Virtual Machine Specification (§ 4.3.3)
* @param params the parameter list
* @throws RemoteException if an error occurs
*/
private void marshal(Reference ref, String method, Object[] params)
throws RemoteException {
offset = 5;
putShort(ref.getObjectID());
String hashString = method;
String hashModifier = ref.getHashModifier();
if (hashModifier != null) {
hashString = hashModifier + hashString;
}
byte[] buf = Utils.stringToBytes(hashString);
SHA.reset();
SHA.update(buf, 0, buf.length);
try {
SHA.digest(APDUBuffer, offset, SHA.getDigestLength());
} catch (GeneralSecurityException e) {
throw new RemoteException("SHA1 error");
}
offset += 2;
if (params != null) {
for (int i = 0; i < params.length; i++) {
Object obj = params[i];
if (obj == null) {
putByte(0xff);
continue;
}
if (obj instanceof Byte) {
putByte(((Byte)obj).byteValue());
continue;
}
if (obj instanceof Boolean) {
putByte(((Boolean)obj).booleanValue() ? 1 : 0);
continue;
}
if (obj instanceof Short) {
putShort(((Short)obj).shortValue());
continue;
}
if (obj instanceof Integer) {
putInt(((Integer)obj).intValue());
continue;
}
if (obj instanceof byte[]) {
byte[] param = (byte[]) obj;
putByte(param.length);
for (int k = 0; k < param.length; k++) {
putByte(param[k]);
}
continue;
}
if (obj instanceof boolean[]) {
boolean[] param = (boolean[]) obj;
putByte(param.length);
for (int k = 0; k < param.length; k++) {
putByte(param[k] ? 1 : 0);
}
continue;
}
if (obj instanceof short[]) {
short[] param = (short[]) obj;
putByte(param.length);
for (int k = 0; k < param.length; k++) {
putShort(param[k]);
}
continue;
}
if (obj instanceof int[]) {
int[] param = (int[]) obj;
putByte(param.length);
for (int k = 0; k < param.length; k++) {
putInt(param[k]);
}
continue;
}
throw new RemoteException("Incorrect parameter type");
}
}
APDUBuffer[4] = (byte) (offset - 5);
putByte(255);
}
/**
* Parses response containing data about exception.
* @param tag type tag of exception
* @return the exception to be thrown to stub
* @throws RemoteException if an error occurs during parsing
*/
private Exception parseException(byte tag) throws RemoteException {
String message = (tag == ExactExceptionTag) ?
"Exception is thrown on card" :
"Exception subclass is thrown on card";
byte type = (byte) getByte();
short reason = getShort();
switch (type) {
case 0x00:
return new Exception(message + ": java.lang.Throwable");
case 0x01:
return new ArithmeticException(message);
case 0x02:
return new ArrayIndexOutOfBoundsException(message);
case 0x03:
return new ArrayStoreException(message);
case 0x04:
return new ClassCastException(message);
case 0x05:
return new Exception(message);
case 0x06:
return new IndexOutOfBoundsException(message);
case 0x07:
return new NegativeArraySizeException(message);
case 0x08:
return new NullPointerException(message);
case 0x09:
return new RuntimeException(message);
case 0x0A:
return new SecurityException(message);
case 0x0B:
return new IOException(message);
case 0x0C:
return new RemoteException(message);
case 0x20:
return new APDUException(reason);
case 0x21:
return new CardException(reason);
case 0x22:
return new CardRuntimeException(reason);
case 0x23:
return new ISOException(reason);
case 0x24:
return new PINException(reason);
case 0x25:
return new SystemException(reason);
case 0x26:
return new TransactionException(reason);
case 0x27:
return new UserException(reason);
case 0x30:
return new javacard.security.CryptoException(reason);
case 0x40:
return new ServiceException(reason);
default:
throw new RemoteException(
"Unknown exception is thrown on card");
}
}
/**
* Parses response containing data about error.
* @return the exception to be thrown to stub
* @throws RemoteException if error code is unknown
*/
private RemoteException parseError() throws RemoteException {
short code = getShort();
switch (code) {
case 1: return new RemoteException("Object not exported");
case 2: return new RemoteException("Method not found");
case 3: return new RemoteException("Signature mismatch");
case 4: return new RemoteException("Out of parameter resources");
case 5: return new RemoteException("Out of response resources");
case 6: return new RemoteException(
"Protocol error reported by the card");
default:
throw new RemoteException("Error reported by the card: " +
code);
}
}
/**
* Parses the normal JCRMI response.
* @param method method name and signature
* @return the value returned by method
* @throws RemoteException if an error occurs during parsing
*/
private Object parseResult(String method) throws RemoteException {
int index = method.indexOf(')') + 1;
if (index != -1) {
switch (method.charAt(index)) {
case 'V': return null;
case 'B': return new Byte((byte) getByte());
case 'Z': return new Boolean(getByte() == 1 ? true : false);
case 'S': return new Short(getShort());
case 'I': return new Integer(getInt());
case 'L': return createStub();
case '[': {
int len = getByte();
if (len == 255)
return null;
switch (method.charAt(index + 1)) {
case 'B': {
byte[] data = new byte[len];
for (int i = 0; i < len; i++) {
data[i] = (byte) getByte();
}
return data;
}
case 'Z': {
boolean[] data = new boolean[len];
for (int i = 0; i < len; i++) {
data[i] = (getByte() == 1 ? true : false);
}
return data;
}
case 'S': {
short[] data = new short[len];
for (int i = 0; i < len; i++) {
data[i] = getShort();
}
return data;
}
case 'I': {
int[] data = new int[len];
for (int i = 0; i < len; i++) {
data[i] = getInt();
}
return data;
}
}
}
}
}
throw new RemoteException("Incorrect method signature");
}
/**
* Returns the card session identifier for this connection.
* @return the card session identifier
*/
int getCardSessionId() {
return h.cardSessionId;
}
/**
* Returns the flag that indicates if connection is open.
* @return true if the connection is open
*/
boolean isOpened() {
return connectionOpen;
}
/**
* Open and return an input stream for a connection.
* This method always throw
* <code>IllegalArgumentException</code>.
* @return An input stream
* @exception IllegalArgumentException is thrown for all requests
*/
public InputStream openInputStream() {
throw new IllegalArgumentException("Not supported");
}
/**
* Open and return a data input stream for a connection.
* This method always throw
* <code>IllegalArgumentException</code>.
* @return An input stream
* @exception IllegalArgumentException is thrown for all requests
*/
public DataInputStream openDataInputStream() {
throw new IllegalArgumentException("Not supported");
}
/**
* Open and return an output stream for a connection.
* This method always throw
* <code>IllegalArgumentException</code>.
* @return An output stream
* @exception IllegalArgumentException is thrown for all requests
*/
public OutputStream openOutputStream() {
throw new IllegalArgumentException("Not supported");
}
/**
* Open and return a data output stream for a connection.
* This method always throw
* <code>IllegalArgumentException</code>.
* @return An output stream
* @exception IllegalArgumentException is thrown for all requests
*/
public DataOutputStream openDataOutputStream() {
throw new IllegalArgumentException("Not supported");
}
/**
* A call to enterPin method pops up a UI that requests the PIN
* from the user. The pinID field indicates which PIN must be
* requested from the user. The user can either cancel the request
* or continue. If the user enters the PIN and chooses to continue,
* The implementation is responsible for
* presenting the PIN entered by the user to the card for verification.
* @param pinID the type of PIN the implementation is suppose to prompt
* the user to enter.
* @return PINENTRY_CANCELLED if the user cancelled the PIN entry
* request or the value returned by the remote method.
* @exception java.rmi.RemoteException is thrown if the PIN could
* not be communicated to the card or an exception is thrown
* by the card in response to the PIN entry.
* @exception SecurityException is thrown if the J2ME application does
* not have appropriate rights to ask for PIN verification.
*/
public short enterPin(int pinID) throws java.rmi.RemoteException {
return doEnterPin(pinID, 0, ACLPermissions.CMD_VERIFY);
}
/**
* A call to <code>changePin</code> method pops up a UI that requests
* the user for an old or existing PIN value and the new PIN value to
* to change the value of the PIN. The pinID field indicates which PIN is
* to be changed. The user can either cancel the request
* or continue. If the user enters the PIN values and chooses to continue
* the implementation is responsible for presenting the
* the old and new values of the PIN to the card.
* @param pinID the type of PIN the implementation is suppose to prompt
* the user to change.
* @return PINENTRY_CANCELLED if the user cancelled the PIN entry
* request or the value returned by the remote method.
* @exception java.rmi.RemoteException is thrown if the PIN could
* not be communicated to the card or an exception is thrown
* by the card in response to the PIN entry.
* @exception SecurityException is thrown if the J2ME application does
* not have appropriate rights to ask for changing the PIN value.
*/
public short changePin(int pinID) throws RemoteException {
return doEnterPin(pinID, 0, ACLPermissions.CMD_CHANGE);
}
/**
* A call to <code>disablePin</code> method pops up a UI that requests
* the user to enter the value for the PIN that is to be disabled.
* The pinID field
* indicates which PIN is to be disabled. The user can
* either cancel the request
* or continue. If the user enters the PIN and chooses to continue the
* implementation is responsible
* for presenting the PIN value to the card to disable PIN.
* @param pinID the type of PIN the implementation is required to prompt
* the user to enter.
* @return PINENTRY_CANCELLED if the user cancelled the PIN entry
* request or the value returned by the remote method.
* @exception java.rmi.RemoteException is thrown if the PIN could
* not be communicated to the card or an exception is thrown
* by the card in response to the PIN entry.
* @exception SecurityException is thrown if the J2ME application does
* not have appropriate rights to ask for disabling the PIN.
*/
public short disablePin(int pinID) throws RemoteException {
return doEnterPin(pinID, 0, ACLPermissions.CMD_DISABLE);
}
/**
* A call to <code>enablePin</code> method pops up a UI that requests
* the user to enter the value for the PIN that is to be enabled.
* The pinID field
* indicates which PIN is to be enabled. The user can
* either cancel the request
* or continue. If the user enters the PIN and chooses to continue the
* implementation is responsible
* for presenting the PIN value to the card for enabling the PIN.
* @param pinID the type of PIN the implementation is required to prompt
* the user to enter.
* @return PINENTRY_CANCELLED if the user cancelled the PIN entry
* request or the value returned by the remote method.
* @exception java.rmi.RemoteException is thrown if the PIN could
* not be communicated to the card or an exception is thrown
* by the card in response to the PIN entry.
* @exception SecurityException is thrown if the J2ME application does
* not have appropriate rights to ask for enabling the PIN.
*/
public short enablePin(int pinID) throws RemoteException {
return doEnterPin(pinID, 0, ACLPermissions.CMD_ENABLE);
}
/**
* This is a high-level method that lets the J2ME application
* ask the user to enter the value for an unblocking PIN,
* and the new value for the blocked PIN and send
* these to the card.
* A call to <code>unblockPin</code> method pops up a UI that requests
* the user to enter the value for the unblocking PIN and the
* new value for the blocked PIN.
* The <code>unblockingPinID</code> field indicates which unblocking
* PIN is to be
* used to unblock the blocked PIN which is indicated by the field
* <code>blockedPinId</code>.
* The unblockingPinID field indicates which PIN is to be unblocked.
* The user can either cancel the request
* or continue. If the user enters the PIN values and chooses to continue,
* the implementation is responsible
* for presenting the PIN values to the card for unblocking the
* blocked PIN.
* If padding is required for either of the PIN values, the
* implementation is responsible for providing appropriate padding.
* @param blockedPinID the Id of PIN that is to be unblocked.
* @param unblockingPinId the Id of unblocking PIN.
* @return PINENTRY_CANCELLED if the user cancelled the PIN entry
* request or the value returned by the remote method.
* @exception java.rmi.RemoteException is thrown if the PIN could
* not be communicated to the card or an exception is thrown
* by the card in response to the PIN entry.
* @exception SecurityException is thrown if the J2ME application does
* not have appropriate rights to ask for unblocking the PIN.
*/
public short unblockPin(int blockedPinID, int unblockingPinId)
throws RemoteException {
return doEnterPin(blockedPinID, unblockingPinId,
ACLPermissions.CMD_UNBLOCK);
}
/**
* Performs PIN entry operation.
* @param pinID PIN identifier.
* @param uPinID unblocking PIN identifier.
* @param action PIN operation identifier.
* @return PINENTRY_CANCELLED if the user cancelled the PIN entry
* request or the value returned by the remote method.
* @exception java.rmi.RemoteException is thrown if the PIN could
* not be communicated to the card or an exception is thrown
* by the card in response to the PIN entry.
* @exception SecurityException is thrown if the J2ME application does
* not have appropriate rights to ask for unblocking the PIN.
*/
private short doEnterPin(int pinID, int uPinID,
int action) throws RemoteException {
if (! connectionOpen) {
throw new RemoteException("Connection is closed.");
}
String method = null;
method = verifier.preparePIN(pinID, uPinID, action,
internalReference.getClassName());
Object[] pins = verifier.enterPIN(classSecurityToken, action);
if (pins == null) {
return PINENTRY_CANCELLED;
}
try {
Short r = (Short) invoke(internalReference, method, pins);
return r.shortValue();
} catch (Exception e) {
throw new RemoteException("" + e);
}
}
}
|