pkiutilities/DeviceToken/Src/KeyStore/Server/DevCertKeyDataManager.cpp
changeset 0 164170e6151a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkiutilities/DeviceToken/Src/KeyStore/Server/DevCertKeyDataManager.cpp	Tue Jan 26 15:20:08 2010 +0200
@@ -0,0 +1,693 @@
+/*
+* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). 
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:   Implementation of DevCertKeyDataManager
+*
+*/
+
+
+
+#include "DevCertKeyDataManager.h"
+#include "DevTokenDataTypes.h"
+#include "DevTokenCliServ.h"
+#include "DevTokenUtil.h"
+
+_LIT(KDevCertKeyStoreFilename,"DevCertKeys.dat");
+
+
+// ======== MEMBER FUNCTIONS ========
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::NewL()
+// Key store data manager - maintains array of objects representing keys
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyDataManager* CDevCertKeyDataManager::NewL()
+    {
+    CDevCertKeyDataManager* self = new (ELeave) CDevCertKeyDataManager();
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::~CDevCertKeyDataManager()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyDataManager::~CDevCertKeyDataManager()
+    {
+    if (iFileStore)
+        {
+        CompactStore();
+        delete iFileStore;
+        }
+
+    iFile.Close(); // May already have been closed by store
+    iFs.Close();
+
+    iKeys.ResetAndDestroy();
+    iKeys.Close();
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::CDevCertKeyDataManager()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyDataManager::CDevCertKeyDataManager() :
+  iRootStreamId(KNullStreamId),
+  iInfoStreamId(KNullStreamId)
+    {
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::ConstructL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::ConstructL()
+    {
+    User::LeaveIfError(iFs.Connect());
+    OpenStoreL();
+
+    RStoreReadStream lookupStream;
+    lookupStream.OpenLC(*iFileStore, iInfoStreamId);
+
+    TInt count = lookupStream.ReadInt32L();
+    for (TInt index = 0; index < count; index++)
+        {
+        const CDevCertKeyData* keyData = CDevCertKeyData::NewL(lookupStream);
+
+        if (keyData->Handle() > iKeyIdentifier)
+        iKeyIdentifier = keyData->Handle();
+
+        iKeys.Append(keyData);
+        }
+
+    CleanupStack::PopAndDestroy(&lookupStream);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::OpenStoreL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenStoreL()
+    {
+    //  Tries to locate a key store file on the default drive and then from ROM
+    //  If it cannot find one, tries to create a file with permanent file store
+    //  inside it In all cases, should initialise iFileStore unless it cannot
+    //  create the file/store/streams
+
+    __ASSERT_DEBUG(!iFileStore, PanicServer(EPanicStoreInitialised));
+
+    TFileName fullPath;
+    FileUtils::MakePrivateFilenameL(iFs, KDevCertKeyStoreFilename, fullPath);
+
+    FileUtils::EnsurePathL(iFs, fullPath);
+    TRAPD(result, OpenStoreInFileL(fullPath));
+
+    if (result == KErrInUse) 
+        {   
+        // Cannot access the file now. Abort server startup rather than wiping the keystore.
+        User::Leave(result); 
+        }
+
+    if (result != KErrNone)
+        {   
+        // Not yet opened a valid store, either no file to be found, or no valid
+        // store in it. Copy the original one stored in the ROM.
+        delete iFileStore;
+        iFileStore = NULL;
+
+        TFileName romPath;
+        FileUtils::MakePrivateROMFilenameL(iFs, KDevCertKeyStoreFilename, romPath);
+
+        if (result != KErrNotFound)
+            {
+            // Wipe the keystore if we can't open it (it's corrupt anyway)
+            User::LeaveIfError(iFs.Delete(fullPath));
+            }
+
+        // Copy data from rom and open it 
+        TRAPD(err,
+        FileUtils::CopyL(iFs, romPath, fullPath);
+        OpenStoreInFileL(fullPath)
+        );
+
+        if (KErrNone != err)
+            {
+            // We tried to copy the keystore from ROM. For some reason this
+            // failed and we still cannot open the file. Create a new one from
+            // scratch.
+            CreateStoreInFileL(fullPath);
+            }
+        }
+
+    __ASSERT_DEBUG(iFileStore, PanicServer(EPanicStoreInitialised));
+    __ASSERT_DEBUG((KNullStreamId!=iRootStreamId), PanicServer(EPanicRootStreamNotReady));
+    __ASSERT_DEBUG((KNullStreamId!=iInfoStreamId), PanicServer(EPanicManagerStreamNotReady));
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::CreateStoreInFileL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::CreateStoreInFileL(const TDesC& aFile)
+    {
+    TInt r = iFs.MkDirAll(aFile);
+    if ( (r!=KErrNone) && (r!=KErrAlreadyExists) )
+    User::Leave(r);
+
+    iFileStore = CPermanentFileStore::ReplaceL(iFs, aFile, EFileRead | EFileWrite | EFileShareExclusive);
+    iFileStore->SetTypeL(KPermanentFileStoreLayoutUid);
+
+    TCleanupItem cleanupStore(RevertStore, iFileStore);
+    CleanupStack::PushL(cleanupStore);
+
+    // Create info stream - Currently no passphrase created, and no keys
+    RStoreWriteStream managerStream;
+    iInfoStreamId = managerStream.CreateLC(*iFileStore);
+    managerStream.WriteUint32L(0); // Write key count of zero
+    managerStream.CommitL();
+    CleanupStack::PopAndDestroy(&managerStream);
+
+    // Create root stream - just contains id of info stream
+    RStoreWriteStream rootStream;
+    iRootStreamId = rootStream.CreateLC(*iFileStore);
+    iFileStore->SetRootL(iRootStreamId);
+    rootStream.WriteUint32L(iInfoStreamId.Value());   
+    rootStream.CommitL();
+    CleanupStack::PopAndDestroy(&rootStream);
+
+    WriteKeysToStoreL();
+
+    iFileStore->CommitL();
+    CleanupStack::Pop(); // cleanupStore
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::OpenStoreInFileL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenStoreInFileL(const TDesC& aFile)
+    {
+    // Make sure the file isn't write protected
+    User::LeaveIfError(iFs.SetAtt(aFile, 0, KEntryAttReadOnly));
+
+    User::LeaveIfError(iFile.Open(iFs, aFile, EFileRead | EFileWrite | EFileShareExclusive));
+
+    iFileStore = CPermanentFileStore::FromL(iFile);   
+
+    // Get the salt, root and manager TStreamIds
+    iRootStreamId = iFileStore->Root();
+    if (iRootStreamId == KNullStreamId)
+        {
+        User::Leave(KErrCorrupt);
+        }
+
+    RStoreReadStream rootStream;
+    rootStream.OpenLC(*iFileStore, iRootStreamId);
+    iInfoStreamId = (TStreamId)(rootStream.ReadUint32L());
+    CleanupStack::PopAndDestroy(&rootStream);
+    }
+
+
+// Methods dealing with atomic updates to key data file 
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::RevertStore()
+// This is a cleanup item that reverts the store
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::RevertStore(TAny* aStore)
+    {
+    CPermanentFileStore* store = reinterpret_cast<CPermanentFileStore*>(aStore);
+    TRAP_IGNORE(store->RevertL());
+    // We're ignoring the leave code from this becuase there's no way we can
+    // handle this sensibly.  This shouldn't be a problem in practice - this
+    // will leave if for example the file store is on removable which is
+    // unexpectedly remove, and this is never the case for us.
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::RevertStore()
+// Rewrites the info stream (ie the array of key data info) to the store
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::WriteKeysToStoreL()
+    {
+    RStoreWriteStream lookupStream;
+    lookupStream.ReplaceLC(*iFileStore, iInfoStreamId);
+
+    TInt keyCount = iKeys.Count();
+    lookupStream.WriteInt32L(keyCount);
+
+    for (TInt index = 0; index < keyCount; index++)
+        {
+        const CDevCertKeyData* key = iKeys[index];
+        key->ExternalizeL(lookupStream);
+        }
+
+    lookupStream.CommitL();
+    CleanupStack::PopAndDestroy(&lookupStream);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::AddL()
+// Add a key to the store.  Assumes that the key data streams (info, public key
+// and private key) have already been written.
+// ---------------------------------------------------------------------------
+// 
+void CDevCertKeyDataManager::AddL(const CDevCertKeyData* aKeyData)
+    {
+    ASSERT(aKeyData);
+
+    // Add the key to to the array, rewrite the infostream and 
+    // ONLY THEN commit the store
+    User::LeaveIfError(iKeys.Append(aKeyData));
+
+    TRAPD(err, WriteKeysToStoreL());
+
+    // Release ownership of key data and reset default passphrase id if store
+    // can't be written
+    TCleanupItem cleanupStore(RevertStore, iFileStore);
+    CleanupStack::PushL(cleanupStore);
+
+    if (err != KErrNone)
+        {
+        iKeys.Remove(iKeys.Count() - 1);
+        User::Leave(err);
+        }
+
+    iFileStore->CommitL();
+
+    CleanupStack::Pop(); // cleanupStore
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::RemoveL()
+// "Transaction safe" key removal - only removes the key in memory and file if
+//  all operations are successful.
+// ---------------------------------------------------------------------------
+// 
+void CDevCertKeyDataManager::RemoveL(TInt aObjectId)
+    {
+    TInt index;
+    const CDevCertKeyData* key = NULL;
+    for (index = 0 ; index < iKeys.Count() ; ++index)
+        {
+        if (iKeys[index]->Handle() == aObjectId)
+            {
+            key = iKeys[index];
+            break;
+            }
+        }
+
+    if (!key)
+        {
+        User::Leave(KErrNotFound);
+        }
+
+    TCleanupItem cleanupStore(RevertStore, iFileStore);
+    CleanupStack::PushL(cleanupStore);  
+
+    iFileStore->DeleteL(key->PrivateDataStreamId());
+    iFileStore->DeleteL(key->PublicDataStreamId());
+    iFileStore->DeleteL(key->InfoDataStreamId());
+
+    // Remove the key
+    iKeys.Remove(index);
+
+    TRAPD(res, WriteKeysToStoreL());
+    if (res != KErrNone)
+        {
+        User::LeaveIfError(iKeys.Append(key)); // Put it back, shouldn't leave
+        User::Leave(res);
+        }
+    else 
+        {
+        delete key;   // Cannot leave from the point it's removed to here, so no cleanup stack!
+        }   
+    iFileStore->CommitL();
+
+    CleanupStack::Pop(); // cleanupStore
+    CompactStore();
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::IsKeyAlreadyInStore().
+// ---------------------------------------------------------------------------
+// 
+TBool CDevCertKeyDataManager::IsKeyAlreadyInStore(const TDesC& aKeyLabel) const
+    {// Check each key in the store to determine if aKeyLabel already exists
+    TInt keyCount = iKeys.Count();
+    TBool isInStore = EFalse;
+    for (TInt index = 0; index < keyCount; index++)
+        {
+        const TDesC& keyLabel = iKeys[index]->Label();
+        if (keyLabel.Compare(aKeyLabel)==0)
+            {
+            isInStore = ETrue;
+            break;
+            }
+        }
+
+    return (isInStore);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager::Count().
+// ---------------------------------------------------------------------------
+//
+TInt CDevCertKeyDataManager::Count() const
+    {
+    return iKeys.Count();
+    } 
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::operator[](TInt aIndex) const
+// ---------------------------------------------------------------------------
+//
+const CDevCertKeyData* CDevCertKeyDataManager::operator[](TInt aIndex) const
+    {
+    return iKeys[aIndex];
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::Lookup()
+// ---------------------------------------------------------------------------
+//
+const CDevCertKeyData* CDevCertKeyDataManager::Lookup(TInt aObjectId) const
+    {
+    TInt count = Count();
+    for (TInt i = 0; i < count; ++i)
+        {
+        if ((*this)[i]->Handle() == aObjectId)
+            {
+            return (*this)[i];
+            }
+        }
+    return NULL;
+    }
+
+
+//  Management of file and store therein
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::CreateKeyDataLC()
+// ---------------------------------------------------------------------------
+//
+const CDevCertKeyData* CDevCertKeyDataManager::CreateKeyDataLC( const TDesC& aLabel )
+    {
+    TInt objectId = ++iKeyIdentifier;
+    TStreamId infoData = CreateWriteStreamL();
+    TStreamId publicKeyData = CreateWriteStreamL();
+    TStreamId privateKeyData = CreateWriteStreamL();
+    return CDevCertKeyData::NewLC(objectId, aLabel, infoData, publicKeyData, privateKeyData);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::CreateWriteStreamL()
+//  Creates a new write stream in the store (which it then closes)
+//  Returns the TStreamId associated with it
+// ---------------------------------------------------------------------------
+//
+TStreamId CDevCertKeyDataManager::CreateWriteStreamL()
+    {
+    __ASSERT_DEBUG(iFileStore, PanicServer(EPanicStoreInitialised));
+    if (!iFileStore)
+    User::Leave(KErrNotReady);
+
+    RStoreWriteStream newStream;
+    TStreamId result = newStream.CreateLC(*iFileStore);
+    if (KNullStreamId == result)
+    User::Leave(KErrBadHandle);
+
+    newStream.CommitL();
+    CleanupStack::PopAndDestroy(&newStream);
+
+    return result;
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::ReadKeyInfoLC()
+// ---------------------------------------------------------------------------
+//
+CDevTokenKeyInfo* CDevCertKeyDataManager::ReadKeyInfoLC(const CDevCertKeyData& aKeyData) const
+    {
+    __ASSERT_ALWAYS(iFileStore, PanicServer(EPanicStoreInitialised));
+    RStoreReadStream stream;
+    stream.OpenLC(*iFileStore, aKeyData.InfoDataStreamId());
+    CDevTokenKeyInfo* info = CDevTokenKeyInfo::NewL(stream);
+    CleanupStack::PopAndDestroy(&stream);
+    info->CleanupPushL();
+    if (info->Handle() != aKeyData.Handle())
+        {
+        User::Leave(KErrCorrupt); // is this appropriate?
+        }
+    return info;
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::WriteKeyInfoL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::WriteKeyInfoL(const CDevCertKeyData& aKeyData, const CDevTokenKeyInfo& aKeyInfo)
+    {
+    RStoreWriteStream infoStream;
+    OpenInfoDataStreamLC(aKeyData, infoStream);
+    infoStream << aKeyInfo;
+    infoStream.CommitL();
+    CleanupStack::PopAndDestroy(&infoStream);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::SafeWriteKeyInfoL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::SafeWriteKeyInfoL(const CDevCertKeyData& aKeyData, const CDevTokenKeyInfo& aKeyInfo)
+    {
+    TCleanupItem cleanupStore(RevertStore, iFileStore);
+    CleanupStack::PushL(cleanupStore);  
+
+    WriteKeyInfoL(aKeyData, aKeyInfo);
+    iFileStore->CommitL();
+
+    CleanupStack::Pop(); // cleanupStore  
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::OpenInfoDataStreamLC()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenInfoDataStreamLC(const CDevCertKeyData& aKeyData, RStoreWriteStream& aStream)
+    {
+    __ASSERT_ALWAYS(iFileStore, PanicServer(EPanicStoreInitialised));
+    aStream.ReplaceLC(*iFileStore, aKeyData.InfoDataStreamId());
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::OpenPublicDataStreamLC()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenPublicDataStreamLC(const CDevCertKeyData& aKeyData, RStoreWriteStream& aStream)
+    {
+    __ASSERT_ALWAYS(iFileStore, PanicServer(EPanicStoreInitialised));
+    aStream.ReplaceLC(*iFileStore, aKeyData.PublicDataStreamId());
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::OpenPublicDataStreamLC()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenPublicDataStreamLC(const CDevCertKeyData& aKeyData, RStoreReadStream& aStream) const
+    {
+    __ASSERT_ALWAYS(iFileStore, PanicServer(EPanicStoreInitialised));
+    aStream.OpenLC(*iFileStore, aKeyData.PublicDataStreamId());
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::OpenPrivateDataStreamLC()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenPrivateDataStreamLC(const CDevCertKeyData& aKeyData, 
+                          RStoreWriteStream& aStream)
+    {
+    __ASSERT_DEBUG(iFileStore, PanicServer(EPanicStoreInitialised));
+    aStream.ReplaceLC(*iFileStore, aKeyData.PrivateDataStreamId());
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::OpenPrivateDataStreamLC()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::OpenPrivateDataStreamLC(const CDevCertKeyData& aKeyData, 
+                          RStoreReadStream& aStream) 
+    {
+    __ASSERT_DEBUG(iFileStore, PanicServer(EPanicStoreInitialised));
+    aStream.OpenLC(*iFileStore, aKeyData.PrivateDataStreamId());
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyDataManager:::CompactStore()
+// Attempt to compact the store - it doesn't matter if these calls leave, it
+// will only mean that the store takes up more space than necessary.
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyDataManager::CompactStore()
+    {
+    ASSERT(iFileStore);
+    TRAP_IGNORE(iFileStore->ReclaimL(); iFileStore->CompactL());
+    }
+
+
+// CDevCertKeyData
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::NewLC()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyData* CDevCertKeyData::NewLC(TInt aObjectId, const TDesC& aLabel, TStreamId aInfoData,
+                  TStreamId aPublicData, TStreamId aPrivateData)
+    {
+    CDevCertKeyData* self = new (ELeave) CDevCertKeyData(aObjectId, aInfoData, aPublicData, aPrivateData);
+    CleanupStack::PushL(self);
+    self->ConstructL(aLabel);
+    return self;
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::NewL()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyData* CDevCertKeyData::NewL(RStoreReadStream& aReadStream)
+    {
+    CDevCertKeyData* self = new (ELeave) CDevCertKeyData();
+    CleanupStack::PushL(self);
+    self->InternalizeL(aReadStream);
+    CleanupStack::Pop(self);
+    return (self);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::~CDevCertKeyData()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyData::~CDevCertKeyData()
+    {
+    delete iLabel;
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::CDevCertKeyData()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyData::CDevCertKeyData(TInt aObjectId, TStreamId aInfoData,
+               TStreamId aPublicData, TStreamId aPrivateData) :
+  iObjectId(aObjectId), iInfoData(aInfoData), 
+  iPublicKeyData(aPublicData), iPrivateKeyData(aPrivateData)
+    {
+    ASSERT(iObjectId);
+    ASSERT(iInfoData != KNullStreamId);
+    ASSERT(iPublicKeyData != KNullStreamId);
+    ASSERT(iPrivateKeyData != KNullStreamId);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::CDevCertKeyData()
+// ---------------------------------------------------------------------------
+//
+CDevCertKeyData::CDevCertKeyData()
+    {
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::ConstructL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyData::ConstructL(const TDesC& aLabel)
+    {
+    TInt labelLen = aLabel.Length();
+    iLabel = HBufC::NewMaxL(labelLen);
+    TPtr theLabel(iLabel->Des());
+    theLabel.FillZ();
+    theLabel.Copy(aLabel);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::InternalizeL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyData::InternalizeL(RReadStream& aReadStream)
+    {
+    iObjectId = aReadStream.ReadInt32L();
+    iInfoData.InternalizeL(aReadStream);
+    iPublicKeyData.InternalizeL(aReadStream);
+    iPrivateKeyData.InternalizeL(aReadStream);
+
+    TInt labelLen = aReadStream.ReadInt32L();
+    iLabel = HBufC::NewMaxL(labelLen);
+    TPtr theLabel((TUint16*)iLabel->Ptr(), labelLen, labelLen);
+    theLabel.FillZ(labelLen);
+    aReadStream.ReadL(theLabel);
+    }
+
+
+// ---------------------------------------------------------------------------
+// CDevCertKeyData::ExternalizeL()
+// ---------------------------------------------------------------------------
+//
+void CDevCertKeyData::ExternalizeL(RWriteStream& aWriteStream) const
+    {
+    aWriteStream.WriteInt32L(iObjectId);
+    iInfoData.ExternalizeL(aWriteStream);
+    iPublicKeyData.ExternalizeL(aWriteStream);
+    iPrivateKeyData.ExternalizeL(aWriteStream);
+
+    TInt labelLen = iLabel->Length();
+    aWriteStream.WriteInt32L(labelLen);
+    TPtr theLabel(iLabel->Des());
+    theLabel.SetLength(labelLen);
+    aWriteStream.WriteL(theLabel);
+    }
+
+//EOF
+