Overall the JCE is more a software based crypto-solution i.e. the concept was not designed to use cryptographic hardware. Nevertheless we implemented an own crypto provider based on smartcards where most of the cryptographic calculations all secure operations are completely executed on the smartcard. Public operations like verifying a signure may be done in software. are executed in hardware. For this purpose we desgined an own SDK extending providers as smartcard-providers. In this chapter we give an overview of our basic concepts and all extra-features offered by smartcard-providers.

SDKs

For future purposes we designed two SDKs. Both of them define base classes and implement basic functionality. The SDKs are:

  • cgSDK CORE
    The CORE SDK is completely independent and defines the interfaces for the smartcard layer. Furthermore it implements reader managing services and defines basic data types.
  • cgSDK JCE
    The JCE SDK uses the CORE SDK and specifies the interfaces mandatory for the JCE layer. Furthermore it implements the Java SPI classes mentioned in 1.3 but does not implement any algorithm. Compared to usual SPI implementations they behave more general and forward function calls to specialized implementations. For this purpose we specified a set of own SPI interfaces (see 2.3.1 for details) which shall be implemented by those specialisations. 
    The JCE SDK already implements the SmartCardProviders which serve the JCE. 

Figure 1 shows how the SDKs are layered. On top there is an application which wants to make use of the bottomed smartcard-layer. Between there are our SDKs which handle the requests in conjunction with the Java JCE and the the layers which implement our interfaces.
The CORE SDK is independent and has no knownledge about JCE functionality. Its main purpose is to manage smartcard readers and communicate with smartcards.
The JCE SDK uses the CORE SDK and serves the JCE where it does not need to know anything about how the CORE SDK communicates with smartcards.
Due to this design decision we are be able to switch both implementation layers without changing the other.



Figure 1: abstraction layer / SDKs

Architecture

Before going into detail we first have to discuss the JCE's architecture rougly depicted in Figure 2. On the left side there are the standard JCE classes where our special implementations are on the right. Providers Provider / AuthProvider always contain the algorithms they provide as services. An algorithm is implemented in an extended SPI class and then connected to a service. A service is then added to a provider.


For extending the JCE with smartcard functionality we developed two smartcard concepts explained in 2.3. The SmartCardCryptoFactory handles the usual functionality offered by the standard JCE where the SmartCardExtension brings new functionality dedicated for smartcards (e.g. changing the PIN, unlock the user PIN, etc. …). For both concepts we created interfaces which are used by our SmartCardAuthProvider.
Figure 3 gives a deeper look at the architecture and the SDK barriers. On top there is the JCEImpl layer which implements the JCE SDK's interfaces and all necessary algorithms. Furthermore it implements a SmartCardAuthProviderManager which automates the whole creation & registration process of providers (more in 3.3).


The JCE- and the CORE layer define interfaces for the top and bottom layers. They implement the providers and other managing classes which are described in 3.
On the hardware layer also called the SmartCard Impl we handle the communication with the smartcard and serve the interfaces defined in SDK CORE. We decided to use cgPKCS#11 which already handles smartcard communication in conjunction with PCSC-lite.



Figure 2: System architecture JCE / JCE SDK




Figure 3: roughly overview of SDKs

Concepts

In order to implement smartcard based providers we developed two different concepts – one for handling usual JCE tasks and another extending a provider's functionality with smartcard specific features. The SmartCardCryptoFactory covers the JCE features while the SmartCardExtension brings the extra functionality. In the following we handle both concepts.

SmartCardCryptoFactory

The SmartCardCryptoFactory is a concept which serves the standard JCE functionality. The factory cooperates with the CORE SDK and has the following tasks:

  • Query the CORE SDK which algorithms are supported by the smartcard:
    • getSupportedStreamCiphers
    • getSupportedSingleBlockCiphers
    • getSupportedKeyPairGens
    • getSupportedKeyGens
    • getSupportedSignatures
    • getSupportedDigests

Those functions class-specifically fetch the smartcards supported algorithms and return them to the caller.


  • Create crypto instances which can be forwared to the JCE:
    • getStreamCipher
    • getSingleBlockCipher
    • getKeyGenerator
    • getKeyPairGenerator
    • getSignature
    • getSecureRandom
    • getKeyStore
    • getDigest

Those functions create algorithm specific objects which are transferred to the corresponding generalized SPI class mentioned in 2.1. To be more precise each function is called with the demanded algorithm as parameter. SmartCardCryptoFactory's task is then to create the matching instance and return it to the caller.


