FileDocCategorySizeDatePackage
WIMApplication.javaAPI DocphoneME MR2 API (J2ME)82275Wed May 02 18:00:38 BST 2007com.sun.satsa.pki

WIMApplication

public class WIMApplication extends Object
This class provides interface to WIM card application.

Fields Summary
static final int
SKIP
Operation result constant (skip this SE and try next).
static final int
SUCCESS
Operation result constant.
static final int
CANCEL
Operation result constant.
static final int
ERROR
Operation result constant.
private com.sun.midp.security.SecurityToken
securityToken
This class has a different security domain than the MIDlet suite
private static final byte
INS_VERIFY
INS byte for APDU command.
private static final byte
INS_MSE
INS byte for APDU command.
private static final byte
INS_PSO
INS byte for APDU command.
static final byte
INS_NEW
INS byte for command APDU.
private static final int
PIN_BLOCKED
PIN status constant.
private static final int
PIN_DISABLED
PIN status constant.
private static final int
PIN_REQUIRED
PIN status constant.
private static final int
PIN_CANCELLED
PIN status constant.
private static final short
ODFPath
ODF path.
private static final short
TokenInfoPath
EF(TokenInfo) path.
private static final short
UnusedSpace
EF(UnusedSpace) path.
private static final int
PRIVATE_KEYS_TAG
ODF entry tag.
private static final int
PUBLIC_KEYS1_TAG
ODF entry tag.
private static final int
PUBLIC_KEYS2_TAG
ODF entry tag.
private static final int
USEFUL_CERTIFICATES_TAG
ODF entry tag.
private static final int
TRUSTED_CERTIFICATES_TAG
ODF entry tag.
private static final int
USER_CERTIFICATES_TAG
ODF entry tag.
private static final int
PINS_TAG
ODF entry tag.
private static final byte[]
selectAPDUs
APDUs that must be used for WIM application selection.
private static final byte[]
WIM_GENERIC_RSA_OID
Binary representation of WIM_GENERIC_RSA SE OID, 2.23.43.1.1.2
private static final byte[]
WAP_WSG_WIMPATH
Binary representation of WAP_WSG_WIMPATH OID, 2.23.43.1.3
private static final byte[]
DigestInfoHeader
RSA digest header.
private Connection
apdu
Connection object.
private WimFileSystem
files
File system object.
private int
WIM_GENERIC_RSA_ID
Identifier of WIM_GENERIC_RSA SE
private int
RSA_ALGORITHM_ID
Identifier of RSA algorithm in EF(TokenInfo) and PrKDFs.
private boolean
readOnly
If true this WIM application doesn't allow to modify data.
private String
serialNumber
Serial number of this WIM card.
private Vector
ODF
This vector contains parsed objects from DF(ODF).
private PrivateKey[]
PrKeys
Private keys.
private PublicKey[]
PuKeys
Public keys.
private com.sun.satsa.acl.PINAttributes[]
PINs
PINs.
private Certificate[]
Certificates
Certificates.
private Vector
certificateIDs
Vector contains identifiers of existing certificates.
private Vector
loaderObjects
This vector contains objects loaded from directory file.
private Vector
loaderLocations
This vector contains locations of loaded objects.
private Vector
loaderFreeBlocks
This vector contains locations of free space in directory files.
private short[]
UnusedSpacePath
Full path to EF(UnusedSpace) file.
private Vector
updateLocations
Vector contains locations of blocks that must be updated.
private Vector
updateData
Vector contains values that must be written.
private boolean[]
updatePIN
Flags that indicate which PINs must be verified before the update.
Constructors Summary
private WIMApplication(com.sun.midp.io.j2me.apdu.Handle h)
Constructs a new WIMApplication object.

param
h the APDU connection handle

        this.apdu = new Connection(h);
        files = new WimFileSystem(apdu);
        securityToken = h.token;
    
Methods Summary
public intaddCredential(java.lang.String label, TLV top, java.util.Vector keyIDs)
Adds a user certificate or certificate URI to a certificate store. See UserCredentialManager.addCredential for details. Calling method must remove leading and trailing spaces in label.

param
label the user friendly name associated with the certificate
param
top chain of certificates from pkiPath
param
keyIDs vector that contains identifiers of keys for which certificates are expected
return
operation result
throws
IllegalArgumentException if certificate parsing error occurs or label is not unique or user credential exists already
throws
SecurityException if a PIN is blocked due to an excessive number of incorrect PIN entries


        // load existing certificates
        try {
            loadPrivateKeys();
            loadCertificates(true, true);
        } catch (IOException e) {
            return SKIP;
        }

        // put certificates into array
        Vector u = new Vector();
        while (top != null) {
            u.addElement(top);
            top = top.next;
        }
        TLV path[] = new TLV[u.size()];
        u.copyInto(path);

        // verify certificates encoding and calculate identifier
        // the purpose of the check is to ensure that new certificates
        // will not cause runtime exceptions, not to verify X.509
        // compliance
        byte[][] IDs = new byte[path.length][];
        try {
            TLV Issuer = null;
            for (int i = 0; i < path.length; i++) {
                TLV t = path[i].child.child.skipOptional(0xa0).next.next;
                RFC2253Name.compare(t, t);  // issuer
                if (Issuer != null && ! RFC2253Name.compare(Issuer, t)) {
                    throw new IllegalArgumentException();
                }
                t = t.next;                 // validity
                t.child.getTime();          // notBefore
                t.child.next.getTime();     // notAfter
                t = t.next;                 // subject
                RFC2253Name.compare(t, t);
                Issuer = t;
                IDs[i] = getKeyHash(path[i]);   // subjectPublicKeyInfo
            }
        } catch (IOException e) {
            throw new IllegalArgumentException("Invalid pkiPath");
        } catch (NullPointerException npe) {
            throw new IllegalArgumentException("Invalid pkiPath");
        }
        
        // check if this WIM contains corresponding private key
        if (getPrivateKey(IDs[path.length - 1]) == null) {
            return SKIP;
        }

        // check that the label is unique for this card
        if (getCertificate(label) != null) {
            throw new IllegalArgumentException(label);
        }

        // eliminate certificates that already present on the card
        for (int i = 0; i < path.length; i++) {
            TLV t = path[i].child.child.skipOptional(0xa0);
            if (getCertificate(t.next.next, t) != null) {
                path[i] = null;
            }
        }

        if (path[path.length - 1] == null) {
            throw new IllegalArgumentException("credential exists");
        }

        // if the 1st certificate is self-signed we don't need to save it
        if (path.length > 1 && path[0] != null) {
            TLV t = path[0].child.child.skipOptional(0xa0).next.next;
            if (RFC2253Name.compare(t, t.next.next)) {
                path[0] = null;
            }
        }

        // find place for every certificate and generate CDF records

        startUpdate();

        Location[] locations;
        try {
            locations = putObjects(path);
        } catch (IOException e) {
            return ERROR;
        }
        if (locations == null) { // if no enough space
            return ERROR;
        }

        Vector headers = new Vector();
        int labelNum = 0;

        for (int i = 0; i < path.length; i++) {

            if (path[i] == null) {
                continue;
            }

            // generate header for certificate

            // create unique label if necessary
            String t_label = label;
             if (i < path.length - 1) {
                 for (int k = 0; k < 100000; k++) {
                     t_label = "certificate # " + labelNum++;
                     if (! label.equals(t_label) &&
                           getCertificate(t_label) == null) {
                         break;
                     }
                 }
            }

            // find identifier of the previous certificate
            byte[] prevID;
            if (i == 0) {
                TLV t = path[i].child.child.skipOptional(0xa0).next.next;
                Vector v = getCertsBySubject(t);
                if (v.size() == 0) {
                    prevID = new byte[20];
                } else {
                    prevID = ((Certificate) v.elementAt(0)).id;
                }
            } else {
                prevID = IDs[i - 1];
            }

            TLV commonAttrs = TLV.createSequence();
            commonAttrs.
                setChild(createLabel(t_label)).
                setNext(new TLV(TLV.BITSTRING_TYPE, new byte[2]));

            TLV commonCertAttrs = TLV.createSequence();
            commonCertAttrs.
                setChild(TLV.createOctetString(IDs[i])).
                setNext(TLV.createOctetString(prevID));

            Location l = locations[i];

            TLV x509Attrs = new TLV(0xa1);
            x509Attrs.setChild(TLV.createSequence()).
                    setChild(createPath(l.path, l.offset, l.length));

            TLV cdf = TLV.createSequence();
            cdf.setChild(commonAttrs).
                setNext(commonCertAttrs).
                setNext(x509Attrs);

            headers.addElement(cdf);
        }

        // find free space for headers in CDFs
        if (! putHeaders(headers)) {
            return ERROR;
        }

        // check PINs
        for (int i = 0; i < updatePIN.length; i++) {
            if (updatePIN[i]) {
                int pinStatus = checkPIN(PINs[i]);
                if (pinStatus == PIN_CANCELLED) {
                    return CANCEL;
                }
                if (pinStatus == PIN_BLOCKED) {
                    throw new SecurityException("PIN blocked");
                }
                if (pinStatus != PIN_DISABLED) {
                    return ERROR;
                }
            }
        }

        // update
        try {
            doUpdate();
        } catch (IOException e) {
            return ERROR;
        }

        // remove key identifier from the list of expected certificates
        for (int i = 0; i < keyIDs.size(); i++) {
            if (Utils.byteMatch(IDs[path.length - 1],
                                (byte[]) keyIDs.elementAt(i))) {
                keyIDs.removeElementAt(i);
                break;
            }
        }

