Symbian
Symbian Developer Library

SYMBIAN OS V9.4

Feedback

[Index] [Previous] [Next]


The unified key store

[Top]


Introduction

The key store (keystore) provides functionality to:

The keystore comprises an API and a software implementation. The software implementation uses a client/server architecture, and stores keys in encrypted form in the filesystem. (Keys are stored and imported/exported in PKCS#8 format, and optionally encrypted with PKCS#5. In a permanent stream store each key is stored as a separate stream.) Partners can supply their own hardware-based implementations of the API that may be used seamlessly by application code.

[Top]


Key store concepts


Key info - CCTKeyInfo

Detailed information about a key is represented as a CCTKeyInfo object. Objects of this type are passed back to the client by MCTKeyStore::List() and MCTKeyStore::GetKeyInfo() operations. The class is defined in the MCTKeyStore.h header file. It has accessors for the following attributes:

Attribute Description

ID

A SHA-1 hash of the public key, used to uniquely identify it in the store

Usage

A bitfield representing the allowed usages of the key. This is of type TKeyUsagePKCS15, defined in securitydefs.h

Size

The size of the key in bits

Label

A text string to identify the key to users

Owner

The UID of the application that created or imported the key

Users

A list of UIDs of applications that are allowed to use the key

Algorithm

The key algorithm: RSA, DSA or DH

AccessType

A bitfield that determines the sensitivity of the key. See the EKeyAccess enum

Native

Indicates whether cryptographic operations involving the key are carried out within the keystore implementation itself (i.e., in hardware if the keystore is implemented in hardware)

StartDate

The start of the validity period of the key

EndDate

The end of the validity period of the key

PKCS8AttributeSet

Additional key attributes as a DER-encoded set

Protector

An MCTAuthenticationObject that can be used to access the authentication mechanism for the keystore

Token

An MCTToken object that represents the actual keystore implementation the key is in. It is possible to use this to get access to the keystore implementation, but this is not recommended.

Handle

The object ID part of the object handle; an integer that is unique amongst keys in this token.

These attributes are set when the key is created or imported, and cannot subsequently be changed by the client.

CCTKeyInfo has a conversion operator to TCTTokenObjectHandle, so instances of this class can be passed to any functions in the unified keystore requiring a token object handle.


Authentication objects - MCTAuthenticationObject

See the API reference documentation for the MCTAuthenticationObject class.

Authentication objects are used to control the authentication mechanism of the keystore; they are accessed by calling the CCTKeyInfo::Protector() function. The authentication object returned is owned by the keystore implementation and you should not attempt to free it yourself - this is managed automatically. They may also be shared between keys - i.e., the same object may be returned for more than one key. The software keystore implementation works like this - it associates one authentication object instance with each key owner application. In other words, every key with the same owner has the same authentication object. This is because authentication properties like the passphrase timeout are set on a per-owner basis in this implementation. (See the Ownership and usership and Changing the passphrase sections below.)

[Top]


Creating a CUnifiedKeyStore

See the API reference documentation for theCUnifiedKeyStore class.

The object is created in the standard Symbian OS way, by calling the static CUnifiedKeyStore::NewL() or CUnifiedKeyStore::NewLC() functions, and then must be initialised by calling the asynchronous CUnifiedKeyStore::Initialize() function.

For example, a client might use the following code to create and initialise a unified keystore object:

void CKeyStoreExample::CreateUnifiedKeystoreL()
    {
    // iKeyStore is a CUnifiedKeyStore* 
    // iFS is an RFs (file server client)
    // iState is an enum that records the state of the active object

    iKeyStore = CUnifiedKeyStore::NewL(iFs);
    iKeyStore->Initialize(iStatus);
    iState = EInitializingKeystore;
    SetActive();

    // RunL() called when this completes
    }

[Top]


Finding keys

The unified keystore allows a client to search all keys on a device regardless of which physical store they are in. A filter object is specified that determines which keys are returned - you can filter on:

Description

The key indentifier

Used when searching for a particular key

The key usage

Used when searching for a key usage, e.g., encryption

The key owner UID

Used when searching for a key owner.

Applications should use this to prevent them seeing insecure keys that might have been added by a malicious application.

As well as a UID, you can also specify KAnyUsableKey and KAnyKeyIncludingUnusable

The key algorithm

Used when searching for a particular key algorithm, e.g., RSA.

EInvalidAlgorithm indicates "don't care".

All of these default to "don't care". So for instance, an application that wishes to find all DSA keys owned by a certain application that are usable for signing might do the following:

void CKeyStoreExample::FindSomeKeys()
{
    // KApplicationUID is the UID of the key owner application
    // iKeys is an RMPointerArray<CCTKeyInfo> that is filled with the keys found

    TCTKeyAttributeFilter filter;
    filter.iOwner = KApplicationUID;
    filter.iUsage = EPKCS15UsageSign;
    filter.iKeyAlgorithm = CCTKeyInfo::EDSA;

    iKeyStore->List(iKeys, filter, iStatus);
    iState = EFindingKeys;
    SetActive();

    // RunL() called when this completes
}

[Top]


Performing cryptographic operations with keys

Performing a cryptographic operation is a two stage process. Firstly, the key must be "opened", which creates an object capable of performing the required operation. Secondly, the new object's functions are called to actually execute it. Below is a list of the supported operations together with the objects created:

Operation Object created

DSA Sign

MDSASigner

RSA Sign

MRSASigner

RSA Decrypt

MCTDecryptor

DH agreement

MCTDH

These interfaces are defined in the header file mkeystore.h.

The created objects are owned by the client, and hence must be disposed of by calling their Release() function when they are no longer required.

To perform an RSA signing operation, the key is opened to create an MRSASigner object. This object supports two signing functions:

Function Description

SignMessage()

Computes a SHA-1 digest of the input data and signs that

Sign()

Takes as input a message digest to sign (i.e., does a raw RSA sign operation).

These signing functions create a CRSASignature object as output.

For example, to RSA sign some data in iDataToSign:

void CKeyStoreExample::RSASign(const CCTKeyInfo& aKey)
{
    // iRSASigner is an MRSASigner*
    // iDataToSign is a HBufC8* containing data to be signed
    // iRSASignature is a CRSASignature* which will contain the result

    iKeyStore->Open(aKey, iRSASigner, iStatus);
    iState = EOpenRSAKeyForSigning;
    SetActive();
}

void CKeyStoreExample::RunL()
{
    switch (iState)
    {
        ...

        case EOpenRSAKeyForSigning:
            PerformRSASignOperation();
            break;

        ...
    }
}

void CKeyStoreExample::PerformRSASignOperation()
{
    iRSASigner->SignMessage(*iDataToSign, iRSASignature, iStatus);
    iState = EPerformRSASignOperation;
    SetActive();

    // RunL called again when this completes
}

DSA signing works much the same as for RSA signing described above. The only difference is that the Open() function creates an MDSASigner object (The Open() functions are overloaded on the type of the created object). The DSA signer differs from an MRSASigner only in that the signing function creates a CDSASignature object.

RSA encryption and DH (Diffie-Hellman) key agreement are analogous.

[Top]


Generating keys

For this operation it is necessary to know in advance which of the keystore implementations on the device the new key should be stored in. The application could ask the user for this information, or it could choose a store itself based on some policy. Either way, the available keystores that support write operations can be found using the KeyStoreManagerCount() and KeyStoreManager() accessor functions.

A keystore manager object is a token interface (see MCTTokenInteface defined in mcttokeninterface.h), and from this it is possible the get the token via the Token() function and hence the token label. So, in order to ask the user which keystore they wanted a key to be created in, a list of the labels of available keystores could be built up like this:

void CKeyStoreExample::GetKeyStoreManagerLabelsL()
{
    // iLabelList is a RPointerArray<HBufC8>

    for (TInt index = 0 ; index < iKeyStore->KeyStoreManagerCount() ; ++index)
    {
        MCTKeyStoreManager& manager = iKeyStore->KeyStoreManager(index);
        MCTToken& token = manager.Token();
        HBufC* label = token.Label().AllocLC();
        User::LeaveIfError(iLabelList.Append(label));
        CleanupStack::Pop(label);
    }
}

Once the index of the keystore manager has been determined, the key can be created by calling the CreateKey() function. Among other things it takes the following parameters that are a subset of the attributes of a CCTKeyInfo:

Parameter Description

aUsage

The key usage flags in the PKCS#15 format

aSize

The key size in bits

aLabel

The textual label for the key

aAlgorithm

The key algorithm - RSA, DSA, or DH

aAccessType

The key's access type bitfield. Only two of the bits defined may be set when creating a key - EExtractable and ESensitive

aStartDate

The start of the validity period of the key

aEndDate

The end of the validity period of the key.

For example:

void CKeyStoreExample::CreateRSAKey(TInt aKeyStoreIndex)
{
    // iKey is a CCTKeyInfo* that will be set if the function succeeds

    _LIT(KMyKeyName, "Example key");

    TTime startDate, endDate;
    startDate.HomeTime();
    endDate.HomeTime();
    endDate += TTimeIntervalYears(1); // key valid for a year

    iKeyStore->CreateKey(
                    aKeyStoreIndex,
                    EPKCS15UsageSign,
                    1024,
                    KMyKeyName,
                    CCTKeyInfo::ERSA,
                    CCTKeyInfo::EExtractable,
                    startDate,
                    endDate,
                    iKey,
                    iStatus);
    iState = ECreateKey;
    SetActive();

    // RunL called when this completes
}

[Top]


Importing keys

Importing keys is very similar to creating keys, except that the ImportKey() function takes an additional two parameters:

Parameter Description

aKeyData

The key data in PKCS#8 format

aIsEncrypted

Whether the key data is PKCS#5 encrypted.

[Top]


Deleting keys

Keys can be deleted with the DeleteKey() function, for example:

void CKeyStoreExample::DeleteKey()
{
    // iKey is a CCTKeyInfo*

    iKeyStore->DeleteKey(*iKey, iStatus);
    iState = EDeleteKey;
    SetActive();
    
    // RunL called when this completes
}

[Top]


Getting the public key

The ExportPublic() function allows the public key to be exported, in DER-encoded ASN.1 format. To export an RSA public key, and then reconstruct a useful key object from it, an application might do the following:

CKeyStoreExample::ExportRSAPublicKey()
{
    // iKey is a CCTKeyInfo*
    // iPublicKeyData is an HBufC8*

    iKeyStore->ExportPublic(*iKey, iPublicKeyData, iStatus);
    iState = EExportPublic;
    SetActive();
}

void CKeyStoreExample::RunL()
{
    switch (iState)
    {
        ...

        case EExportPublic:
            DecodeRSAPublicKeyData();
            break;

        ...
    }
}

void CKeyStoreExample::DecodeRSAPublicKeyData()
{
    // iRSAPublicKey is a CRSAPublicKey*

    TX509KeyFactory factory; 
    iRSAPublicKey = factory.RSAPublicKeyL(*iPublicKeyData);
}

[Top]


Ownership and usership

Typical applications that use the keystore APIs comprise a User Interface (the key owner) and an Engine (the key user). Applications enforce ownership of keys.

The user is required to authorise certain operations on the keystore by providing the correct passphrase. Passphrase handling uses the authentication object API; MCTAuthenticationObject protects all keys owned by the owner (see the Authentication objects - MCTAuthenticationObject section above).

[Top]


Changing the passphrase

Changing the passphrase is accomplished by telling a keystore to ask the user for the new passphrase. This is done by obtaining the authentication object for a key in the appropriate keystore, and calling its ChangeReferenceData() function. The keystore will then prompt the user for the old and new passphrases, and if the old passphrase is entered correctly, the passphrase is changed. The old and new passphrases are never seen by the calling application.

For example:

void ChangePassphraseL(CCTKeyInfo& aKey)
{
    MCTAuthenticationObject* authObject = aKey.Protector();
    if (authObject == NULL)
        User::Leave(KErrNotSupported);

    authObject->ChangeReferenceData(iStatus);
    iState = EChangePassphrase;
    SetActive();

    // RunL called when this completes
}

[Top]


CUnifiedKeyStore class diagram

The diagram below shows the main class used in the unified keystore. Blue dotted arrows indicate that a class is contained or used by another class. The arrows are labelled with the variable(s) through which the pointed class is accessible. The colour of the boxes indicates the type of Symbian class, i.e., M, C, R or T class. For detailed information on each component see the certman API Reference material.

The diagram above shows the dependencies...


The diagram above shows the dependencies for the CUnifiedKeyStore class (shaded).