The SmartCardCryptoFactory uses the SmartCardCryptoFunctions- and SmartCardPersistentMemoryFunctions-interfaces defined in the CORE SDK. Both interfaces need to be implemented at the smartcard layer.
The JCE SDK already implements generalized SPI classes for all the ones mentioned in 1.3. Usually someone has to extend from one of the Java SPI classes in order to implement a single algorithm. Our SPI classes are implemented more generally and not algorithm specific since they shall support dynamic loading i.e. when a provider detects that a card is present it querys which hardware algorithms are supported by the smartcard and adds them as its provided services. Of course we could implement each supported algorithm as own SPI class but we found a solution making our providers quite more flexible.
First we analized the Java SPI classes and extracted all abstract functions to own SPI interfaces (com.AirID.sdk.jce.crypto.interfaces). These interfaces are defined within the JCE SDK. Then we extended and implemented the Java SPI classes (com.AirID.sdk.jce.crypto.spis) which later make use of the objects implementing the SPI interfaces. For provider implementations it is usually necessary to exactly know which algorithms are supported. Our design avoids that circumstance and allows us to dynamically load the smartcards supported algorithms. Furthermore this design allows us that the JCE SDK already provides the whole logic for implementing a provider – only the implementations of our SPI interfaces need to be additionally delivered. This gives us the opportunity to deliver pre-signed code running on all machines on Oracle Java systems it is necessary to sign the provider with an Oracle signed certificate. Otherwise Cipher SPI objects will not operate with the Oracle Java JCE!. Figure 4 illustrates the hirarchie of the concept. See 2.4.5 for more details.


Figure 4: SmartCardCryptoFactory hirarchie

SmartCardExtension

The SmartCardExtension is a concept which extends the functionality of a SmartCardAuthProvider with additional features. These features are:

  • getRemainingAttempts
    This function returns an enum representing how many login attemps can be executed until the card is locked. These values represent "at least 2", "last try" and "locked". Unfortunately there is no indicator we are using cgPKCS#11 for smartcard communication. PKCS#11 does not provide better information regarding PIN retry attempts. which enables us to differentiate between two or three remaining tries.
  • verifyPIN
    This function validates the PIN for a specific user (SO / USER).
  • changePIN
    Changes the PIN for a specific user (SO / USER).
  • overwriteUserPIN
    Overwrites the users PIN.
  • getFirmwareVersion
    Returns the firmware version of the smartcard.
  • getManufacturerID
    Returns the manufacturer ID of the smartcard.
  • getSerialNumber
    Returns the smartcards serial number.
  • getCardType
    Returns the type of smartcard.
  • getReaderName
    Returns the reader name.
  • getFreeMemorySizeByte
    Returns the size of free memory in bytes.
  • getSupportsRNG
    Returns whether the smartcard supports a hardware random number generator.
  • registerCardEventObserver
    Registers an observer which will receive smartcard events.
  • isCardPresent
    Returns whether a smartcard is currently inserted.

The SmartCardExtension uses the SmartCardAdministrationFunctions- and SmartCardInfoFunctions-interfaces defined in the CORE SDK. Both interfaces need to be implemented at the smartcard layer.

In order to verify, change or overwrite / unlock PINs the smartcard is not allowed to be in private state. For this reason re-load a potential privatly loaded keystore to public mode and ensure that no pending private operation is ongoing.


SmartCardAuthProvider

A SmartCardAuthProvider is extended from the standard Java class AuthProvider and uses the concepts explained in 2.3. Nevertheless there are a few more concepts which are discussed within the next subchapters.

Reader

The CORE SDK provides an easy to use reader concept handled by the ReaderManager (see 3.1). As soon as a reader is reserved a SmartCardAuthProvider shall bind it i.e. when more readers are available potentially more than one SmartCardAuthProvider can be registered to the Java system (see 3.3).
During the lifetime of a SmartCardAuthProvider the reader never changes. The reader can be retrieved by calling getReader while calling getReaderState gives information about whether a smartcard is inserted currently or not.
A SmartCardAuthProvider further on listens only to that reader and reacts to ReaderEvents.

ReaderEvent

We designed SmartCardAuthProviders to react on three different reader events:

  • smartcard removal
    When a smartcard is removed the SmartCardAuthProvider cancels all pending operations and unregisters it's services. Furthermore all returned SPI objects (i.e. previously returned services) e.g. a cipher and a keystore are locked and resetted (buffers & states). Potentially cashed PINs are also cleared. Trying to use such a locked object will always fail with an exception when the SPI object's function is able to return an exception. Otherwise null is returned or nothing happens..
  • smartcard insertion
    When a smartcard is inserted the SmartCardAuthProvider starts interacting. First all card supported features are fetched and registered as services. Then the provider transmits the smartcards serialnumber to all previously returned objects. The objects compare the serial with the one once used for creation and unlock when the serials match. Unlocked objects may need to be re-initialized (cashed PINs & states are gone). A potentially returned keystore re-loads all contained objects in public mode. See more about keystore modes in 4.2.
  • reader removal
    When the reader is removed the provider unregisters itself at the SmartCardAuthProviderManager and releases it's services this only happens when the SmartCardAuthProvider was once registered to the ReaderManager. The registration is executed as soon as the SmartCardAuthProvider is used the very first time.. All objects returned by the provider are no more usable and locked forever.