//        typeInfo("AddCredential");
        return SUCCESS;
    
private intcheckPIN(com.sun.satsa.acl.PINAttributes pin)
Verifies the PIN if necessary.

param
pin object containing PIN attributes
return
PIN_DISABLED - if the PIN is verified or not required; PIN_BLOCKED - if the PIN is blocked; PIN_CANCELLED - if user cancelled PIN entry dialog


        while (true) {

            int status = getPINStatus(pin);

            if (status == PIN_DISABLED) {
                return PIN_DISABLED;
            }

            if (status == PIN_BLOCKED) {
                try {
                    MessageDialog
			.showMessage(securityToken,
				     Resource.getString(ResourceConstants
							.ERROR),
				     Resource
				     .getString(ResourceConstants
						.JSR177_WIM_PIN_BLOCKED) + ":\n" +
				     pin.label,
				     false);
                } catch (InterruptedException e) {}
                return PIN_BLOCKED;
            }

            // verification is required
            PINEntryDialog dialog;
            try {
                dialog = new PINEntryDialog(securityToken,
                        ACLPermissions.CMD_VERIFY,
                        pin, null);
            } catch (InterruptedException e) {
                return PIN_CANCELLED;
            }

            dialog.waitForAnswer();

            Object[] pins = dialog.getPINs();

            if (pins == null) {
                return PIN_CANCELLED;
            }

            boolean ok = true;
            try {
                byte[] tmp = (byte[]) pins[0];
                apdu.resetCommand().
                     putBytes(tmp, 0, tmp.length).
                     sendCommand(INS_VERIFY, pin.pinReference, 127, false);
            } catch (IOException e) {
                ok = false;
            }

            if (ok & (apdu.lastSW == 0x9000)) {
                return PIN_DISABLED;
            }

            try {
                MessageDialog
		    .showMessage(securityToken,
				 Resource
				 .getString(ResourceConstants
					    .ERROR),
				 Resource
				 .getString(ResourceConstants
					    .JSR177_WIM_PIN_NOT_VERIFIED),
				 false);
            } catch (InterruptedException e) {}
        }
    
private static booleancomparePaths(short[] path1, short[] path2)
Compares two paths.

param
path1 the first path
param
path2 the second path
return
true if the paths are equal


        if (path1.length != path2.length) {
            return false;
        }

        for (int i = 0; i < path1.length; i++) {
            if (path1[i] != path2[i]) {
                return false;
            }
        }
        return true;
    
private static TLVcreateLabel(java.lang.String label)
Pads the string to 32 bytes as recommended in WIM and returns TLV value that can be used as label.

param
label label string
return
TLV object for new label

        int len = Utils.stringToBytes(label).length;
        while (len < 32) {
            label = label + " ";
            len++;
        }
        return TLV.createUTF8String(label);
    
private TLVcreatePath(short[] path, int offset, int length)
Creates PKCS#15 path.

param
path card file path
param
offset offset in the file
param
length length of data in the file
return
TLV object that represents PKCS#15 path


        TLV t = TLV.createSequence();
        t.setChild(TLV.createOctetString(Utils.shortsToBytes(path))).
            setNext(TLV.createInteger(Utils.shortToBytes(offset))).
            setNext(TLV.createInteger(Utils.shortToBytes(length)).
                    setTag(0x80));
        return t;
    
private voiddeleteBlock(Location l)
Mark record of EF(UnusedSpace) as unused.

param
l location of the record

        update(UnusedSpacePath, l.offset, getEmptySpaceHeader(l.length));
    
private static voiddoParseDF(byte[] data, short[] path, java.util.Vector objects, java.util.Vector locations, java.util.Vector freeBlocks)
Parses EF(DF).

param
data data to be parsed
param
path file path
param
objects method places objects from file into this vector. Can be null. Contains values of TLV type
param
locations method places location of objects into this vector. Can be null. Contains values of type Location.
param
freeBlocks method places locations of free memory in EF(DF) into this vector. Can be null. Contains values of type Location.
throws
TLVException if parsing error occurs


        int start = 0;

        int current = 0;
        while (current < data.length) {

            // free space - skip
            if (data[current] == (byte) 0xff) {
                current++;
                continue;
            }

            // TLV object
            TLV t = new TLV(data, current);

            // empty one - skip
            if (t.type == 0) {
                current = t.valueOffset + t.length;
                continue;
            }

            // real object

            if (objects != null) {
                objects.addElement(t);
            }

            if (locations != null) {
                locations.addElement(new Location(path, current,
                                   t.valueOffset + t.length - current));
            }

            if (freeBlocks != null && start < current) {
                freeBlocks.addElement(
                            new Location(path, start, current - start));
            }

            current = t.valueOffset + t.length;
            start = current;
        }
        if (start < current && freeBlocks != null) {
            freeBlocks.addElement(
                            new Location(path, start, current - start));
        }
    
private voiddoRemove(java.util.Vector chain, int count)
Register all necessary file updates for certificate removal.

param
chain certificate chain to be removed
param
count the number of certificates to be removed
throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs


        // Load and parse UnusedSpace.
        files.select(UnusedSpacePath);

        Vector v_free = new Vector();     // records in UnusedSpace
        Vector v_location = new Vector(); // their offsets
        Vector v_hole = new Vector();     // empty space in the file
        doParseDF(files.readFile(), UnusedSpacePath,
                     v_free, v_location, v_hole);

        TLV[] free = new TLV[v_free.size()];
        v_free.copyInto(free);

        Location[] block = new Location[free.length];
        for (int i = 0; i < free.length; i++) {
            block[i] = files.pathToLocation(free[i].child);
        }

        for (int i = 0; i < count; i++) {

            Certificate cert = (Certificate) chain.elementAt(i);

            // remove the certificate header from CDF
            update(cert.header.path, cert.header.offset,
                    getEmptySpaceHeader(cert.header.length));

            // Now area in data file must be marked as unused.
            // try to append/prepend the new block to existing blocks

            Location hole = cert.body;

            int head = -1;
            int tail = -1;
            int empty = -1;

            for (int k = 0; k < block.length; k++) {
                if (block[k] == null ||
                    ! comparePaths(hole.path, block[k].path)) {
                    continue;
                }
                if (block[k].length == 0) {
                    empty = k;
                    continue;
                }
                if (doesFollow(block[k], hole)) {
                    head = k;
                    continue;
                }
                if (doesFollow(hole, block[k])) {
                    tail = k;
                }
            }

            if (head != -1 && tail != -1) {
                block[head].length = block[head].length + hole.length +
                                     block[tail].length;
                setBlockLength(free[head], block[head].length);

                Location newHole = (Location) v_location.elementAt(tail);
                deleteBlock(newHole);
                v_hole.addElement(newHole);
                block[tail] = null;
                continue;
            }

            if (head != -1) {
                block[head].length += hole.length;
                setBlockLength(free[head], block[head].length);
                continue;
            }

            if (tail != -1) {
                block[tail].offset -= hole.length;
                block[tail].length += hole.length;
                setBlockOffset(free[tail], block[tail].offset);
                setBlockLength(free[tail], block[tail].length);
                continue;
            }

            if (empty != -1) {
                block[empty].offset = hole.offset;
                block[empty].length = hole.length;
                setBlockOffset(free[empty], block[empty].offset);
                setBlockLength(free[empty], block[empty].length);
                continue;
            }

            // this is a new block, have to allocate new entry

            // generate new record with PIN-G authId
            TLV n = TLV.createSequence();
            TLV t = n.setChild(createPath(hole.path, hole.offset,
                        hole.length));
            t.setNext(TLV.createOctetString(new byte[] {(byte) PINs[0].id}));
            byte[] data = n.getDERData();

            // find space for new entry
            Location l = null;
            for (int k = 0; k < v_hole.size(); k++) {
                Location loc = (Location) v_hole.elementAt(k);
                if (loc.length >= data.length) {
                    l = loc;
                    break;
                }
            }

            if (l == null) {
                throw new IOException(
                        "Can't allocate new entry in EF(UnusedSpace)");
            }

            // update data
            update(UnusedSpacePath, l.offset, data);
            l.offset += data.length;
            l.length -= data.length;
            if (l.length != 0) {
                update(UnusedSpacePath, l.offset,
                        getEmptySpaceHeader(l.length));
            }
        }
    
