Added python script to import Mozilla certificates into Symbian OS (Bug 287). Also added the Mozilla certificate store file.
/*
* Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:
*
*/
#include "wtlscertchainao.h"
#include <asymmetric.h>
#include <bigint.h>
#include <ccertattributefilter.h>
#include <cctcertinfo.h>
CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs,
CWTLSCertChain& aWTLSCertChain,
const CArrayPtr<CWTLSCertificate>& aRootCerts)
{
CWTLSCertChainAO* self = new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain);
CleanupStack::PushL(self);
self->ConstructL(aRootCerts);
CleanupStack::Pop(self);
return self;
}
CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs,
CWTLSCertChain& aWTLSCertChain,
const TUid aClient)
{
return new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain, aClient);
}
CWTLSCertChainAO::~CWTLSCertChainAO()
{
Cancel();
delete iCertStoreManager;
delete iFilter;
delete iEncodedCertTemp;
iRootSubjectClientHashList.ResetAndDestroy();
iRootSubjectStoreHashList.Close();
iCertInfos.Close(); //In an RMPointerArray Close deletes all elements as
//well as any personal allocated space
iRootsFromStore.ResetAndDestroy();
iRootsFromStore.Close();
iRootsFromClient.ResetAndDestroy();
}
CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs,
CWTLSCertChain& aWTLSCertChain)
: CActive(EPriorityNormal), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iEncodedCert(NULL, 0)
{
CActiveScheduler::Add(this);
}
CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs,
CWTLSCertChain& aWTLSCertChain,
const TUid aClient)
: CActive(0), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iClient(aClient),
iEncodedCert(NULL, 0)
{
CActiveScheduler::Add(this);
}
void CWTLSCertChainAO::ConstructL(const CArrayPtr<CWTLSCertificate>& aRootCerts)
{
for(TInt i=0; i< aRootCerts.Count(); i++)
{
CWTLSCertificate* root = CWTLSCertificate::NewLC(*(aRootCerts[i]));
User::LeaveIfError( iRootsFromClient.Append(root) );
CleanupStack::Pop(); //root
}
}
void CWTLSCertChainAO::RunL()
{
//If any of my active objects complete with errors then we don't
//want to proceed
User::LeaveIfError(iStatus.Int());
switch (iState)
{
case EStoreManagerInitialization:
HandleEStoreManagerInitializationL();
break;
case EStoreManagerInitialized:
HandleEStoreManagerInitializedL();
break;
case EGetCertHashes:
HandleEGetCertHashesL();
break;
case EPruneList:
HandleEPruneListL();
break;
case EPruneListDone:
HandleEPruneListDoneL();
break;
case ECheckTCA:
HandleECheckTCAL();
break;
case EIsChainSelfSigned:
HandleEIsChainSelfSignedL();
break;
case ERetrieveRoots:
HandleERetrieveRootsL();
break;
case EAddRootToList:
HandleEAddRootToListL();
break;
case EFindRoot:
HandleEFindRootL();
break;
case EValidateEnd:
HandleEValidateEndL();
break;
default:
__ASSERT_DEBUG(EFalse, User::Panic(_L("CWTLSCertChainAO"), 1));
User::Leave(KErrArgument);
break;
}
}
TInt CWTLSCertChainAO::RunError(TInt aError)
{
User::RequestComplete(iOriginalRequestStatus, aError);
delete iCertStoreManager;
iCertStoreManager = 0;
return 0;
}
void CWTLSCertChainAO::DoCancel()
{
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrCancel);
if (iOriginalRequestStatus)
{
User::RequestComplete(iOriginalRequestStatus, KErrCancel);
}
}
void CWTLSCertChainAO::Validate(CWTLSValidationResult& aValidationResult,
const TTime& aValidationTime,
TRequestStatus& aStatus)
{
iValidationResult = &aValidationResult;
iValidationResult->Reset();
iValidationTime = &aValidationTime;
iOriginalRequestStatus = &aStatus;
aStatus = KRequestPending;
__ASSERT_DEBUG(!IsActive(), User::Panic(_L("CWTLSCertChainAO"), 1));
__ASSERT_DEBUG(!iCertStoreManager, User::Panic(_L("CWTLSCertChainAO"), 1));
iState = EStoreManagerInitialization;
TRequestStatus *status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
TBool CWTLSCertChainAO::CheckSignatureAndNameL(const CWTLSCertificate& aCert,
CWTLSValidationResult& aResult,
TInt aPos) const
{
TInt issuerPos = aPos + 1;
TBool res = EFalse;
if (issuerPos == iWTLSCertChain.iChain->Count())
//then it's the root
{
if (aCert.IssuerName().ExactMatchL(aCert.SubjectName()))
//then it claims to be self signed, sig must verify
{
if (aCert.VerifySignatureL(aCert.PublicKey().KeyData()))
{
res = ETrue;
}
else
{
aResult.SetError(ESignatureInvalid, aPos);
}
}
else
{
aResult.AppendWarningL(TWTLSValidationStatus(ERootCertNotSelfSigned, aPos));
res = ETrue; //if its a warning we continue the validation process with the
//warning duly noted so we can check for further warn/errors
}
}
else
//then it isn't the root: so names must chain & sigs must verify
{
const CWTLSCertificate* issuer = iWTLSCertChain.iChain->At(issuerPos);
TBool subject = EFalse;
TBool signature = EFalse;
subject = aCert.IssuerName().ExactMatchL(issuer->SubjectName());
if( !subject )
{
aResult.SetError(ENamesDontChain, aPos);
return EFalse;
}
signature = aCert.VerifySignatureL(issuer->PublicKey().KeyData());
if( !signature )
{
aResult.SetError(ESignatureInvalid, aPos);
return EFalse;
}
res = subject && signature;
}
return res;
}
TBool CWTLSCertChainAO::CheckValidityPeriod(const CWTLSCertificate& aCert,
CWTLSValidationResult& aResult,
const TTime aTime,
TInt aPos) const
{
if (aCert.ValidityPeriod().Valid(aTime))
{
return ETrue;
}
aResult.SetError(EDateOutOfRange, aPos);
return EFalse;
}
void CWTLSCertChainAO::HandleEStoreManagerInitializationL()
{
iFilter = CCertAttributeFilter::NewL();
iFilter->SetFormat(EWTLSCertificate);
iFilter->SetUid(iClient);
iFilter->SetOwnerType(ECACertificate);
iCertStoreManager = CUnifiedCertStore::NewL(iFs, EFalse);
iCertStoreManager->Initialize(iStatus);
iState = EStoreManagerInitialized;
SetActive();
}
void CWTLSCertChainAO::HandleEStoreManagerInitializedL()
{
iCertStoreManager->List(iCertInfos, *iFilter, iStatus);
iState = EGetCertHashes;
SetActive();
}
void CWTLSCertChainAO::HandleEGetCertHashesL()
{
for(TInt i=0; i<iRootsFromClient.Count(); i++)
{
HBufC8* hash = &GeneratePublicKeyHashL( *(iRootsFromClient[i]));
CleanupStack::PushL(hash);
User::LeaveIfError( iRootSubjectClientHashList.Append(hash) );
CleanupStack::Pop(); //hash
}
for(TInt j=0; j < iCertInfos.Count(); j++ )
{
User::LeaveIfError( iRootSubjectStoreHashList.Append( &((iCertInfos[j])->SubjectKeyId()) ) );
}
iPruned = EFalse;
iPrunedChainLength = iWTLSCertChain.iChain->Count();
iIndex = -1;
iState = EPruneList;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
/* Walk through the canadiate list and compare the hash of the subjects with the previously
* computed subject hash of certs from the CertStore and certs supplied by the client
*/
void CWTLSCertChainAO::HandleEPruneListL()
{
iIndex++;
if(iIndex < iWTLSCertChain.iChain->Count() )
{
CWTLSCertificate* cert = iWTLSCertChain.iChain->At(iIndex);
HBufC8* hash = &GeneratePublicKeyHashL(*cert);
CleanupStack::PushL(hash);
for(TInt i=0; i < iRootSubjectClientHashList.Count(); i++)
{
if( (iRootSubjectClientHashList[i])->Compare(*hash) == 0 )
{
iPrunedChainLength = iIndex;
iPruned = ETrue;
break;
}
}
if(!iPruned)
{
for(TInt j=0; j<iRootSubjectStoreHashList.Count(); j++)
{
if( (iRootSubjectStoreHashList[j])->Compare(*hash) == 0 )
{
iPrunedChainLength = iIndex;
iPruned = ETrue;
break;
}
}
}
CleanupStack::PopAndDestroy(hash);
if(iPruned)
{
iState = EPruneListDone;
}
else
{
iState = EPruneList;
}
}
else
{
iState = EPruneListDone;
}
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
void CWTLSCertChainAO::HandleEPruneListDoneL()
{
if(iPruned)
{
TInt count = iWTLSCertChain.iChain->Count();
for( TInt i=count - 1; i > iPrunedChainLength; i-- )
{
delete iWTLSCertChain.iChain->At(i);
iWTLSCertChain.iChain->Delete(i);
}
iWTLSCertChain.iChain->Compress();
}
iState = ECheckTCA;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
//checks to see if each certificate in a chain has the authority to sign other certificates
void CWTLSCertChainAO::HandleECheckTCAL()
{
TBool validChain = ETrue;
for( TInt i = 1; i < iWTLSCertChain.iChain->Count(); i++ )
//all intermediate certs (ie not EE certs and not self signed) need
// to have a field T=ca indicating that they can sign other certs
{
if( (iWTLSCertChain.iChain)->At(i)->IsTCAL() == EFalse &&
(iWTLSCertChain.iChain)->At(i)->IsSelfSignedL() == EFalse )
{
iValidationResult->SetError(ENotCACert, i);
User::RequestComplete(iOriginalRequestStatus, KErrNone);
validChain = EFalse;
break;
}
}
if(validChain && iPruned)
{
//if we've pruned the list and the chain we have is valid,
//then our chain already has a root that we trust.
//therefore there is no need to retrieve one :)
//therefore goto validation
iState = EValidateEnd;
TRequestStatus *status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
else if(validChain) // ie && !iPruned
{
//if we haven't pruned but chain is valid then we're back a square one.
iState = EIsChainSelfSigned;
TRequestStatus *status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
}
void CWTLSCertChainAO::HandleEIsChainSelfSignedL()
{
TInt last = iWTLSCertChain.iChain->Count() - 1;
if( iWTLSCertChain.iChain->At(last)->IsSelfSignedL() )
{
//if chained is self signed, and no earlier cert in the sequence was trusted
//then this is going to fail validation
//This is just an optimisation to avoid retrieving all the roots from the store
iValidationResult->SetError(EChainHasNoRoot, last);
User::RequestComplete(iOriginalRequestStatus, KErrNone);
}
else
{
//standard chain -> need to find the appropriate trusted root for chain if it exists
iState = ERetrieveRoots;
iIndex = -1;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
}
void CWTLSCertChainAO::HandleERetrieveRootsL()
{
iIndex++;
if(iIndex < iCertInfos.Count() )
{
if( iEncodedCertTemp != NULL )
{
delete iEncodedCertTemp;
}
iEncodedCertTemp = HBufC8::NewMaxL( (iCertInfos[iIndex])->Size() );
iEncodedCert.Set( iEncodedCertTemp->Des() );
iCertStoreManager->Retrieve( *(iCertInfos[iIndex]), iEncodedCert, iStatus );
iState = EAddRootToList;
}
else
{
iState = EFindRoot;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
}
SetActive();
}
void CWTLSCertChainAO::HandleEAddRootToListL()
{
//are we guarenteed that a cert from the store is a valid WTLScert?
//ie is this going to leave for reasons other than OOM?
CWTLSCertificate *cert = CWTLSCertificate::NewL( iEncodedCert );
User::LeaveIfError( iRootsFromStore.Append(cert) );
iState = ERetrieveRoots;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
void CWTLSCertChainAO::HandleEFindRootL()
{
TInt last = iWTLSCertChain.iChain->Count() - 1;
const CWTLSName* issuerName = &(iWTLSCertChain.iChain->At(last)->IssuerName());
iFoundRoot = EFalse;
for(TInt i=0; i<iRootsFromClient.Count(); i++)
{
if( issuerName->ExactMatchL( (iRootsFromClient[i])->SubjectName() ) )
{
iFoundRoot = ETrue;
CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromClient[i]) );
iWTLSCertChain.iChain->AppendL( cert );
CleanupStack::Pop(cert);
break;
}
}
if(!iFoundRoot)
{
for(TInt j=0; j<iRootsFromStore.Count(); j++)
{
if( issuerName->ExactMatchL( (iRootsFromStore[j])->SubjectName() ) )
{
iFoundRoot = ETrue;
CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromStore[j]) );
iWTLSCertChain.iChain->AppendL( cert );
CleanupStack::Pop(cert);
break;
}
}
}
if(!iFoundRoot)
{
iValidationResult->SetError(EChainHasNoRoot, last);
User::RequestComplete(iOriginalRequestStatus, KErrNone);
}
else
{
iState = EValidateEnd;
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
SetActive();
}
}
void CWTLSCertChainAO::HandleEValidateEndL()
{
TInt i = iWTLSCertChain.iChain->Count() -1;//we can guarantee that chain has at least 1 cert
for (; i >= 0; i--)
{
const CWTLSCertificate* current = iWTLSCertChain.iChain->At(i);
if ((!CheckSignatureAndNameL(*current, *iValidationResult, i)) ||
(!CheckValidityPeriod(*current, *iValidationResult, *iValidationTime, i)))
{
//these functions set the error internally if there is one
break;
}
}
User::RequestComplete(iOriginalRequestStatus, KErrNone);
}
HBufC8& CWTLSCertChainAO::GeneratePublicKeyHashL(const CWTLSCertificate& aCert) const
{
TWTLSKeyFactory keyFactory;
CRSAPublicKey* key = keyFactory.RSAPublicKeyL( aCert.PublicKey().KeyData() );
CleanupStack::PushL(key);
HBufC8* modulusBuffer = key->N().BufferLC();
CSHA1* sha1 = CSHA1::NewL();
CleanupStack::PushL(sha1);
TPtrC8 hash = sha1->Final(*modulusBuffer);
HBufC8* permHash = hash.AllocL();
CleanupStack::PopAndDestroy(3); //sha1, modulusBuffer, key
return *permHash;
}