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