Furthermore a SmartCardAuthProvider can forward these events to all observer classes. For receiving such an event register your observer via addObserver. For unregistering a previously registered observer call removeObserver. Observers (standard Java Interface) will receive ReaderStateBundles which contain the new ReaderState and an instance of Smartcard (null when card or reader where removed) as soon as an event happens.

Handlers / Callbacks

Typically there are several types of standard callbacks where in fact just one plays a special role for our implementation – the password-callback. The password-callback is a standard Java class which is used to prompt a notification to - and return a password from - a user. The way how the user enters his password is chosen by the handler which is implementation specific.
For our implementation we distinguish between three handlers:

  • The default callback handler
    This handler is implemented by us which outputs the prompt on System.out and reads a password via System.in.
  • The application callback handler
    This handler is implementer specific and can be set via setCallbackHandler.
  • The login callback handler
    This handler is implementer specific and is temporarily set via login. The handler stays active as long as logout has not been called.

All three handlers have different priorities. As long as setCallbackHandler has not been called (or was called with null) the default callback handler stays active. When a callback handler was set via setCallbackHandler the application callback handler stays active as long as no login occurs. When login was called (with handler != null) the login callback handler stays active as long as logout has not been called. When logout was called the application callback handler is active again or the default callbackhandler when no application callback handler was set. To make a long story short – the login callback handler has the highest priority where in contrary the default login handler has the lowest.


The password-callback is used to query the user for the smartcard's PIN. The PIN is needed as soon as the smartcard operates with private objects such as private- or secret keys. The PIN can also be cached.

PIN caching

During a login / logout session the SmartCardAuthProvider will just ask once for the PIN and will reuse it for all private operations only when the entered PIN was correct..
The same applies for the KeyStore. As soon as the keystore's load function is called with a valid PIN no password-callback will be triggered until load is called again or the card is removed and reinserted.
Additionally readers can be configured that the PIN is always cached as soon as it was once entered successfully. The cache is active as long as the smartcard stays within the reader. On removing the card all PIN caches are cleared. This also happens when the reader is removed or detached by ReaderManager. This cache is activated on reader-attachment. For this purpose set the appropriate flag to true when attaching the reader (see 3.1) enabling the PIN cache.
When PIN caching is disabled and no login / logout session is active the user is always prompted for the PIN as soon as a cryptographic operation shall be executed.

SmartCardService

As we already learned providers offer specific services which can be retrieved by calling the getInstance function of the several JCE's SPI classes. Usually a provider is pre-defined i.e. an implementer plans to support specific services / algorithms and adds them to his / her provider. Then the provider is shipped e.g. within a library. Someone who wants to make use of the provider creates an instance of it and registers the provider to the system there is also a way to activate the provider statically for all programs. To do so the provider has to be installed into the Java archive. For more information see 5.1.1.. Afterwards the defined services can be retrieved and used as described in 1.3.
We implemented our providers more generally i.e. we did not pre-define which algorithms we support. In fact our provider itself makes the desission which algorithms it supports during runtime. Therefore it uses the CORE SDK to communicate with the reader it's bound to and asks whether a smartcard is inserted. When a smartcard is inserted the CORE SDK returns all available algorithms supported by the smartcard. Afterwards these algorithms are registered as the providers services.
Usually the registration needs fixed classes where each one implements a single algorithm. Also our provider works this way but indeed it adds always the same SPI class type. The following example will give some clarity:
Someone plans to implement a provider offering a RSA - and an EC keygenerator. Therefore he / she would extend from Java KeyPairGeneratorSpi two times:

  • One for the RSA keygenerator implementation called class A.
  • Another for the EC keygenerator implementation called class B.

Both classes would be implemented algorithm specific and then added via

  • provider.putService(new Service(this, "KeyPairGenerator", "RSA", class A, null, null));
  • provider.putService(new Service(this, "KeyPairGenerator", "EC", class B, null, null));

Our provider is working differently since it shall support dynamic algorithm loading. Lets use the given example in order to explain that.
For each supported SPI class we implemented a generalized class like KeyPairGenSpi in our JCE SDK. This class needs an instance of our CryptoKeyPairGenerator interface which is received during runtime.
So for this example someone would need to implement two classes implementing the CryptoKeyPairGenerator interface:

  • One for the RSA keygenerator implementation called impl A.
  • Another for the EC keygenerator implementation called impl B.