private voiddoUpdate()
Performs all the registered updates.

throws
IOException if I/O error occurs


        for (int i = 0; i < updateLocations.size(); i++) {
            Location l = (Location) updateLocations.elementAt(i);
            files.select(l.path);
            byte[] d = (byte[]) updateData.elementAt(i);
            files.writeData(d, 0, d.length, l.offset);
        }
        updateLocations = null;
        updateData = null;
    
private static booleandoesFollow(Location a1, Location a2)
Verifies if the second block starts right after the fist.

param
a1 location of the first block
param
a2 location of the second block
return
true if the second block starts right after the fist

        return (a1.offset + a1.length == a2.offset);
    
public voiddone()
Safely closes the connection.

        if (apdu != null) {
            apdu.done();
        }
        apdu = null;
    
public byte[]generateCSR(java.lang.String nameInfo, int keyLen, int keyUsage, boolean forceKeyGen, java.util.Vector keyIDs)
Generates CSR. See UserCredentialManager.generateCSR for details. The calling method must load a vector that contains IDs of keys for which CSRs were generated earlier and save it after successful CSR generation.

param
nameInfo certificate subject name
param
keyLen key length
param
keyUsage key usage
param
forceKeyGen if set to true a new key must be generated
param
keyIDs IDs of keys for which CSRs were generated earlier. If the new CSR is generated, the key ID is added into this vector.
return
the new CSR or null if operation cancelled
throws
UserCredentialManagerException if key is not found
throws
CMSMessageSignatureServiceException if CSR generation failed
throws
SecurityException if a PIN is blocked due to an excessive number of incorrect PIN entries


        // check the name

        if (nameInfo == null) {
            nameInfo = "CN=" + serialNumber;
        }

        TLV name;

        try {
            name = new TLV(RFC2253Name.toDER(nameInfo), 0);
        } catch (TLVException e) {
            throw new IllegalArgumentException("Invalid name");
        }

        try {
            if (MessageDialog.showMessage(securityToken,
                Resource.getString(ResourceConstants.AMS_CONFIRMATION),
                Resource.getString(ResourceConstants.
                    JSR177_CERTIFICATE_GENERATED) +
                    "\n\n" + 
                    // "Name: "
                    Resource.getString(ResourceConstants.
                        JSR177_CERTIFICATE_SUBJECT) + ": " +
                    nameInfo +
                    "\n\n" + 
                    // "Key usage: "
                    Resource.getString(ResourceConstants.
                        JSR177_CERTIFICATE_KEYUSAGE) + ": " +
                    ((keyUsage ==
                        UserCredentialManager.KEY_USAGE_AUTHENTICATION) ?
                        // "authentication"
                        Resource.getString(ResourceConstants.
                            JSR177_CERTIFICATE_KEYUSAGE_AUTH) :
                        // "non-repudiation"
                        Resource.getString(ResourceConstants.
                            JSR177_CERTIFICATE_KEYUSAGE_NR)) +
                    "\n" + 
                    // "Algorithm: "
                    Resource.getString(ResourceConstants.
                        JSR177_CERTIFICATE_ALGORITHM) + ": " +
                    "RSA" + 
                    "\n" + 
                    // "Key length: "
                    Resource.getString(ResourceConstants.
                        JSR177_CERTIFICATE_KEYLENGTH) + ": " +
                    keyLen,
                true) == Dialog.CANCELLED) {
                return null;
            }
        } catch (InterruptedException e) {
            return null;
        }

        int keyId = -1;
        if (forceKeyGen) {
            try {
                keyId = generateKey(keyLen, keyUsage);
            } catch (IOException ioe) { // ignored
            } catch (InterruptedException ie) { // ignored
            }

            if (keyId == -1) {
                throw new UserCredentialManagerException(
                       UserCredentialManagerException.SE_NO_KEYGEN);
            }
            if (keyId == -2) {
                throw new UserCredentialManagerException(
                       UserCredentialManagerException.SE_NO_KEYS);
            }
        }

        // load info about keys

        try {
            if (keyId != -1) {
                loadPINs();
            }
            loadCertificates(false, true);
            loadPrivateKeys();
            loadPublicKeys();
        } catch (IOException e) {
                throw new UserCredentialManagerException(
                             UserCredentialManagerException.SE_NO_KEYS);
        }

        // find the 'best' key

        PrivateKey key = null;
        int keyType = 3;        // 0 - no certificate or CSR
                                // 1 - CSR
                                // 2 - certificate
                                // 3 - empty
        TLV keyValue = null;

        for (int i = 0; i < PrKeys.length; i++) {

            if (keyId != -1) {
                if (keyId == PrKeys[i].keyReference) {
                    key = PrKeys[i];
                    keyValue = getPublicKey(PrKeys[i].id);
                    break;
                }
                continue;
            }

            // check key size
            if (PrKeys[i].modulusLength != keyLen) {
                continue;
            }

            // check key usage
            if (! (keyUsage ==
                   UserCredentialManager.KEY_USAGE_AUTHENTICATION ?
                   PrKeys[i].authentication :
                   PrKeys[i].nonRepudiation)) {
                continue;
            }

            // check if certificate or CSR for this key exists
            int type = 0;

            if (getIDIndex(keyIDs, PrKeys[i].id) != -1) {
                type = 1;
            }

            if (getIDIndex(certificateIDs, PrKeys[i].id) != -1) {
                type = 2;
            }

            // is this key is better than the previous?
            if (type >= keyType) {
                continue;
            }

            // if PIN doesn't exist or blocked, find another key
            PINAttributes pin = getPIN(PrKeys[i].authId);
            if (pin == null || getPINStatus(pin) == PIN_BLOCKED) {
                continue;
            }

            // if the public key can't be retrieved, find another key
            TLV t = getPublicKey(PrKeys[i].id);
            if (t == null) {
                continue;
            }

            // the best key so far
            key = PrKeys[i];
            keyValue = t;
            keyType = type;
        }

        if (key == null) {
            throw new UserCredentialManagerException(
                             UserCredentialManagerException.SE_NO_KEYS);
        }

        // the key is found and loaded
        // check PIN for signature operation
        int pinStatus = checkPIN(getPIN(key.authId));

        if (pinStatus == PIN_CANCELLED) {
            return null;
        }

        if (pinStatus == PIN_BLOCKED) {
            throw new SecurityException("PIN blocked");
        }

        if (pinStatus != PIN_DISABLED) {
            // IMPL_NOTE: need warning message?
            throw new CMSMessageSignatureServiceException(
                 CMSMessageSignatureServiceException.SE_CRYPTO_FAILURE);
        }

        // PIN is verified, create the CSR

        TLV CRInfo = TLV.createSequence();

        CRInfo.setChild(TLV.createInteger(0)).
            setNext(name).
            setNext(keyValue).
            setNext(new TLV(TLV.SET_TYPE).setTag(0xa0)).
            setChild(TLV.createSequence()).
            setChild(TLV.createOID("1.2.840.113549.1.9.14")).
            setNext(new TLV(TLV.SET_TYPE)).
            setChild(TLV.createSequence()).
            setChild(TLV.createSequence()).
            setChild(TLV.createOID("2.5.29.15")).
            setNext(new TLV(TLV.BOOLEAN_TYPE, new byte[] {(byte) 255})).
            setNext(new TLV(TLV.OCTETSTR_TYPE,
                keyUsage == UserCredentialManager.KEY_USAGE_AUTHENTICATION ?
                new byte[] {3, 2, 7, (byte) 0x80} :
                new byte[] {3, 2, 6, 0x40}));

        byte[] sign;

        try {
            sign = signData(key, CRInfo.getDERData());
        } catch (IOException e) {
            throw new CMSMessageSignatureServiceException(
                 CMSMessageSignatureServiceException.SE_CRYPTO_FAILURE);
        }

        TLV alg = CRInfo.child.next.next.child.copy();

        TLV OID = TLV.createOID("1.2.840.113549.1.1.5");
        TLV params = alg.child.next;

        alg = TLV.createSequence();
        alg.setChild(OID).setNext(params);

        TLV request = TLV.createSequence();
        request.setChild(CRInfo).
                setNext(alg).
                setNext(new TLV(TLV.BITSTRING_TYPE, sign));

        // add to the vector of IDs of keys for which CSRs are generated
        keyIDs.addElement(key.id);
        return request.getDERData();
    
