First public contribution.
2 * Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
19 #include "wtlscertchainao.h"
20 #include <asymmetric.h>
22 #include <ccertattributefilter.h>
23 #include <cctcertinfo.h>
25 CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs,
26 CWTLSCertChain& aWTLSCertChain,
27 const CArrayPtr<CWTLSCertificate>& aRootCerts)
29 CWTLSCertChainAO* self = new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain);
30 CleanupStack::PushL(self);
31 self->ConstructL(aRootCerts);
32 CleanupStack::Pop(self);
36 CWTLSCertChainAO* CWTLSCertChainAO::NewL(RFs& aFs,
37 CWTLSCertChain& aWTLSCertChain,
40 return new(ELeave) CWTLSCertChainAO(aFs, aWTLSCertChain, aClient);
43 CWTLSCertChainAO::~CWTLSCertChainAO()
46 delete iCertStoreManager;
48 delete iEncodedCertTemp;
49 iRootSubjectClientHashList.ResetAndDestroy();
50 iRootSubjectStoreHashList.Close();
51 iCertInfos.Close(); //In an RMPointerArray Close deletes all elements as
52 //well as any personal allocated space
53 iRootsFromStore.ResetAndDestroy();
54 iRootsFromStore.Close();
55 iRootsFromClient.ResetAndDestroy();
58 CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs,
59 CWTLSCertChain& aWTLSCertChain)
60 : CActive(EPriorityNormal), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iEncodedCert(NULL, 0)
62 CActiveScheduler::Add(this);
65 CWTLSCertChainAO::CWTLSCertChainAO(RFs& aFs,
66 CWTLSCertChain& aWTLSCertChain,
68 : CActive(0), iFs(aFs), iWTLSCertChain(aWTLSCertChain), iClient(aClient),
71 CActiveScheduler::Add(this);
74 void CWTLSCertChainAO::ConstructL(const CArrayPtr<CWTLSCertificate>& aRootCerts)
76 for(TInt i=0; i< aRootCerts.Count(); i++)
78 CWTLSCertificate* root = CWTLSCertificate::NewLC(*(aRootCerts[i]));
79 User::LeaveIfError( iRootsFromClient.Append(root) );
80 CleanupStack::Pop(); //root
84 void CWTLSCertChainAO::RunL()
86 //If any of my active objects complete with errors then we don't
88 User::LeaveIfError(iStatus.Int());
92 case EStoreManagerInitialization:
93 HandleEStoreManagerInitializationL();
96 case EStoreManagerInitialized:
97 HandleEStoreManagerInitializedL();
101 HandleEGetCertHashesL();
109 HandleEPruneListDoneL();
116 case EIsChainSelfSigned:
117 HandleEIsChainSelfSignedL();
121 HandleERetrieveRootsL();
125 HandleEAddRootToListL();
133 HandleEValidateEndL();
137 __ASSERT_DEBUG(EFalse, User::Panic(_L("CWTLSCertChainAO"), 1));
138 User::Leave(KErrArgument);
143 TInt CWTLSCertChainAO::RunError(TInt aError)
145 User::RequestComplete(iOriginalRequestStatus, aError);
147 delete iCertStoreManager;
148 iCertStoreManager = 0;
153 void CWTLSCertChainAO::DoCancel()
155 TRequestStatus* status = &iStatus;
156 User::RequestComplete(status, KErrCancel);
157 if (iOriginalRequestStatus)
159 User::RequestComplete(iOriginalRequestStatus, KErrCancel);
163 void CWTLSCertChainAO::Validate(CWTLSValidationResult& aValidationResult,
164 const TTime& aValidationTime,
165 TRequestStatus& aStatus)
167 iValidationResult = &aValidationResult;
168 iValidationResult->Reset();
169 iValidationTime = &aValidationTime;
170 iOriginalRequestStatus = &aStatus;
171 aStatus = KRequestPending;
173 __ASSERT_DEBUG(!IsActive(), User::Panic(_L("CWTLSCertChainAO"), 1));
174 __ASSERT_DEBUG(!iCertStoreManager, User::Panic(_L("CWTLSCertChainAO"), 1));
176 iState = EStoreManagerInitialization;
177 TRequestStatus *status = &iStatus;
178 User::RequestComplete(status, KErrNone);
182 TBool CWTLSCertChainAO::CheckSignatureAndNameL(const CWTLSCertificate& aCert,
183 CWTLSValidationResult& aResult,
186 TInt issuerPos = aPos + 1;
188 if (issuerPos == iWTLSCertChain.iChain->Count())
191 if (aCert.IssuerName().ExactMatchL(aCert.SubjectName()))
192 //then it claims to be self signed, sig must verify
194 if (aCert.VerifySignatureL(aCert.PublicKey().KeyData()))
200 aResult.SetError(ESignatureInvalid, aPos);
205 aResult.AppendWarningL(TWTLSValidationStatus(ERootCertNotSelfSigned, aPos));
206 res = ETrue; //if its a warning we continue the validation process with the
207 //warning duly noted so we can check for further warn/errors
211 //then it isn't the root: so names must chain & sigs must verify
213 const CWTLSCertificate* issuer = iWTLSCertChain.iChain->At(issuerPos);
214 TBool subject = EFalse;
215 TBool signature = EFalse;
216 subject = aCert.IssuerName().ExactMatchL(issuer->SubjectName());
219 aResult.SetError(ENamesDontChain, aPos);
222 signature = aCert.VerifySignatureL(issuer->PublicKey().KeyData());
225 aResult.SetError(ESignatureInvalid, aPos);
228 res = subject && signature;
233 TBool CWTLSCertChainAO::CheckValidityPeriod(const CWTLSCertificate& aCert,
234 CWTLSValidationResult& aResult,
238 if (aCert.ValidityPeriod().Valid(aTime))
242 aResult.SetError(EDateOutOfRange, aPos);
246 void CWTLSCertChainAO::HandleEStoreManagerInitializationL()
248 iFilter = CCertAttributeFilter::NewL();
249 iFilter->SetFormat(EWTLSCertificate);
250 iFilter->SetUid(iClient);
251 iFilter->SetOwnerType(ECACertificate);
253 iCertStoreManager = CUnifiedCertStore::NewL(iFs, EFalse);
254 iCertStoreManager->Initialize(iStatus);
256 iState = EStoreManagerInitialized;
260 void CWTLSCertChainAO::HandleEStoreManagerInitializedL()
262 iCertStoreManager->List(iCertInfos, *iFilter, iStatus);
264 iState = EGetCertHashes;
268 void CWTLSCertChainAO::HandleEGetCertHashesL()
270 for(TInt i=0; i<iRootsFromClient.Count(); i++)
272 HBufC8* hash = &GeneratePublicKeyHashL( *(iRootsFromClient[i]));
273 CleanupStack::PushL(hash);
274 User::LeaveIfError( iRootSubjectClientHashList.Append(hash) );
275 CleanupStack::Pop(); //hash
277 for(TInt j=0; j < iCertInfos.Count(); j++ )
279 User::LeaveIfError( iRootSubjectStoreHashList.Append( &((iCertInfos[j])->SubjectKeyId()) ) );
283 iPrunedChainLength = iWTLSCertChain.iChain->Count();
287 TRequestStatus* status = &iStatus;
288 User::RequestComplete(status, KErrNone);
292 /* Walk through the canadiate list and compare the hash of the subjects with the previously
293 * computed subject hash of certs from the CertStore and certs supplied by the client
295 void CWTLSCertChainAO::HandleEPruneListL()
298 if(iIndex < iWTLSCertChain.iChain->Count() )
300 CWTLSCertificate* cert = iWTLSCertChain.iChain->At(iIndex);
301 HBufC8* hash = &GeneratePublicKeyHashL(*cert);
302 CleanupStack::PushL(hash);
304 for(TInt i=0; i < iRootSubjectClientHashList.Count(); i++)
306 if( (iRootSubjectClientHashList[i])->Compare(*hash) == 0 )
308 iPrunedChainLength = iIndex;
315 for(TInt j=0; j<iRootSubjectStoreHashList.Count(); j++)
317 if( (iRootSubjectStoreHashList[j])->Compare(*hash) == 0 )
319 iPrunedChainLength = iIndex;
325 CleanupStack::PopAndDestroy(hash);
328 iState = EPruneListDone;
337 iState = EPruneListDone;
339 TRequestStatus* status = &iStatus;
340 User::RequestComplete(status, KErrNone);
344 void CWTLSCertChainAO::HandleEPruneListDoneL()
348 TInt count = iWTLSCertChain.iChain->Count();
349 for( TInt i=count - 1; i > iPrunedChainLength; i-- )
351 delete iWTLSCertChain.iChain->At(i);
352 iWTLSCertChain.iChain->Delete(i);
354 iWTLSCertChain.iChain->Compress();
357 TRequestStatus* status = &iStatus;
358 User::RequestComplete(status, KErrNone);
362 //checks to see if each certificate in a chain has the authority to sign other certificates
363 void CWTLSCertChainAO::HandleECheckTCAL()
365 TBool validChain = ETrue;
366 for( TInt i = 1; i < iWTLSCertChain.iChain->Count(); i++ )
367 //all intermediate certs (ie not EE certs and not self signed) need
368 // to have a field T=ca indicating that they can sign other certs
370 if( (iWTLSCertChain.iChain)->At(i)->IsTCAL() == EFalse &&
371 (iWTLSCertChain.iChain)->At(i)->IsSelfSignedL() == EFalse )
373 iValidationResult->SetError(ENotCACert, i);
374 User::RequestComplete(iOriginalRequestStatus, KErrNone);
379 if(validChain && iPruned)
381 //if we've pruned the list and the chain we have is valid,
382 //then our chain already has a root that we trust.
383 //therefore there is no need to retrieve one :)
384 //therefore goto validation
385 iState = EValidateEnd;
386 TRequestStatus *status = &iStatus;
387 User::RequestComplete(status, KErrNone);
390 else if(validChain) // ie && !iPruned
392 //if we haven't pruned but chain is valid then we're back a square one.
393 iState = EIsChainSelfSigned;
394 TRequestStatus *status = &iStatus;
395 User::RequestComplete(status, KErrNone);
400 void CWTLSCertChainAO::HandleEIsChainSelfSignedL()
403 TInt last = iWTLSCertChain.iChain->Count() - 1;
404 if( iWTLSCertChain.iChain->At(last)->IsSelfSignedL() )
406 //if chained is self signed, and no earlier cert in the sequence was trusted
407 //then this is going to fail validation
408 //This is just an optimisation to avoid retrieving all the roots from the store
410 iValidationResult->SetError(EChainHasNoRoot, last);
411 User::RequestComplete(iOriginalRequestStatus, KErrNone);
415 //standard chain -> need to find the appropriate trusted root for chain if it exists
416 iState = ERetrieveRoots;
418 TRequestStatus* status = &iStatus;
419 User::RequestComplete(status, KErrNone);
424 void CWTLSCertChainAO::HandleERetrieveRootsL()
427 if(iIndex < iCertInfos.Count() )
429 if( iEncodedCertTemp != NULL )
431 delete iEncodedCertTemp;
433 iEncodedCertTemp = HBufC8::NewMaxL( (iCertInfos[iIndex])->Size() );
434 iEncodedCert.Set( iEncodedCertTemp->Des() );
435 iCertStoreManager->Retrieve( *(iCertInfos[iIndex]), iEncodedCert, iStatus );
436 iState = EAddRootToList;
441 TRequestStatus* status = &iStatus;
442 User::RequestComplete(status, KErrNone);
447 void CWTLSCertChainAO::HandleEAddRootToListL()
449 //are we guarenteed that a cert from the store is a valid WTLScert?
450 //ie is this going to leave for reasons other than OOM?
451 CWTLSCertificate *cert = CWTLSCertificate::NewL( iEncodedCert );
452 User::LeaveIfError( iRootsFromStore.Append(cert) );
454 iState = ERetrieveRoots;
455 TRequestStatus* status = &iStatus;
456 User::RequestComplete(status, KErrNone);
460 void CWTLSCertChainAO::HandleEFindRootL()
462 TInt last = iWTLSCertChain.iChain->Count() - 1;
463 const CWTLSName* issuerName = &(iWTLSCertChain.iChain->At(last)->IssuerName());
466 for(TInt i=0; i<iRootsFromClient.Count(); i++)
468 if( issuerName->ExactMatchL( (iRootsFromClient[i])->SubjectName() ) )
471 CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromClient[i]) );
472 iWTLSCertChain.iChain->AppendL( cert );
473 CleanupStack::Pop(cert);
479 for(TInt j=0; j<iRootsFromStore.Count(); j++)
481 if( issuerName->ExactMatchL( (iRootsFromStore[j])->SubjectName() ) )
484 CWTLSCertificate* cert = CWTLSCertificate::NewLC( *(iRootsFromStore[j]) );
485 iWTLSCertChain.iChain->AppendL( cert );
486 CleanupStack::Pop(cert);
493 iValidationResult->SetError(EChainHasNoRoot, last);
494 User::RequestComplete(iOriginalRequestStatus, KErrNone);
498 iState = EValidateEnd;
499 TRequestStatus* status = &iStatus;
500 User::RequestComplete(status, KErrNone);
505 void CWTLSCertChainAO::HandleEValidateEndL()
507 TInt i = iWTLSCertChain.iChain->Count() -1;//we can guarantee that chain has at least 1 cert
510 const CWTLSCertificate* current = iWTLSCertChain.iChain->At(i);
511 if ((!CheckSignatureAndNameL(*current, *iValidationResult, i)) ||
512 (!CheckValidityPeriod(*current, *iValidationResult, *iValidationTime, i)))
514 //these functions set the error internally if there is one
519 User::RequestComplete(iOriginalRequestStatus, KErrNone);
522 HBufC8& CWTLSCertChainAO::GeneratePublicKeyHashL(const CWTLSCertificate& aCert) const
524 TWTLSKeyFactory keyFactory;
525 CRSAPublicKey* key = keyFactory.RSAPublicKeyL( aCert.PublicKey().KeyData() );
526 CleanupStack::PushL(key);
527 HBufC8* modulusBuffer = key->N().BufferLC();
528 CSHA1* sha1 = CSHA1::NewL();
529 CleanupStack::PushL(sha1);
530 TPtrC8 hash = sha1->Final(*modulusBuffer);
531 HBufC8* permHash = hash.AllocL();
532 CleanupStack::PopAndDestroy(3); //sha1, modulusBuffer, key