Both classes would be implemented algorithm specific and then added via

  • provider.putService(new Service(this, "KeyPairGenerator", "RSA", KeyPairGenSpi, null, null));
  • provider.putService(new Service(this, "KeyPairGenerator", "EC", KeyPairGenSpi, null, null));

The KeyPairGenSpi then has to decide during runtime which instance – impl A or impl B – shall be used. So another step would be to register the "impls" at KeyPairGenSpi.
For this reason we implemented the SmartCardService class and another interface called SmartCardProviderSpi. The SmartCardService receives an object type called Algorithm instead the algorithm name like "RSA" or "EC" and extends from Service. SmartCardService has an overloaded newInstance function which makes use of the SmartCardProviderSpi interface implemented in KeyPairGenSpi. Instead of putting a service we then put a SmartCardService like this way:
provider.putService(new SmartCardService(this, "KeyPairGenerator", keyPairGen, KeyPairGenSpi, null, null));
where keyPairGen is an instance of type Algorithm which is dynamically received from the smartcard via the CORE SDK. The newInstance function of SmartCardService then later uses the SmartCardProviderSpi's declared setSpiProperties function implemented in KeyPairGenSpi which communicates with the SmartCardCryptoFactory in order to receive the correct algorithm instance of type CryptoKeyPairGenerator indicated in keyPairGen.
In fact we even more simplified the necessity to implement impl A and impl B. Actually we only need to implement one impl per interface! See 2.4.6 for more information.
The "KeyPairGenSpi --uses--> CryptoKeyPairGenerator"-concept applies to all of our supported SPIs (see 1.3).

Crypto Impls

So far we have learned most of our concepts which turn usual AuthProviders into SmartCardAuthProviders. Both SDKs – the CORE SDK and the JCE SDK – implement most of the logic used for provider creation / registration. On the top layer we implemented all necessary interfaces required for the JCE SDK. Besides the SmartCardExtension and the SmartCardCryptoFactory these are the Crypto-interfaces listed in Table 1.
We implemented a class – called the "impl" – for each of the interfaces. Each impl is able to communicate with the smartcard and uses whether the SmartCardCryptoFunctions- and / or the SmartCardPersistensMemoryFunctions-interface depicted in Figure 3 the names of the interfaces where shortened there. SmartCardCryptoFunctions map to SmartCardCrypto, SmartCardPersistensMemoryFunctions to SmartCardMemory. . Those two interfaces satisfy all the needs of the impls. Table 1 additionally shows how the interfaces are used by which impl.


Interface

Used Functions

CryptoCipher

SmartCardCryptoFunctions->decrypt
SmartCardCryptoFunctions->encrypt
SmartCardCryptoFunctions->wrapKey
SmartCardCryptoFunctions->unwrapKey

CryptoKeyGenerator

SmartCardCryptoFunctions->generateKey

CryptoKeyPairGenerator

SmartCardCryptoFunctions->generateKeyPair

CryptoKeyStore

SmartCardCryptoFunctions->registerSmartCardKeyGenerationObserver
SmartCardPersistentMemoryFunctions->createObject
SmartCardPersistentMemoryFunctions->deleteObject
SmartCardPersistentMemoryFunctions->modifyObject
SmartCardPersistentMemoryFunctions->setCachedPIN
SmartCardPersistentMemoryFunctions->findPublicObjects
SmartCardPersistentMemoryFunctions->findPrivateObjects
SmartCardPersistentMemoryFunctions->loadPrivate
SmartCardPersistentMemoryFunctions->loadPublic

CryptoMessageDigest

SmartCardCryptoFunctions->hash

CryptoSecureRandom

SmartCardCryptoFunctions->generateRandom
SmartCardCryptoFunctions->seedRandom

CryptoSignature

SmartCardCryptoFunctions->sign
SmartCardCryptoFunctions->verify


Table 1: crypto interfaces - impls - mapping to smartcard interfaces


Recently we mentioned that each algorithm – determined for being offered by the provider – should be implemented in an own impl. Since the SmartCardCryptoFunctions-interface functions always carry the intended algorithm even the impls only need to be implemented once as we did this for the SPIs per SPI class because they can potentially forward the demanded algorithm. This leads us to the point that the lowest layer needs to be able to switch between the algorithms since there is no algorithm specific implementation on the upper layers. More precisely this means that the bottom layer has to implement the algorithms used for the services usualy – as we learned – this would be achieved by implementing serveral SPIs.


We decided to use cgPKCS#11 at the bottom layer since this avoids implementing algorithm specific APDU commands for all different crypto features offered by the smartcard. PKCS#11 exactly does this job for us and switches automatically between the distinct algorithms by using pre-defined algorithm IDs. Also all the other CORE SDK features offered by our interfaces can be implemented by using PKCS#11.

  • No labels