intgenerateKey(int keyLen, int keyUsage)
Generates new key.

param
keyLen key length
param
keyUsage key usage
return
key reference or -1 if the key generation is not supported or -2 if key cannot be generated
throws
IOException if I/O error occurs
throws
InterruptedException if interrupted


        boolean nonRepudiation = (keyUsage ==
                UserCredentialManager.KEY_USAGE_NON_REPUDIATION);

        byte[] tmp = apdu.resetCommand().
            putByte(nonRepudiation ? 1 : 0).
            putShort(keyLen).
            sendCommand(INS_NEW, 0x0100, 240, false);

        if (apdu.lastSW == 0x9001) {
            return -2;
        }

        if (tmp.length != 6 ||
            Utils.getShort(tmp, 0) != 0x1234 ||
            Utils.getShort(tmp, 2) != 0x4321) {
            return -1;
        }

        apdu.resetCommand().
            putByte(nonRepudiation ? 1 : 0).
            putShort(keyLen);

        if (nonRepudiation) {
            // must create new PIN
            String[] pinInfo = MessageDialog.enterNewPIN(securityToken);
            if (pinInfo == null) {
                return -1;
            }

            tmp = pinInfo[1].getBytes();
            int len = tmp.length;
            apdu.putBytes(tmp, 0, len);
            while (len++ < 8) {
                apdu.putByte(0xff);
            }

            tmp = Utils.stringToBytes(pinInfo[0]);
            len = tmp.length < 32 ? tmp.length : 32;
            apdu.putBytes(tmp, 0, len);
            while (len++ < 32) {
                apdu.putByte(0x20);
            }
        }

        tmp = apdu.sendCommand(INS_NEW, 0x0000, 240, false);
        return apdu.lastSW == 0x9000 ? tmp[0] & 0xff : -2;
    
public byte[]generateSignature(boolean nonRepudiation, byte[] data, int options, TLV[] caNames)
Generates a signature.

param
nonRepudiation if true, the non-repudiation key must be used, otherwise - authentication key
param
data the data to be signed
param
options signature content options
param
caNames array that contains parsed names of certificate authorities
return
the DER encoded signature, null if the signature generation was cancelled by the user before completion
throws
CMSMessageSignatureServiceException if an error occurs during signature generation


        // load existing certificates
        try {
            loadPrivateKeys();
            loadCertificates(true, true);
        } catch (IOException e) {
            throw new CMSMessageSignatureServiceException(
                    CMSMessageSignatureServiceException.SE_FAILURE);
        }

        // find certificate chains
        Vector chains = getChains(nonRepudiation, caNames);
        if (chains.size() == 0) {
            throw new CMSMessageSignatureServiceException(
             CMSMessageSignatureServiceException.CRYPTO_NO_CERTIFICATE);
        }

        // select certificate
        Vector chain = selectChain(chains);
        if (chain == null) {
            return null;
        }

        Certificate cert = (Certificate) chain.elementAt(0);
        PrivateKey key = getPrivateKey(cert.id);
        PINAttributes pin = getPIN(key.authId);

        int pinStatus = checkPIN(pin);

        if (pinStatus == PIN_BLOCKED) {
            throw new SecurityException();
        }

        if (pinStatus == PIN_CANCELLED) {
            return null;
        }

        TLV signedAttrs = new TLV(TLV.SET_TYPE);
        TLV t = signedAttrs.setChild(TLV.createSequence());
        t.setChild(TLV.createOID("1.2.840.113549.1.9.3")). // ContentType
          setNext(new TLV(TLV.SET_TYPE)).
          setChild(TLV.createOID("1.2.840.113549.1.7.1"));  // data

        t.next = TLV.createSequence();
        t = t.next;

        Calendar calendar = Calendar.getInstance();
        calendar.setTimeZone(TimeZone.getTimeZone("GMT"));

        t.setChild(TLV.createOID("1.2.840.113549.1.9.5")). // signingTime
          setNext(new TLV(TLV.SET_TYPE)).
          setChild(TLV.createUTCTime(calendar));

        t.next = TLV.createSequence();
        t = t.next;

        t.setChild(TLV.createOID("1.2.840.113549.1.9.4")). // messageDigest
          setNext(new TLV(TLV.SET_TYPE)).
          setChild(TLV.createOctetString(Utils.getHash(data, 0, data.length)));

        // generate signature
        byte[] signature;
        try {
            signature = signData(key, signedAttrs.getDERData());
        } catch (IOException e) {
            throw new CMSMessageSignatureServiceException(
             CMSMessageSignatureServiceException.CRYPTO_FAILURE);
        }

        // format the signature
        /*
         *   ContentInfo ::= SEQUENCE {
         *      contentType ContentType,
         *      content [0] EXPLICIT ANY DEFINED BY contentType }
         *
         *  ContentType ::= OBJECT IDENTIFIER
         *
         *  id-signedData OBJECT IDENTIFIER ::= { iso(1) member-body(2)
         *      us(840) rsadsi(113549) pkcs(1) pkcs7(7) 2 }
         */

        TLV ContentInfo = TLV.createSequence();
        t = ContentInfo.
            setChild(TLV.createOID("1.2.840.113549.1.7.2")).
            setNext(new TLV(0xa0)).
            setChild(TLV.createSequence());

        /*
         *   t - SignedData
         *  SignedData ::= SEQUENCE {
         *    version CMSVersion,
         *     digestAlgorithms DigestAlgorithmIdentifiers,
         *    encapContentInfo EncapsulatedContentInfo,
         *    certificates [0] IMPLICIT CertificateSet OPTIONAL,
         *    - crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
         *    signerInfos SignerInfos }
         *
         *    DigestAlgorithmIdentifiers ::= SET OF
         *                                    DigestAlgorithmIdentifier
         */

        t = t.
            setChild(TLV.createInteger(1)).  // version
            setNext(new TLV(TLV.SET_TYPE));  // digestAlgorithms

        TLV SHAAlgId = TLV.createSequence();
        SHAAlgId.setChild(TLV.createOID("1.3.14.3.2.26"));   // SHA-1

        t.setChild(SHAAlgId.copy());

        /*
         *   EncapsulatedContentInfo ::= SEQUENCE {
         *    eContentType ContentType,
         *    eContent [0] EXPLICIT OCTET STRING OPTIONAL }
         *
         *    ContentType ::= OBJECT IDENTIFIER
         *
         *    id-data OBJECT IDENTIFIER ::= { iso(1) member-body(2)
         *        us(840) rsadsi(113549) pkcs(1) pkcs7(7) 1 }
         */

        t = t.setNext(TLV.createSequence());

        TLV m = t.setChild(TLV.createOID("1.2.840.113549.1.7.1"));

        if ((options &
            CMSMessageSignatureService.SIG_INCLUDE_CONTENT) != 0) {
            m.setNext(new TLV(0xa0)).
                setChild(TLV.createOctetString(data));
        }

        /*
         *   certificates [0] IMPLICIT CertificateSet OPTIONAL,
         *
         *  CertificateSet ::= SET OF CertificateChoices
         *
         * CertificateChoices ::= CHOICE {
         *   certificate Certificate,                 -- See X.509
         *  extendedCertificate [0] IMPLICIT ExtendedCertificate,
         *                                           -- Obsolete
         *  attrCert [1] IMPLICIT AttributeCertificate }
         *
         */

        if ((options &
             CMSMessageSignatureService.SIG_INCLUDE_CERTIFICATE) != 0) {
            t = t.setNext(new TLV(0xa0));
            TLV n = t.setChild(cert.cert);
            for (int i = 1; i < chain.size(); i++) {
                n = n.setNext(((Certificate) chain.elementAt(i)).cert);
            }
        }

        /*
         *   signerInfos SignerInfos }
         *  SignerInfos ::= SET OF SignerInfo
         *
         *  SignerInfo ::= SEQUENCE {
         *    version CMSVersion,
         *    sid SignerIdentifier,
         *    digestAlgorithm DigestAlgorithmIdentifier,
         *    signedAttrs [0] IMPLICIT SignedAttributes OPTIONAL,
         *    signatureAlgorithm SignatureAlgorithmIdentifier,
         *    signature SignatureValue,
         *    - unsignedAttrs [1] IMPLICIT UnsignedAttributes OPTIONAL }
         *
         *    SignerIdentifier ::= CHOICE {
         *      issuerAndSerialNumber IssuerAndSerialNumber,
         *      - subjectKeyIdentifier [0] SubjectKeyIdentifier }
         *
         *      IssuerAndSerialNumber ::= SEQUENCE {
         *        issuer Name,
         *        serialNumber CertificateSerialNumber }
         *
         */

        t = t.setNext(new TLV(TLV.SET_TYPE)).
              setChild(TLV.createSequence()).
              setChild(TLV.createInteger(1)).
              setNext(cert.getIssuerAndSerialNumber()).
              setNext(SHAAlgId).      // SHA-1
              setNext(signedAttrs).setTag(0xa0).
              setNext(cert.getKeyAlgorithmID()).
              setNext(new TLV(TLV.OCTETSTR_TYPE, signature, 1));

        return ContentInfo.getDERData();
    
