![Symbian Developer Library](../../../../../a_stock/images/mainheading.gif)
![]() |
![]() |
|
The key store (keystore) provides functionality to:
Generate, import and export RSA, DSA, and DH key pairs
List stored keys
Protect stored keys by encrypting them under a passphrase provided by the user
Authenticate users
Perform private key operations for authenticated users.
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.
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 |
---|---|
|
A SHA-1 hash of the public key, used to uniquely identify it in the store |
|
A bitfield representing the allowed usages of the key. This is
of type |
|
The size of the key in bits |
|
A text string to identify the key to users |
|
The UID of the application that created or imported the key |
|
A list of UIDs of applications that are allowed to use the key |
|
The key algorithm: RSA, DSA or DH |
|
A bitfield that determines the sensitivity of the key. See the
|
|
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) |
|
The start of the validity period of the key |
|
The end of the validity period of the key |
|
Additional key attributes as a DER-encoded set |
|
An |
|
An |
|
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.
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.)
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
}
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 |
The key algorithm |
Used when searching for a particular key algorithm, e.g., RSA.
|
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
}
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 |
|
RSA Sign |
|
RSA Decrypt |
|
DH agreement |
|
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.
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 |
---|---|
|
The key usage flags in the PKCS#15 format |
|
The key size in bits |
|
The textual label for the key |
|
The key algorithm - RSA, DSA, or DH |
|
The key's access type bitfield. Only two of the bits defined may
be set when creating a key - |
|
The start of the validity period of the key |
|
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
}
Importing keys is very similar to creating keys, except that the
ImportKey()
function takes an additional two parameters:
Parameter | Description |
---|---|
|
The key data in PKCS#8 format |
|
Whether the key data is PKCS#5 encrypted. |
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
}
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);
}
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.
Owners (i.e., applications) are responsible for
creating, deleting, and managing keys; they can assign keys to users (e.g.,
other applications). Keys can be listed by owner (see the
Finding keys section
above). The owner interface is given by MCTKeyStoreManager
Users actually use the keys for cryptographic
operations; they cannot delete keys and cannot change a key's ownership. The
user interface is given by MCTKeyStore
.
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).
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
}
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.