Code sample: Implementing a class to enable a communication session with a smart card


package com.rim.samples.device.smartcard;
import net.rim.device.api.crypto.*;
import net.rim.device.api.crypto.certificate.*;
import net.rim.device.api.crypto.certificate.x509.*;
import net.rim.device.api.crypto.keystore.*;
import net.rim.device.api.smartcard.*;
import net.rim.device.api.util.*;

/*******************************************************************************
* This class represents a communication session with a physical smart card.
*
* Application Protocol Data Units (APDUs) can be sent to the smart card over a
* session to provide the desired functionality.
* 
* Do not keep sessions open when you are not using them. They should be
* short-lived. As a security precaution, only one open session is allowed 
* per SmartCardReader.Subsequent openSession() requests will block until the 
* current session is closed.
*/

     public class MyCryptoSmartCardSession extends CryptoSmartCardSession
     {
     
        // We assume that the smart card has three certificates identified 
        // by: ID_PKI, SIGNING_PKI and ENCRYPION_PKI. Your smart card may have a
        // different number of certificates or be identified differently. These
        // three certificates are just an example of what might be stored on a
        // smart card.
        
     public static final byte ID_PKI             = (byte)0x00;
     public static final byte SIGNING_PKI        = (byte)0x01;
     public static final byte ENCRYPTION_PKI     = (byte)0x02;
     private static final String WAITING_MSG     = "Please Wait";
     private static final String ID_STRING       = "Jason Hood";
     private static final String ID_CERT         = "ID Certificate";
     private static final String SIGNING_CERT    = "Signing Certificate";
     private static final String ENCRYPTION_CERT = "Encryption Certificate";
     

     /**************************************************************************
     * Provide a constructor for the session.
     *
     * The smartCard argument is the smart card associated with this session.
     * The readerSession argument holds the reader session commands sent to this
     * smart card.
     ***************************************************************************/
     
     protected MyCryptoSmartCardSession(SmartCard smartCard, SmartCardReaderSession readerSession)
     {
          super( smartCard, readerSession );
     }
     
     /**************************************************************************
     * Provide a method to close the session.
     *
     * Do not close the underlying SmartCardReaderSession.Use this method to
     * clean up the session prior to its closure.
     ***************************************************************************
     /
     protected void closeImpl()
     {
        // Do any session cleanup needed here.
     }
     
     /**************************************************************************
     * Provide a method that returns the maximum number of allowed login 
     * attempts. Return Integer.MAX_VALUE to allow an infinite number of login
     * attempts.
     ***************************************************************************/
     
     protected int getMaxLoginAttemptsImpl() throws SmartCardException
     {
          return 5;
     }
     
     /**************************************************************************
     * Provide a method that returns the number of allowed login attempts
     * remaining (before the smart card will lock). Return Integer.MAX_VALUE if
     * the smart card will never lock.
     ***************************************************************************
     /
     protected int getRemainingLoginAttemptsImpl() throws SmartCardException
     {
          return 4;
     }
     
     /**************************************************************************
     * Provide a method that logs into the smart card with a specified password.
     * This method should not implement UI.
     ***************************************************************************/
     
     protected boolean loginImpl( String password ) throws SmartCardException
     {
        // Create the APDU command for your smart card. Consult documentation 
        // from your smartcard vendor.
        
          CommandAPDU command = 
           new CommandAPDU( (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00 );
          command.setLcData( password.getBytes() );
          ResponseAPDU response = new ResponseAPDU();
          sendAPDU( command, response );
     
        // Check for response codes specific to your smart card.
          if ( response.checkStatusWords( (byte)0x90, (byte)0x00 ) ) 
          {
               return true;
          }
          else if ( response.checkStatusWords( (byte)0x64, (byte)0xF8 ) ) 
          {
               throw new SmartCardLockedException();
          }
          else 
          {
             // Authentication failed
               return false;
          }
     }
     
     /**************************************************************************
     * Provide a method that returns an ID for the smart card associated with
     * this session.
     ***************************************************************************/
     protected SmartCardID getSmartCardIDImpl() throws SmartCardException
     {
          // Retrieve a unique ID from the card
          
          byte [] uniqueCardData = new byte[] 
          { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b };
     
          // Convert the byte array to a long
          
          SHA1Digest digest = new SHA1Digest();
          digest.update( uniqueCardData );
          long idLong = byteArrayToLong(Arrays.copy(digest.getDigest(), 0, 8));
     
          return new SmartCardID( idLong , ID_STRING, getSmartCard() );
     }


     /**************************************************************************
     * Private method to convert a byte array into a long integer. Note that the
     * return value should be interpretted as an unsigned value.
     * 
     * The method throws an IllegalArgumentException if the byte array contains
     * a number larger than 64 bits.
     * 
     * Note: If your smart card driver is only designed to work with BlackBerry
     * Version 4.2 or later, you can replace this method with a call to
     * CryptoByteArrayArithmetic.valueOf( byte [] ).
     **************************************************************************/
     
     private long byteArrayToLong( byte[] array )
     {
          if ( array == null ) 
          {
               throw new IllegalArgumentException();
          }
          // Remove the leading zeros from the specified byte array and return
          // a new byte array without them.
          
          int zeros = 0;
          for ( int i = 0; i < array.length && array[i] == 0; i++ )
          {
               zeros++;
          }
     
          if ( zeros != 0 ) 
          {
               array = Arrays.copy( array, zeros, array.length - zeros );
          }
          int length = array.length;
          
          if( length > 8 ) 
          {
               throw new IllegalArgumentException();
          }
          long n = 0;
          
          for( int i=0; i<length; i++ ) 
          {
               n <<= 8;
               n += array[i] & 0xff;
          }
          return n;
     }
     
     
     /**************************************************************************
     * Provide a method that returns random data from the smart cards internal
     * random number generator.
     ***************************************************************************/
     protected byte [] getRandomBytesImpl( int maxBytes ) throws SmartCardException
     {
          // Create the appropriate CommandAPDU for your smart card. See your
          // smart card vendor's documentation.
          
          CommandAPDU command = 
           new CommandAPDU((byte)0x00,(byte)0x4C,(byte)0x00,(byte)0x00,maxBytes);
          ResponseAPDU response = new ResponseAPDU();
          sendAPDU( command, response );
          
          // Check for response codes specific to your smart card
          
          if( response.checkStatusWords( (byte)0x90, (byte)0x00 ) ) 
          {
          // return the response code containing the random data
               return response.getData();
          }
          return null;
     }
     
     /**************************************************************************
     * Provide a method that returns certificates from the smart card.
     * Return an array containing copies of certificates on the card.
     ***************************************************************************/
     protected CryptoSmartCardKeyStoreData[] getKeyStoreDataArrayImpl() 
      throws SmartCardException, CryptoTokenException
     {
          try 
          {
               // Display a progress dialog because this operation may take a long
               // time.
               
               displayProgressDialog( WAITING_MSG, 4 );
               
               // Associate the certificates with a particular card.
               
               SmartCardID smartCardID = getSmartCardID();
               RSACryptoToken token = new MyRSACryptoToken();
               RSACryptoSystem cryptoSystem = new RSACryptoSystem( token, 1024 );
               RSAPrivateKey privateKey;
               CryptoSmartCardKeyStoreData[] keyStoreDataArray = new
                CryptoSmartCardKeyStoreData[ 3 ];
          
               // This encoding would be extracted from the card using a series of
               // APDU commands.
          
               Certificate certificate = null;
               
               // Extract the certificate encoding from the card.
          
               byte [] certificateEncoding = new byte[0];
               try 
               {
                    certificate = new X509Certificate( certificateEncoding );
               }
               catch( CertificateParsingException e )
               { 
                    // invalid X509 certificate 
               }
               }
          
               stepProgressDialog( 1 );
               privateKey = new RSAPrivateKey(cryptoSystem, 
                 new MyCryptoTokenData( smartCardID, ID_PKI ) );
               keyStoreDataArray[ 0 ] = 
               new CryptoSmartCardKeyStoreData( null, ID_CERT, privateKey, null, 
                 KeyStore.SECURITY_LEVEL_HIGH, certificate, null, null, 0 );
               
               stepProgressDialog( 1 );
               privateKey = new RSAPrivateKey( cryptoSystem,
                 new MyCryptoTokenData( smartCardID, SIGNING_PKI ) );
               keyStoreDataArray[ 1 ] = 
               new CryptoSmartCardKeyStoreData( null, SIGNING_CERT, privateKey,
                 null, KeyStore.SECURITY_LEVEL_HIGH, certificate, null, null, 0 );
               
               stepProgressDialog( 1 );
               privateKey = new RSAPrivateKey( cryptoSystem,
                new MyCryptoTokenData( smartCardID, ENCRYPTION_PKI ) );
               keyStoreDataArray[ 2 ] = new CryptoSmartCardKeyStoreData( null, 
                 ENCRYPTION_CERT, privateKey, null, 
                 KeyStore.SECURITY_LEVEL_HIGH, certificate, null, null, 0 );
               
               stepProgressDialog( 1 );
               
               // Sleep so the user sees the last step of the progress dialog
               // as it moves to 100%
               
               try 
               {
                    Thread.sleep( 250 );
               } 
               catch ( InterruptedException e ) 
               {
               }
               dismissProgressDialog();
               return keyStoreDataArray;
          } 
          catch ( CryptoUnsupportedOperationException e ) 
          {
          } 
          catch ( UnsupportedCryptoSystemException e ) 
          {
          } 
          catch ( CryptoTokenException e ) 
          {
          }
          throw new SmartCardException();
     }
     
     /**************************************************************************
     * Send some data to the smart card for signing or decryption.
     ***************************************************************************/
     /*package*/ void signDecrypt( RSACryptoSystem cryptoSystem, 
       MyCryptoTokenData privateKeyData,byte[] input, int inputOffset,
       byte[] output, int outputOffset ) throws SmartCardException
     {
     // Check for nulls
     if ( cryptoSystem == null || privateKeyData == null || input == null || output == null) {
     throw new IllegalArgumentException();
     }
     // Validate the input parameters
     int modulusLength = cryptoSystem.getModulusLength();
     if ( ( input.length < inputOffset + modulusLength ) || ( output.length < outputOffset + 
     modulusLength ) ) {
     throw new IllegalArgumentException();
     }
     // Construct the response Application Protocol Data Unit
     ResponseAPDU response = new ResponseAPDU();
     // Construct the command and set its information
     // Create a CommandAPDU which your smart card will understand
     CommandAPDU signAPDU = new CommandAPDU( (byte)0x80, (byte)0x56, (byte)0x00, 
     (byte)0x00, modulusLength );
     signAPDU.setLcData( input, inputOffset, input.length - inputOffset );
     // Send the command to the smart card
     sendAPDU( signAPDU, response );
     // Validate the status words of the response
     // Check for response codes specific to your smart card
     if ( response.checkStatusWords( (byte)0x90, (byte)0x00 ) ) {
     byte [] responseData = response.getData();
     System.arraycopy( responseData, 0, output, outputOffset, responseData.length );
     }
     else {
     throw new SmartCardException( “Invalid response code, sw1=” + Integer.toHexString( 
     response.getSW1() & 0xff ) + “ sw2=” + Integer.toHexString( response.getSW2() & 0xff ) );
     }
     }
}

Was this information helpful? Send us your comments.