private CertificategetCertificate(java.lang.String label)
Returns certificate for given label.

param
label the user friendly certificate label
return
the certificate object or null if not found


        for (int i = 0; i < Certificates.length; i++) {
            if (Certificates[i].label.equals(label)) {
                return Certificates[i];
            }
        }
        return null;
    
private CertificategetCertificate(TLV issuer, TLV serialNumber)
Returns certificate for given issuer and serial number.

param
issuer TLV structure that represents RFC 2253 name.
param
serialNumber certificate serial number
return
the certificate object or null if not found


        for (int i = 0; i < Certificates.length; i++) {
            TLV t = Certificates[i].cert.child.child.skipOptional(0xa0);
            if (t.match(serialNumber) &&
                t.next.next.match(issuer)) {
                return Certificates[i];
            }
        }
        return null;
    
private java.util.VectorgetCertsBySubject(TLV subject)
Returns certificates for given subject.

param
subject TLV structure that represents RFC 2253 name.
return
certificates for given subject


        Vector v = new Vector();
        for (int i = 0; i < Certificates.length; i++) {
            if (RFC2253Name.compare(subject,
                                    Certificates[i].getSubject())) {
                v.addElement(Certificates[i]);
            }
        }
        return v;
    
private java.util.VectorgetChain(Certificate cert, TLV[] caNames, boolean checkValidity)
Builds certificate chain for given certificate.

param
cert user certificate
param
caNames array that contains parsed names of certificate authorities
param
checkValidity check validity of certificates
return
vector containing certificate chain or null


        Vector chain = new Vector();

        while (true) {

            if (checkValidity && cert.isExpired()) {
                // the certificate is expired, can't build the chain
                return null;
            }

            chain.addElement(cert);
            TLV issuer = cert.getIssuer();

            // if this is the certificate issued by one of the CAs in
            // the list then the chain is complete
            if (caNames != null) {
                // check if we need any additional certificates
                for (int i = 0; i < caNames.length; i++) {
                    if (RFC2253Name.compare(issuer, caNames[i])) {
                        return chain;
                    }
                }
            }

            // search for the issuer certificate
            boolean found = false;
            for (int i = 0; i < Certificates.length; i++) {
                Certificate next = Certificates[i];
                found = cert.isIssuedBy(next) &&
                        ! chain.contains(next);
                if (found) {
                    cert = next;
                    break;
                }
            }

            if (! found) {
                // issuer certificate was not found
                // if caNames is null, the chain is good enough,
                // otherwise the chain is not found
                return caNames == null ? chain : null;
            }
        }
    
private java.util.VectorgetChains(boolean nonRepudiation, TLV[] caNames)
Finds certificate chains for specified operation.

param
nonRepudiation if true, the non-repudiation key must be used, otherwise - authentication key
param
caNames array that contains parsed names of certificate authorities
return
vector of certificate chains


        // find the chains
        Vector chains = new Vector();
        for (int i = 0; i < Certificates.length; i++) {
            PrivateKey key = getPrivateKey(Certificates[i].id);
            if ((key != null) && (nonRepudiation ? key.nonRepudiation :
                                                   key.authentication)) {
                Vector chain = getChain(Certificates[i], caNames, true);
                if (chain != null) {
                    chains.addElement(chain);
                }
            }
        }
        return chains;
    
private static byte[]getEmptySpaceHeader(int length)
Creates block header that can be used to mark empty space in directory file.

param
length free block length
return
block header


        if (length == 1) {
            return new byte[] {(byte) 0xff};
        }

        if (length < 130) {
            return new byte[] {0, (byte) (length - 2)};
        }

        if (length < 259) {
            return new byte[] {0, (byte) 0x81, (byte) (length - 3)};
        }

        length -= 4;
        return new byte[] {0, (byte) 0x82, (byte) (length >> 8),
                           (byte) length};
    
private intgetIDIndex(java.util.Vector IDs, byte[] id)
Returns index of given identifier in the vector or -1 if not found.

param
IDs vector containing identifiers
param
id identifier
return
index of given identifier or -1


        for (int j = 0; j < IDs.size(); j++) {
            if (Utils.byteMatch((byte[]) IDs.elementAt(j), id)) {
                return j;
            }
        }
        return -1;
    
public static com.sun.satsa.pki.WIMApplicationgetInstance(com.sun.midp.security.SecurityToken token, int slotNum, java.lang.String securityElementID, boolean readOnly)
Creates connection with WIM application (WIM spec, 11.3.3) on card in specified slot. Doesn't throw exceptions.

param
token security token
param
slotNum the slot number
param
securityElementID identifies the security element
param
readOnly if true WIM data can be protected
return
WIMApplication object or null.



                                                      
        
                  

        for (int i = 0; i < selectAPDUs.length; i++) {

            Handle h;
            APDUManager.initACL(slotNum, token);
            try {
                h = APDUManager.selectApplication(
                        selectAPDUs[i], (byte) slotNum, token);
            } catch (IOException e) {
                continue;
            }

            WIMApplication w = new WIMApplication(h);
            if (w.init(securityElementID) &&
                (readOnly || (! w.readOnly))) {
                return w;
            }
            w.done();
        }
        return null;
    
private static byte[]getKeyHash(TLV cert)
Calculates public key hash for given X.509 certificate.

param
cert TLV structure that represents X.509 certificate
return
public key hash
throws
TLVException if parsing error occurs


        TLV t = cert.child.child.skipOptional(0xa0).
                next.next.next.next.next.child;
        // t is at subjectPublicKeyInfo.algorithm field

        byte[] data;
        int offset;
        int length;

        TLV m = t.child;
        t = t.next;
        if (m.match(TLV.createOID("1.2.840.113549.1.1.1"))) {
            // RSA
            data = (new TLV(t.data, t.valueOffset + 1).child).getValue();
            offset = 0;
            length = data.length;
        } else
        if (m.match(TLV.createOID("1.2.840.10045.2.1"))) {
            // ECDSA
            data = t.data;
            offset = t.valueOffset + 2;
            if (t.data[t.valueOffset + 1] == 4) {
                // uncompressed form
                length = (t.length - 2) / 2;
            } else {
                // compressed form
                length = (t.length - 2);
            }
        } else {
            // PKCS#15 doesn't say anything about this case
            // just calculate the hash of subjectPublicKey data
            data = t.data;
            offset = t.valueOffset + 1;
            length = t.length - 1;
        }

        return Utils.getHash(data, offset, length);
    
