os/security/cryptoservices/certificateandkeymgmt/pkixcertbase/pkixcertchainao.cpp
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 "pkixcertchainao.h"
22 #include <pkixcertchain.h>
23 #include "x509constraintext.h"
25 CPKIXCertChainAO* CPKIXCertChainAO::NewL(MCertStore& aCertStore,
26 CPKIXCertChainBase &aPKIXCertChain,
27 const RPointerArray<CX509Certificate>& aRootCerts)
29 CPKIXCertChainAO* self = new(ELeave) CPKIXCertChainAO(aCertStore, aPKIXCertChain);
30 CleanupStack::PushL(self);
31 self->ConstructL(aRootCerts);
32 CleanupStack::Pop(self);
36 CPKIXCertChainAO* CPKIXCertChainAO::NewL(MCertStore& aCertStore,
37 CPKIXCertChainBase &aPKIXCertChain,
40 return new(ELeave) CPKIXCertChainAO(aCertStore, aPKIXCertChain, aClient);
43 CPKIXCertChainAO::~CPKIXCertChainAO()
49 delete iCertsFromStoreRoots;
52 CPKIXCertChainAO::CPKIXCertChainAO(MCertStore& aCertStore,
53 CPKIXCertChainBase &aPKIXCertChain)
54 : CActive(EPriorityNormal), iCertStore(&aCertStore), iPKIXCertChain(aPKIXCertChain)
56 CActiveScheduler::Add(this);
59 CPKIXCertChainAO::CPKIXCertChainAO(MCertStore& aCertStore,
60 CPKIXCertChainBase &aPKIXCertChain,
62 : CActive(EPriorityNormal), iCertStore(&aCertStore),
63 iPKIXCertChain(aPKIXCertChain), iClient(aClient)
65 CActiveScheduler::Add(this);
68 void CPKIXCertChainAO::ConstructL(const RPointerArray<CX509Certificate>& aRootCerts)
70 CPKIXCertsFromClient* roots = CPKIXCertsFromClient::NewLC(aRootCerts);
71 iRoots = CPKIXChainBuilder::NewL();
72 iRoots->AddSourceL(roots);
73 CleanupStack::Pop(roots);
76 void CPKIXCertChainAO::RunL()
78 User::LeaveIfError(iStatus.Int());
86 case ERootsInitialized:
87 HandleERootsInitializedL();
90 case EBuildChainStart:
91 HandleEBuildChainStartL();
94 case EBuildChainAddCandidateEnd:
95 HandleEBuildChainAddCandidateEndL();
98 case EBuildChainCertsFromStoreBegin:
99 HandleEBuildChainCertsFromStoreBeginL();
102 case EBuildChainCertsFromStoreEnd:
103 HandleEBuildChainCertsFromStoreEndL();
106 case EAddCandidateIntermediateCertsEnd:
107 HandleEAddCandidateIntermediateCertsEndL();
111 HandleEValidateEndL();
115 User::Panic(_L("CPKIXCertChainAO"), 1);
120 TInt CPKIXCertChainAO::RunError(TInt aError)
122 iPKIXCertChain.RemoveLastCerts(iNumberOfAddedCertificates);
123 iNumberOfAddedCertificates = 0;
130 delete iCertsFromStoreRoots;
131 iCertsFromStoreRoots = 0;
133 iValidationResult->RemovePolicies();
135 User::RequestComplete(iOriginalRequestStatus, aError);
140 * Creates a list of all the certificates retrieved from the store based on the filter passed.
143 void CPKIXCertChainAO::HandleEAddRootsL()
145 __ASSERT_DEBUG(!iCertsFromStoreRoots, User::Panic(_L("CPKICCertChainAO"), 1));
146 iCertsFromStoreRoots = CPKIXCertsFromStore::NewL(*iCertStore, iClient);
147 iCertsFromStoreRoots->Initialize(iStatus);
148 iState = ERootsInitialized;
153 * Adds the list of certificates retrieved from the store, iRoots (CPKIXChainBuilder)
154 * maintains a templatized list of all the certificates in MPKIXCertSource format.
157 void CPKIXCertChainAO::HandleERootsInitializedL()
159 iRoots->AddSourceL(iCertsFromStoreRoots);
160 // Ownership has been passed to iRoots
161 iCertsFromStoreRoots = 0;
162 iState = EBuildChainStart;
163 TRequestStatus* status = &iStatus;
164 User::RequestComplete(status, KErrNone);
168 void CPKIXCertChainAO::HandleEBuildChainStartL()
170 if ( false == iPKIXCertChain.ChainHasRoot())
172 if (iPKIXCertChain.Chain().Count() == 0)
174 iState = EValidateEnd;
175 TRequestStatus* status = &iStatus;
176 User::RequestComplete(status, KErrNone);
180 //1) look for an issuer that's a root
181 iRoots->AddIssuer(iNumberOfAddedCertificates, iAddIssuerResult, iPKIXCertChain.Chain(), iStatus);
182 iState = EBuildChainAddCandidateEnd;
187 // This is the correct state as at this point the chain of certificate has been build upto a
189 iState = EValidateEnd;
190 TRequestStatus* status = &iStatus;
191 User::RequestComplete(status, KErrNone);
196 void CPKIXCertChainAO::HandleEBuildChainAddCandidateEndL()
198 if (iAddIssuerResult)
200 iPKIXCertChain.SetChainHasRoot(ETrue);
201 iState = EValidateEnd;
205 //2) look for a non-root issuer in intermediate certs
206 iBuilder = CPKIXChainBuilder::NewL();
208 CPKIXCertsFromClient* serverCerts = CPKIXCertsFromClient::NewLC(iPKIXCertChain.IntermediateCerts());
209 iBuilder->AddSourceL(serverCerts);
210 CleanupStack::Pop(serverCerts);
212 iState = EBuildChainCertsFromStoreBegin;
215 TRequestStatus* status = &iStatus;
216 User::RequestComplete(status, KErrNone);
220 void CPKIXCertChainAO::HandleEBuildChainCertsFromStoreBeginL()
222 //3) look for a non-root issuer in the store
223 iCertsFromStoreRoots = CPKIXCertsFromStore::NewL(*iCertStore);
224 iCertsFromStoreRoots->Initialize(iStatus);
225 iState = EBuildChainCertsFromStoreEnd;
229 void CPKIXCertChainAO::HandleEBuildChainCertsFromStoreEndL()
231 iBuilder->AddSourceL(iCertsFromStoreRoots);
232 iCertsFromStoreRoots = 0;
234 iBuilder->AddIssuer(iNumberOfAddedCertificates, iAddIssuerResult, iPKIXCertChain.Chain(), iStatus);
235 iState = EAddCandidateIntermediateCertsEnd;
239 void CPKIXCertChainAO::HandleEAddCandidateIntermediateCertsEndL()
241 if (iAddIssuerResult)
243 // cert is a pointer to something we don't own
244 CX509Certificate* cert = iPKIXCertChain.Chain().At(iPKIXCertChain.Chain().Count() - 1);
246 /* If the issuer is not a self signed certificate then it cannot be trusted anchor for the chain
247 * validation process, this means that we restart the certification validation process.
250 if (!(cert->IsSelfSignedL()))
252 iState = EBuildChainStart;
256 iState = EValidateEnd;
261 iState = EValidateEnd;
267 TRequestStatus* status = &iStatus;
268 User::RequestComplete(status, KErrNone);
272 void CPKIXCertChainAO::HandleEValidateEndL()
276 __ASSERT_DEBUG(iValidationResult, User::Panic(_L("CPKICCertChainAO"), 1));
277 DoValidateL(*iValidationResult, iValidationTime, iInitialPolicies);
279 User::RequestComplete(iOriginalRequestStatus, KErrNone);
282 void CPKIXCertChainAO::DoCancel()
290 delete iCertsFromStoreRoots;
291 iCertsFromStoreRoots = 0;
293 User::RequestComplete(iOriginalRequestStatus, KErrCancel);
296 void CPKIXCertChainAO::ValidateL(CPKIXValidationResultBase& aValidationResult,
297 const TTime& aValidationTime,
298 const CArrayPtr<HBufC>* aInitialPolicies,
299 TRequestStatus& aStatus)
301 aValidationResult.Reset();
302 iValidationResult = &aValidationResult;
303 iValidationTime = aValidationTime;
304 iInitialPolicies = aInitialPolicies;
305 iOriginalRequestStatus = &aStatus;
306 iNumberOfAddedCertificates = 0;
308 __ASSERT_ALWAYS(!IsActive(), User::Panic(_L("CPKICCertChainAO"), 1));
312 // If iRoots is 0, it means that the caller gave a uid and that
313 // we must retrieve the trusted certificates from the different
315 iRoots = CPKIXChainBuilder::NewL();
320 // The caller gave a set of certificates it trusts,
321 // so we don't have to retrieve anything from the stores
322 iState = EBuildChainStart;
325 aStatus = KRequestPending;
326 TRequestStatus *status = &iStatus;
327 User::RequestComplete(status, KErrNone);
331 void CPKIXCertChainAO::CancelValidate()
336 void CPKIXCertChainAO::InitParamsL()
338 this function initialises signing key parameters for the certificates
339 -only DSA needs these at present
340 -we get the signing key, from the spki of the issuer
341 -if it's dsa, we look for params here
342 -if we find them we initialise the cert with them
343 -otherwise, we look in the issuer's issuer
344 -if we don't find them there we give up.
348 // If the root is DSA signed, set its parameters
350 TInt count = iPKIXCertChain.Chain().Count();
352 CX509Certificate* current = iPKIXCertChain.Chain().At(count-1);
353 TAlgorithmId signingAlgorithm = current->SigningAlgorithm().AsymmetricAlgorithm().Algorithm();
355 if (signingAlgorithm == EDSA)
358 const CSubjectPublicKeyInfo& key = current->PublicKey();
359 SetParamsL(*current, key.EncodedParams());
363 // Also the rest of the chain
365 for (TInt i = count - 2; i >= 0; i--)
368 current = iPKIXCertChain.Chain().At(i);
369 TAlgorithmId signingAlgorithm = current->SigningAlgorithm().AsymmetricAlgorithm().Algorithm();
371 if (signingAlgorithm == EDSA)
374 // Look down the chain for parameters
376 for (TInt j = i+1; j < count; j++)
379 CX509Certificate* issuer = iPKIXCertChain.Chain().At(j);
380 const CSubjectPublicKeyInfo& key = issuer->PublicKey();
381 if (key.EncodedParams() != KNullDesC8 && key.AlgorithmId() == EDSA)
383 SetParamsL(*current, key.EncodedParams());
394 void CPKIXCertChainAO::SetParamsL(CX509Certificate& aCert, const TPtrC8& aEncodedParams)
396 TX509KeyFactory factory;
397 CDSAParameters* theDSAParams = factory.DSAParametersL(aEncodedParams);
398 CleanupStack::PushL(theDSAParams);
400 CSigningKeyParameters* params = CSigningKeyParameters::NewLC();
401 params->SetDSAParamsL(*theDSAParams);
403 aCert.SetParametersL(*params);
405 CleanupStack::PopAndDestroy(2, theDSAParams);
408 void CPKIXCertChainAO::DoValidateL(CPKIXValidationResultBase& aValidationResult,
409 const TTime& aValidationTime,
410 const CArrayPtr<HBufC>* aInitialPolicies)
412 if (!iPKIXCertChain.ChainHasRoot())
414 aValidationResult.SetError(EChainHasNoRoot, 0);
418 CPKIXValidationState* state = CPKIXValidationState::NewLC(aValidationTime, iPKIXCertChain.Chain().Count(), aInitialPolicies);
419 TRAPD(err, ProcessCertsL(*state, aValidationResult));
420 //a leave here means either:
421 // -a validation error, in which case we've set the error field in result, or
422 // -some other error (e.g. OOM) in which case error is still EChainHasNoRoot
423 if ((err != KErrNone) && ((aValidationResult.Error().iReason) == EChainHasNoRoot))
424 //then we left with a non-validation-related error, so leave again...
428 CleanupStack::PopAndDestroy(state);
432 // ProcessCertsL: This function validates a complete certificate
433 // chain. If an error occurs in this function the function
434 // SetErrorAndLeaveL must be called.
436 // Note Do not use SetErrorAndLeaveL with EChainHasNoRoot (see TRAP code in
437 // CPKIXCertChainAO::DoValidateL )
438 void CPKIXCertChainAO::ProcessCertsL(CPKIXValidationState& aState,
439 CPKIXValidationResultBase& aResult) const
441 TPKIXPolicyConstraint policy(aState, aResult);
442 TPKIXNameConstraint name(aState, aResult);
443 TPKIXBasicConstraint basic(aState, aResult);
444 TPKIXKeyUsageConstraint keyUsage(aState, aResult);
445 for (; aState.iPos >= 0; aState.iPos--)
447 aState.iMaxPathLength--;
448 if (aState.iMaxPathLength < aState.iPos)
450 aResult.SetErrorAndLeaveL(EPathTooLong, aState.iPos);
452 const CX509Certificate* current = iPKIXCertChain.Chain().At(aState.iPos);
453 CCertificateValidationWarnings* certWarnings = CCertificateValidationWarnings::NewLC(aState.iPos);
454 aResult.AppendCertificateValidationObjectL(*certWarnings);
455 CleanupStack::Pop(certWarnings);
456 CriticalExtsL(aState, *current);
457 CheckCriticalExtsL(aState, aResult);
458 CheckSignatureAndNameL(*current, aState, aResult);
459 //!!!!NO!!checks for revocation at this time!!
461 if (!(current->ValidityPeriod().Valid(aState.iValidationTime)))
463 //validity period invalid, now check how to report this
464 if (iPKIXCertChain.ValidityPeriodCheckFatal())
466 aResult.SetErrorAndLeaveL(EDateOutOfRange, aState.iPos);
470 aResult.AppendWarningL(TValidationStatus(EDateOutOfRange, aState.iPos));
474 policy.CheckCertPoliciesL(*current);
475 name.CheckNameConstraintsL(*current);
476 keyUsage.CheckKeyUsageL(*current);
477 if (aState.iPos < (iPKIXCertChain.Chain().Count() - 1))
479 basic.CheckCertSubjectTypeL(*current);
481 basic.UpdatePathLengthConstraintsL(*current);
482 name.UpdateNameConstraintsL(*current);
483 policy.UpdatePolicyConstraintsL(*current);
484 aState.iCriticalExts->Reset();
486 policy.FinishPolicyCheckL();
487 //*copy* all policies from aState.iAuthorityConstrainedPolicies into aResult.iPolicies
488 TInt policyCount = aState.iAuthorityConstrainedPolicies->Count();
489 for (TInt i = 0; i < policyCount; i ++)
491 CX509CertPolicyInfo* policyInfo = CX509CertPolicyInfo::NewLC(*(aState.iAuthorityConstrainedPolicies->At(i)));
492 aResult.AppendPolicyL(*policyInfo);
493 CleanupStack::Pop(policyInfo);
496 aResult.SetError(EValidatedOK, 0);
499 void CPKIXCertChainAO::CriticalExtsL(CPKIXValidationState& aState,
500 const CX509Certificate& aCert) const
502 const CArrayPtrFlat<CX509CertExtension>& exts = aCert.Extensions();
503 TInt count = exts.Count();
504 for (TInt i = 0; i < count; i++)
506 CX509CertExtension* ext = exts.At(i);
509 aState.iCriticalExts->AppendL(ext);
514 void CPKIXCertChainAO::CheckSignatureAndNameL(const CX509Certificate& aCert, CPKIXValidationState& aState,
515 CPKIXValidationResultBase& aResult) const
517 TInt issuerPos = aState.iPos + 1;
518 if (issuerPos == iPKIXCertChain.Chain().Count())
521 if (aCert.IssuerName().ExactMatchL(aCert.SubjectName()))
522 //then it claims to be self signed, sig must verify
524 if (!(aCert.VerifySignatureL(aCert.PublicKey().KeyData())))
526 aResult.SetErrorAndLeaveL(ESignatureInvalid, aState.iPos);
530 //we generate a warning
532 aResult.AppendWarningL(TValidationStatus(ERootCertNotSelfSigned, aState.iPos));
536 //then it isn't the root: so names must chain & sigs must verify
538 const CX509Certificate* issuer = iPKIXCertChain.Chain().At(issuerPos);
539 if (!(aCert.IssuerName().ExactMatchL(issuer->SubjectName())))
541 aResult.SetErrorAndLeaveL(ENamesDontChain, aState.iPos);
543 if (!(aCert.VerifySignatureL(issuer->PublicKey().KeyData())))
545 aResult.SetErrorAndLeaveL(ESignatureInvalid, aState.iPos);
550 void CPKIXCertChainAO::CheckCriticalExtsL(CPKIXValidationState& aState, CPKIXValidationResultBase& aResult) const
552 TBool foundUnrecognisedCritExt;
554 // retrieve the supported list of critical extensions. If a critical extension is found whose OID matches an
555 // element in this set then certificate validation shall treat this as a warning instead of an error.
556 const RPointerArray<TDesC>& supportedCritExt = iPKIXCertChain.SupportedCriticalExtensions();
558 TInt count = aState.iCriticalExts->Count();
559 TInt supportedCount = supportedCritExt.Count();
560 for (TInt i = 0; i < count; i++)
562 foundUnrecognisedCritExt = ETrue;
563 const CX509CertExtension* ext = aState.iCriticalExts->At(i);
564 const TPtrC& extName = ext->Id();
566 for (TInt j = 0; j < supportedCount; ++j)
568 if (extName == *supportedCritExt[j])
570 foundUnrecognisedCritExt = EFalse;
571 HBufC* oid = extName.AllocLC();
572 aResult.AppendCriticalExtensionWarningL(*oid);
573 CleanupStack::Pop(oid);
578 if (extName == KExtendedKeyUsage)
580 aResult.AppendWarningL(TValidationStatus(ECriticalExtendedKeyUsage, aState.iPos));
582 else if (extName == KPolicyMapping)
584 aResult.AppendWarningL(TValidationStatus(ECriticalPolicyMapping, aState.iPos));
586 else if (extName == KInhibitAnyPolicy)
588 //ignore this in the same way
590 else if (extName == KDeviceIdListConstraint)
592 aResult.AppendWarningL(TValidationStatus(ECriticalDeviceId, aState.iPos));
594 else if(extName == KSidListConstraint)
596 aResult.AppendWarningL(TValidationStatus(ECriticalSid, aState.iPos));
598 else if(extName == KVidListConstraint)
600 aResult.AppendWarningL(TValidationStatus(ECriticalVid, aState.iPos));
602 else if(extName == KCapabilitiesConstraint)
604 aResult.AppendWarningL(TValidationStatus(ECriticalCapabilities, aState.iPos));
607 if (foundUnrecognisedCritExt)
609 aResult.SetErrorAndLeaveL(EUnrecognizedCriticalExtension, aState.iPos);