private com.sun.satsa.acl.PINAttributesgetPIN(int authId)
Returns PIN attributes for given authId.

param
authId identifier of PIN
return
PIN attributes or null


        for (int i = 0; i < PINs.length; i++) {
            if (PINs[i].id == authId) {
                return PINs[i];
            }
        }
        return null;
    
private intgetPINStatus(com.sun.satsa.acl.PINAttributes pin)
Verifies PIN status.

param
pin object containing PIN attributes
return
PIN status.


        try {
            files.select(pin.path);
            apdu.resetCommand().
            sendCommand(INS_VERIFY, pin.pinReference, 0, false);
        } catch (IOException e) {
            return PIN_BLOCKED;
        }

        if (apdu.lastSW == 0x9000) {
            return PIN_DISABLED;
        }

        if (apdu.lastSW == 0x6983) {
            return PIN_BLOCKED;
        }

        return PIN_REQUIRED;
    
private PrivateKeygetPrivateKey(byte[] id)
Returns private key for given ID.

param
id key identifier
return
the key or null if not found


        for (int i = 0; i < PrKeys.length; i++) {
            if (Utils.byteMatch(id, PrKeys[i].id)) {
                return PrKeys[i];
            }
        }
        return null;
    
private TLVgetPublicKey(byte[] id)
Returns TLV that contains SubjectPublicKeyInfo structure for public key.

param
id key identifier
return
TLV that contains SubjectPublicKeyInfo structure or null


        // try to obtain the key from certificate

        for (int i = 0; i < Certificates.length; i++) {
            if (! Utils.byteMatch(Certificates[i].id, id)) {
                continue;
            }
            try {
                TLV t = files.loadObject(Certificates[i].body);
                return t.child.child.skipOptional(0xa0).next.next.next.
                        next.next.copy();
            } catch (IOException e) {
                continue;
            }
        }

        /*
            there is no certificate for this private key, try to
            read public key
        */
        for (int i = 0; i < PuKeys.length; i++) {
            if (! Utils.byteMatch(PuKeys[i].id, id)) {
                continue;
            }

            try {
                files.select(PuKeys[i].body.path);
                if (PuKeys[i].body.length == -1) {
                    PuKeys[i].body.length = files.getCurrrentFileSize();
                }
                byte[] tmp = files.readData(1, PuKeys[i].body.length,
                                            PuKeys[i].body.offset);

                TLV subjectPKInfo = TLV.createSequence();

                TLV alg = TLV.createSequence();
                subjectPKInfo.setChild(alg);

                alg.setChild(TLV.createOID("1.2.840.113549.1.1.1")).
                             setNext(new TLV(TLV.NULL_TYPE));

                alg.setNext(new TLV(TLV.BITSTRING_TYPE, tmp));
                return subjectPKInfo;
            } catch (IOException e) {
                break;
            }
        }
        return null;
    
private booleaninit(java.lang.String securityElementID)
Reads configuration (EF(TokenInfo) file). Doesn't throw exceptions.

param
securityElementID identifies the security element
return
true if successful


        try {
            if (! readTokenInfo(securityElementID)) {
                return false;
            }

            readODF();
            loadPINs();
            UnusedSpacePath = files.makePath(TLV.createOctetString(
                                      Utils.shortToBytes(UnusedSpace)));
            return true;
        } catch (IOException te) {
            done();
        }

        return false;
    
private voidloadCertificates(boolean loadValues, boolean loadTrusted)
Loads attributes of X.509 certificates. Places results into Certificates array. Places identifiers of all certificates into certificateIDs vector.

param
loadValues if true loads also the certificates
param
loadTrusted if true load trusted certificates
throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs


        Vector objects = new Vector();
        Vector locations = new Vector();
        resetLoader(objects, locations, null);
        if (loadTrusted) {
            loadObjects(TRUSTED_CERTIFICATES_TAG);
        }
        loadObjects(USEFUL_CERTIFICATES_TAG);
        loadObjects(USER_CERTIFICATES_TAG);

        Vector k = new Vector();
        certificateIDs = new Vector();

        for (int i = 0; i < objects.size(); i++) {

            TLV t = (TLV) objects.elementAt(i);

            // is it x.509 certificate?
            if (t.type != TLV.SEQUENCE_TYPE) {
                certificateIDs.addElement(t.child.next.child.getValue());
                continue;
            }

            Certificate cert = new Certificate();
            k.addElement(cert);

            t = t.child;        // commonObjectAttributes

            cert.label = t.child.getUTF8().trim();

            t = t.next;

            cert.id = t.child.getValue();
            certificateIDs.addElement(cert.id);

            TLV v = t.child.next;
            if (v != null) {
                v = v.skipOptional(TLV.BOOLEAN_TYPE);
                if (v != null) {
                    cert.requestId = v.getValue();
                }
            }

            t = t.next.child.child;

            cert.body = files.pathToLocation(t);

            if (loadValues) {
                cert.cert = files.loadObject(cert.body);
            }

            cert.header = (Location) locations.elementAt(i);
        }

        Certificates = new Certificate[k.size()];
        k.copyInto(Certificates);
    
private voidloadObjects(int tag)
Finds all the files for specified type, reads and parses them.

param
tag tag of ODF entry for this type of objects
throws
IOException if IO error occurs


        for (int i = 0; i < ODF.size(); i++) {
            TLV t = (TLV) ODF.elementAt(i);
            if (t.type == tag) {
                parseDF(files.makePath(t.child.child));
            }
        }
    
private voidloadPINs()
Loads PIN objects and places them into PINs array.

throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs


        Vector v = new Vector();
        resetLoader(v, null, null);
        loadObjects(PINS_TAG);

        Vector k = new Vector();

        for (int i = 0; i < v.size(); i++) {

            TLV t = (TLV) v.elementAt(i);

            if (t.type != TLV.SEQUENCE_TYPE) {      // not a PIN object
                continue;
            }

            PINAttributes pin = new PINAttributes();
            k.addElement(pin);

            t = t.child;        // commonObjectAttributes
            pin.label = t.child.getUTF8().trim();

            t = t.next;         // CommonAuthenticationObjectAttributes
            pin.id = t.child.getId();

            t = t.next.child.child;   // PinAttributes.pinFlags

            if (t.checkFlag(0)) {
                pin.pinFlags = PINAttributes.FLAG_CASE_SENSITIVE;
            }
            if (t.checkFlag(5)) {
                pin.pinFlags = PINAttributes.FLAG_NEEDS_PADDING;
            }

            t = t.next;
            pin.pinType = t.getEnumerated();

            t = t.next;
            pin.minLength = t.getInteger();

            t = t.next;
            pin.storedLength = t.getInteger();

            t = t.next;
            if (t.type == TLV.INTEGER_TYPE) {
                pin.maxLength = t.getInteger();
                t = t.next;
            } else {
                pin.maxLength = pin.storedLength;
            }

            // this entry is optional, default value is 0
            if (t.type == 0x80) {
                pin.pinReference = t.getInteger();
                t = t.next;
            }

            pin.padChar = t.getId();

            t = t.next.skipOptional(TLV.GEN_TIME_TYPE);
            pin.path = files.makePath(t.child);
        }

        if (k.size() == 0) {
            throw new IOException("PINs not found");
        }

        PINs = new PINAttributes[k.size()];
        k.copyInto(PINs);
    
private voidloadPrivateKeys()
Loads all RSA private keys. Keys are stored in PrKeys array.

throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs


        Vector v = new Vector();
        resetLoader(v, null, null);
        loadObjects(PRIVATE_KEYS_TAG);

        Vector k = new Vector();

        for (int i = 0; i < v.size(); i++) {

            TLV t = (TLV) v.elementAt(i);

            if (t.type != TLV.SEQUENCE_TYPE) {      // non-RSA key
                continue;
            }

            PrivateKey key = new PrivateKey();

            t = t.child;        // commonObjectAttributes

            key.label = t.child.getUTF8().trim();
            key.authId = t.child.next.next.getId();

            t = t.next;

            TLV m = t.child;
            key.id = m.getValue();

            m = m.next;
            key.authentication = m.checkFlag(2);
            key.nonRepudiation = m.checkFlag(9);

            m = m.next.skipOptional(TLV.BOOLEAN_TYPE);
            key.keyReference = m.getInteger() & 0xff;

            // skip PKCS15CommonPrivateKeyAttributes
            t = t.next.skipOptional(0xa0);

            t = t.child.child;
            key.path = files.makePath(t.child);
            t = t.next;
            key.modulusLength = t.getInteger();

            t = t.next;
            if (t != null &&
                t.type == TLV.INTEGER_TYPE &&
                t.getInteger() != RSA_ALGORITHM_ID) {
                continue;
            }

            k.addElement(key);
        }

        PrKeys = new PrivateKey[k.size()];
        k.copyInto(PrKeys);
    
private voidloadPublicKeys()
Loads all RSA public keys. Stores keys in PuKeys array.

throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs


        Vector v = new Vector();
        resetLoader(v, null, null);
        loadObjects(PUBLIC_KEYS1_TAG);
        loadObjects(PUBLIC_KEYS2_TAG);

        Vector k = new Vector();

        for (int i = 0; i < v.size(); i++) {

            TLV t = (TLV) v.elementAt(i);

            if (t.type != TLV.SEQUENCE_TYPE) {      // non-RSA key
                continue;
            }

            PublicKey key = new PublicKey();

            t = t.child;        // commonObjectAttributes
            t = t.next;         // CommonKeyAttributes

            key.id = t.child.getValue();
            TLV m = t.child.next.next;

            if (m.type != TLV.BOOLEAN_TYPE ||
                m.data[m.valueOffset] != 0) {
                continue;       // native, useless for CSR generation
            }

            // skip PKCS15CommonPublicKeyAttributes
            t = t.next.skipOptional(0xa0);

            key.body = files.pathToLocation(t.child.child);

            k.addElement(key);
        }

        PuKeys = new PublicKey[k.size()];
        k.copyInto(PuKeys);
    
private voidparseDF(short[] path)
Parses directory file. Places results into vectors specified resetLoader method.

param
path path to directory file
throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs

        files.select(path);
        doParseDF(files.readFile(), path,
                      loaderObjects, loaderLocations, loaderFreeBlocks);
    
private booleanputHeaders(java.util.Vector headers)
Finds place for new certificates directory entries and registers all necessary file updates.

param
headers vector containing new CDF entries
return
true if successful


        Vector holes = null;

        for (int i = 0; i < headers.size(); i++) {

            try {
                if (i == headers.size() - 1) {
                    holes = new Vector();
                    resetLoader(null, null, holes);
                    loadObjects(USEFUL_CERTIFICATES_TAG);
                } else
                if (i == 0) {
                    holes = new Vector();
                    resetLoader(null, null, holes);
                    loadObjects(USER_CERTIFICATES_TAG);
                }
            } catch (IOException e) {
                // should never happen
                return false;
            }

            TLV header = (TLV) headers.elementAt(i);
            byte[] data = header.getDERData();

            boolean found = false;
            for (int k = 0; k < holes.size(); k++) {

                Location l = (Location) holes.elementAt(k);
                if (l.length < data.length) {
                    continue;
                }

                l.length -= data.length;
                update(l.path, l.offset + l.length, data);

                // now update the free space after the new record
                if (l.length != 0) {
                    update(l.path, l.offset,
                                        getEmptySpaceHeader(l.length));
                    found = true;
                }
                break;
            }
            if (! found) {
                return false;
            }
        }
        return true;
    
private Location[]putObjects(TLV[] path)
Finds place for new certificates and registers necessary file updates.

param
path array containing certificates
return
array that contains locations for new certificates.
throws
TLVException if parsing error occurs
throws
IOException if I/O error occurs


        // find the free space where certificates can be stored
        files.select(UnusedSpacePath);

        Vector freeSpace = new Vector();
        doParseDF(files.readFile(), UnusedSpacePath,
                freeSpace, null, null);

        Location[] blocks = new Location[freeSpace.size()];
        TLV[] records = new TLV[freeSpace.size()];
        for (int i = 0; i < freeSpace.size(); i++) {
            records[i] = (TLV) freeSpace.elementAt(i);
            blocks[i] = files.pathToLocation(records[i].child);
        }

        Location[] result = new Location[path.length];

        for (int j = 0; j < path.length; j++) {
            if (path[j] == null) {
                continue;
            }

            byte[] data = path[j].getDERData();

            for (int i = 0; i < blocks.length; i++) {

                Location block = blocks[i];
                if (block.length < data.length) {
                    continue;
                }

                block.length -= data.length;
                result[j] = new Location(block.path,
                        block.offset + block.length,
                        data.length);

                update(result[j].path, result[j].offset, data);

                // check if PIN is required for this update
                TLV t = records[i].child.next;
                if (t != null && t.type == TLV.OCTETSTR_TYPE) {

                    int id;
                    try {
                        id = t.getId();
                    } catch (TLVException e) {
                        // should never happen
                        return null;
                    }

                    for (int k = 0; k < PINs.length; k++) {
                        if (PINs[k].id == id) {
                            updatePIN[k] = true;
                        }
                    }
                }

                // Update length of block

                t = records[i].child.child.next.next;

                update(UnusedSpacePath, t.valueOffset,
                        Utils.shortToBytes(block.length));
                break;
            }
            if (result[j] == null) {
                return null;
            }
        }
        return result;
    
private voidreadODF()
Reads ODF and sets WIM root directory if necessary.

throws
IOException if I/O error occurs


        ODF = new Vector();
        resetLoader(ODF, null, null);
        parseDF(new short[] {ODFPath});

        for (int i = 0; i < ODF.size(); i++) {

            TLV t = (TLV) ODF.elementAt(i);

            if (t.type != 0xa7) {
                continue;
            }

            t = t.child;
            if (t.type != 0xa0) {   // not [0]SEQUENCE OF ObjectType
                continue;
            }

            t = t.child.child;      // 1st of SEQUENCE OF PKCS15Data

            while (t != null) {

                if (t.type != TLV.SEQUENCE_TYPE) {      // not opaqueDO
                    t = t.next;
                    continue;
                }

                TLV m = t.child.next.child.skipOptional(TLV.UTF8STR_TYPE);
                if (m.type != TLV.OID_TYPE ||
                    ! m.valueEquals(WAP_WSG_WIMPATH)) {
                    t = t.next;
                    continue;
                }

                m = t.child.next.next.child;

                short[] root = new short[m.length / 2];

                for (int j = 0; j < root.length; j++) {
                    root[j] = Utils.getShort(m.data,
                                             m.valueOffset + j * 2);
                }
                files.setRoot(root);
                break;
            }
        }
    
private booleanreadTokenInfo(java.lang.String securityElementID)
Reads and parses EF(TokenInfo).

param
securityElementID identifies the security element specified by user
return
true if successful and WIM application is found
throws
IOException if the file is not found or there is some other IO error
throws
RuntimeException if there is an error parsing the file (e.g. its non-WIM PKCS#15 application and some mandatory for WIM fields are absent)


        files.select(TokenInfoPath);
        TLV t = new TLV(files.readFile(), 0);

        /*
         *  PKCS15TokenInfo ::= SEQUENCE {
         *      version        INTEGER {v1(0)} (v1,...),
         *      serialNumber   OCTET STRING,
         *      -manufacturerID PKCS15Label OPTIONAL,
         *      +label          [0] PKCS15Label OPTIONAL,
         *      +tokenflags     PKCS15TokenFlags,
         *      +seInfo         SEQUENCE OF PKCS15SecurityEnvironmentInfo
         *                      OPTIONAL,
         *      -recordInfo     [1] PKCS15RecordInfo OPTIONAL,
         *      +supportedAlgorithms [2]SEQUENCE OF PKCS15AlgorithmInfo
         *                      OPTIONAL,
         *      ... -- For future extensions
         *  }
         */

        t = t.child;    // version

        // it must be integer and it must be 0 (version 1)
        if (t.getInteger() != 0) {
            return false;
        }

        t = t.next;     // serial number
        if (t.type != TLV.OCTETSTR_TYPE) {
            return false;
        }
        serialNumber = Utils.hexNumber(t.data, t.valueOffset, t.length);

        // skip optional manufacturerID
        t = t.next.skipOptional(TLV.UTF8STR_TYPE);

        // it must be label
        if (t.type != 0x80) {
            return false;
        }

        String label = t.getUTF8();

        if (! (label.startsWith("WIM 1.01") &&
              (label.length() == 8 || label.charAt(8) == ' "))) {
            return false;
        }

        if (securityElementID != null &&
            label.indexOf(securityElementID) == -1) {
            return false;
        }

        t = t.next;     // Token flags. Check if this card is read-only.
        readOnly = t.checkFlag(0);

        t = t.next;     // seInfo
        // check if WIM_GENERIC_RSA SE is supported
        WIM_GENERIC_RSA_ID = -1;
        TLV v = t.child;
        while (v != null) {
            if (v.child.next.valueEquals(WIM_GENERIC_RSA_OID)) {
                WIM_GENERIC_RSA_ID = v.child.getInteger();
                break;
            }
            v = v.next;
        }

        if (WIM_GENERIC_RSA_ID == -1) {
            return false;
        }

        // skip otional recordInfo
        t = t.next.skipOptional(0xa1).child;

        // check if RSA signature is supported
        boolean supportsSignature = false;
        while (t != null) {
            TLV m = t.child;
            if (m.next.getInteger() == 0) {     // 0 - RSA
                RSA_ALGORITHM_ID = m.getInteger();
                supportsSignature = m.next.next.next.checkFlag(1);
                break;
            }
            t = t.next;
        }

        return supportsSignature;
    
public intremoveCredential(java.lang.String label, TLV isn)
Removes credential.

param
label the user friendly name associated with the certificate.
param
isn the DER encoded ASN.1 structure that contains the certificate issuer and serial number
return
operation result
throws
SecurityException if a PIN is blocked due to an excessive number of incorrect PIN entries


        // load existing certificates (excluding trusted - can't delete
        // them)
        try {
            loadCertificates(true, false);
        } catch (IOException e) {
            return SKIP;
        }

        Certificate cert = getCertificate(isn.child, isn.child.next);

        if (cert == null) {
            // there is no such certificate
            return SKIP;
        }

        // IMPL_NOTE: should there be a warning?
        // if (! cert.label.trim().equals(label)) {}

        // find the certificate chain
        Vector chain = getChain(cert, null, false);

        // this chain can have common certificates with some other
        // chains, in this case only part of the chain should be deleted
        int count = chain.size();
        check:
        for (int i = 1; i < chain.size(); i++) {
            for (int k = 0; k < Certificates.length; k++) {
                if (Certificates[k] != chain.elementAt(i - 1) &&
                    Certificates[k].isIssuedBy((Certificate)
                        chain.elementAt(i))) {
                    count = i;
                    break check;
                }
            }
        }

        try {
            if (MessageDialog.showMessage(securityToken,
                Resource.getString(ResourceConstants.AMS_CONFIRMATION),
                Resource.getString(ResourceConstants
                       .JSR177_CERTIFICATE_DELETED) +
                    "\n\n" + 
                    // "Label: " 
                    Resource.getString(ResourceConstants.
                        JSR177_CERTIFICATE_LABEL) + ": " +
                    cert.label + "\n\n" +
                    Certificate.getInfo(cert.cert) + "\n\n",
                true) == Dialog.CANCELLED) {
                return CANCEL;
            }
        } catch (InterruptedException e) {
            return CANCEL;
        }

        startUpdate();
        try {
            doRemove(chain, count);
            int pinStatus = checkPIN(PINs[0]);
            if (pinStatus == PIN_CANCELLED) {
                return CANCEL;
            }
            if (pinStatus == PIN_BLOCKED) {
                throw new SecurityException("PIN blocked");
            }
            if (pinStatus != PIN_DISABLED) {
                return ERROR;
            }
            doUpdate();
        } catch (IOException e) {
            return ERROR;
        }

//        typeInfo("RemoveCredential");
        return SUCCESS;
    
private voidresetLoader(java.util.Vector objects, java.util.Vector locations, java.util.Vector freeBlocks)
Initialises object loader.

param
objects vector for loaded objects or null
param
locations vector for object locations or null
param
freeBlocks vector for unused block locations or null

        loaderObjects = objects;
        loaderLocations = locations;
        loaderFreeBlocks = freeBlocks;
    
private java.util.VectorselectChain(java.util.Vector chains)
Allows user to select certificate or cancel signature operation.

param
chains array of certifcate chains
return
user selected certificate chain


        String[] labels = new String[chains.size()];
        for (int i = 0; i < chains.size(); i++) {
            labels[i] = ((Certificate)
                ((Vector) chains.elementAt(i)).elementAt(0)).label;
        }

        int chainIndex = -1;
        try {
            if (chains.size() == 1) {
                // if only one chain is found show certificate label to
                // the user

                if (MessageDialog.showMessage(securityToken,
                        Resource.getString(ResourceConstants
					   .AMS_CONFIRMATION),
                        Resource.getString(ResourceConstants
					   .JSR177_CERTIFICATE_USED) +
                        labels[0], true) != Dialog.CANCELLED) {
                    chainIndex = 0;
                }
            } else {
                // if more than one chain is found ask user to choose
                // one of them
                chainIndex = MessageDialog.chooseItem(securityToken,
                        Resource.getString(ResourceConstants
					   .JSR177_SELECT_CERTIFICATE),
                        Resource.getString(ResourceConstants
					   .JSR177_CERTIFICATES_AVAILABLE),
                        labels);
            }
        } catch (InterruptedException e) {}

        return (chainIndex == -1) ? null :
               (Vector) chains.elementAt(chainIndex);
    
private voidsetBlockLength(TLV t, int length)
Modify length of block in record of EF(UnusedSpace).

param
t TLV object that represents the record
param
length the new length value


        t = t.child.child.next.next;
        update(UnusedSpacePath,
               t.valueOffset, Utils.shortToBytes(length));
    
private voidsetBlockOffset(TLV t, int offset)
Modify offset in record of EF(UnusedSpace).

param
t TLV object that represents the record
param
offset the new offset value


        t = t.child.child.next;
        update(UnusedSpacePath,
               t.valueOffset, Utils.shortToBytes(offset));
    
private byte[]signData(PrivateKey key, byte[] data)
Sign given data using given key.

param
key private key
param
data data to be signed
return
signature prepended with one zero byte.
throws
IOException if I/O or crypto error occurs


        // calculate SHA-1 digest
        byte[] tmp = Utils.getHash(data, 0, data.length);

        // MSE - RESTORE
        apdu.resetCommand().
             sendCommand(INS_MSE, 0xf300 | WIM_GENERIC_RSA_ID, 0, true);

        // MSE - SET
        apdu.resetCommand().
            putByte(0x84).      // key reference tag
            putByte(0x1).       // length
            putByte(key.keyReference).  // value
            putByte(0x81).      // private key path tag
            putByte(key.path.length * 2);   // length
        for (int i = 0; i < key.path.length; i++) {     // value
            apdu.putShort(key.path[i]);
        }
        apdu.sendCommand(INS_MSE, 0x41b6, 0, true);

        // sign the data
        tmp = apdu.resetCommand().
                putBytes(DigestInfoHeader, 0, DigestInfoHeader.length).
                putBytes(tmp, 0, 20).
                sendCommand(INS_PSO, 0x9e9a);

        byte[] sign = new byte[tmp.length - 1];
        System.arraycopy(tmp, 0, sign, 1, tmp.length - 2);
        return sign;
    
private voidstartUpdate()
Initialises the new update.

        updateLocations = new Vector();
        updateData = new Vector();
        updatePIN = new boolean[PINs.length];
        updatePIN[0] = true;
    
private voidupdate(short[] path, int offset, byte[] data)
Registers file modification.

param
path file path
param
offset offset in the file
param
data data to be written

        updateLocations.addElement(new Location(path, offset, 0));
        updateData.addElement(data);