|
1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 #include <asn1dec.h> |
|
17 #include <x509certext.h> |
|
18 #include "oids.h" |
|
19 #include "pkixcertchain.h" |
|
20 #include "ocsp.h" |
|
21 #include <mcertstore.h> |
|
22 #include <ccertattributefilter.h> |
|
23 #include "ocsprequestandresponse.h" |
|
24 |
|
25 const TInt CERT_SIZE = 1000; |
|
26 |
|
27 EXPORT_C COCSPDelegateAuthorisationScheme* COCSPDelegateAuthorisationScheme::NewLC( |
|
28 MCertStore& aCertStore) |
|
29 /** |
|
30 Factory function allocates new instance of |
|
31 COCSPDelegateAuthorisationScheme. |
|
32 |
|
33 @param aCertStore Cert store interface. This is used to |
|
34 construct a certificate chain builder with |
|
35 CPKIXCertChainBase::NewL(). Certificates from |
|
36 the store are not used for validation if the response |
|
37 contains the responder certificate. If it does not contain |
|
38 the responder cert then validation would be done from store. |
|
39 @return New instance of COCSPDelegateAuthorisationScheme. |
|
40 Leaves if cannot successfully construct. The new |
|
41 object is placed on the cleanup stack. |
|
42 */ |
|
43 { |
|
44 COCSPDelegateAuthorisationScheme* scheme = |
|
45 new(ELeave) COCSPDelegateAuthorisationScheme(aCertStore); |
|
46 CleanupStack::PushL(scheme); |
|
47 scheme->ConstructL(); |
|
48 |
|
49 return scheme; |
|
50 } |
|
51 |
|
52 COCSPDelegateAuthorisationScheme::COCSPDelegateAuthorisationScheme( |
|
53 MCertStore& aCertStore) |
|
54 /** |
|
55 Initializes the CActive base class object and adds |
|
56 this object to the active scheduler. |
|
57 |
|
58 @param aCertStore Cert store interface. This is only used to |
|
59 construct a certificate chain builder with |
|
60 CPKIXCertChainBase::NewL(). Certificates from |
|
61 the store are not used for validation if the response |
|
62 contains the responder certificate. If it does not contain |
|
63 the responder cert then validation would be done from store. |
|
64 */ |
|
65 : CActive(EPriorityStandard), |
|
66 iCertStore(aCertStore) |
|
67 { |
|
68 CActiveScheduler::Add(this); |
|
69 } |
|
70 |
|
71 void COCSPDelegateAuthorisationScheme::ConstructL() |
|
72 /** |
|
73 Strictly, none of the resources owned by this object |
|
74 are required for all of its lifetime need to be preserved |
|
75 between validations. |
|
76 |
|
77 Some of the resources, such as the CPKIXCertChain |
|
78 instance have to be reallocated for every validation. |
|
79 |
|
80 The resources which can be allocated for the lifetime |
|
81 of this object are allocated here. This improves performance |
|
82 and simplifies the validation process, at a RAM cost. |
|
83 */ |
|
84 { |
|
85 // iRespSignCertChainBase is the array of intermediate |
|
86 // certificates which CPKIXCertChain will use to |
|
87 // chain (T->I) to (I->E). Because only immediate |
|
88 // delegates are supported, there will only be one |
|
89 // intermediate (or trusted root from CPKIXCertChain's |
|
90 // point-of-view.) Therefore the array can be |
|
91 // sized here. |
|
92 |
|
93 // RPointerArray<> doesn't have a Reserve() function. |
|
94 const CX509Certificate* nullCert = 0; |
|
95 User::LeaveIfError(iRespSignIntCert.Append(nullCert)); |
|
96 |
|
97 iCertFilter = CCertAttributeFilter::NewL(); |
|
98 iCertFilter->SetOwnerType(ECACertificate); |
|
99 iCertFilter->SetFormat(EX509Certificate); |
|
100 |
|
101 } |
|
102 |
|
103 COCSPDelegateAuthorisationScheme::~COCSPDelegateAuthorisationScheme() |
|
104 /** |
|
105 Cancels any outstanding validation and frees all resources |
|
106 owned by this object. |
|
107 */ |
|
108 { |
|
109 delete iCertFilter; |
|
110 iCertStoreEntries.Close(); |
|
111 iRespSignIntCert.Close(); |
|
112 |
|
113 delete iEncodedCert; |
|
114 delete iResponseCert; |
|
115 |
|
116 delete iPKIXResultBase; |
|
117 delete iRespSignCertChainBase; |
|
118 |
|
119 } |
|
120 |
|
121 /** |
|
122 Implement MOCSPAuthorisationScheme. |
|
123 |
|
124 Validate the response if it is signed by an immediate delegate of the intermediate entity. |
|
125 I.e. if the request has the form |
|
126 (T->I) (I->E) |
|
127 |
|
128 where T is trusted (at least for the purposes of this validation) and I is an intermediate, |
|
129 the response can be signed by R if (I->R). |
|
130 |
|
131 I is the CA, and can be equal to T, i.e. the certificate which is being tested for |
|
132 revocation can be signed by a root certificate. |
|
133 |
|
134 R must be immediately signed by I, and must have id-kp-OCSPSigning in its extended key usage. |
|
135 (RFC 2560 S4.2.2.2) |
|
136 */ |
|
137 void COCSPDelegateAuthorisationScheme::ValidateL( |
|
138 OCSP::TStatus& aOCSPStatus, COCSPResponse& aResponse, |
|
139 const TTime aValidationTime, TRequestStatus& aStatus, |
|
140 const COCSPRequest& aRequest) |
|
141 { |
|
142 // store the client status, so that it can be used for request |
|
143 // completion later. |
|
144 iClientStatus = &aStatus; |
|
145 aStatus = KRequestPending; |
|
146 |
|
147 // By assuming there is only one request / response pair, |
|
148 // there is no need to iterate through each response, which |
|
149 // simplifies this scheme's implementation. |
|
150 // the OCSP requests are constructed in |
|
151 // COCSPClient::ConstructL() such that exactly one certificate |
|
152 // and its signer are sent in each request. |
|
153 // (RFC 2560 S4.1.1 allows multiple pairs) |
|
154 |
|
155 iResponse = &aResponse; |
|
156 iRequest = &aRequest; |
|
157 |
|
158 iValidationTime = aValidationTime; |
|
159 |
|
160 // This default value is changed once the whole validation |
|
161 // process has completed successfully else the default value |
|
162 // is returned on failure. |
|
163 aOCSPStatus = OCSP::EResponseSignatureValidationFailure; |
|
164 iOCSPStatus = &aOCSPStatus; |
|
165 |
|
166 // set this to false before starting the scheme validation |
|
167 // for multiple certificates are being validated through |
|
168 // this scheme. |
|
169 iValidateFromResponse = EFalse; |
|
170 |
|
171 // This authentication scheme supports 2 ways of validation: |
|
172 // 1. If the response contains responder cert then validation |
|
173 // would be performed against the responder cert included in response. |
|
174 // This validation would also check whether the responder cert has been |
|
175 // issued by the CA cert which issued the certificate in question. |
|
176 // 2. If the response does not contain responder cert, search for it |
|
177 // in the store based on the ResponderId which is included in the |
|
178 // response. If the responder cert is found in the store which has signed |
|
179 // the response then authorize the response, in both the cases we check whether |
|
180 // responder cert has been issued by the CA which issued the cert in question, |
|
181 // and the CA should be present in the store. |
|
182 const TPtrC8* certChainData = aResponse.DataElementEncoding(COCSPResponse::ECertificateChain); |
|
183 if (certChainData == 0) // no signing certs |
|
184 { |
|
185 ValidateFromRootsL(); |
|
186 } |
|
187 else |
|
188 { |
|
189 iValidateFromResponse = ETrue; |
|
190 ValidateDelegateCertL(*certChainData, iValidationTime); |
|
191 } |
|
192 } |
|
193 |
|
194 /** |
|
195 Initialize this object to validate the certificate which was sent with the |
|
196 response against the CA which was used to sign the certificate in question. |
|
197 |
|
198 @param aResponseCertChain DER-encoded cert chain that was either sent with the response. |
|
199 or was retrieved from the store as a single certificate. |
|
200 @param aValidationTime Time to be used for chain validation of the delegate certificate. |
|
201 @post If successful, asynchronous validation will be set up. |
|
202 */ |
|
203 |
|
204 void COCSPDelegateAuthorisationScheme::ValidateDelegateCertL( |
|
205 const TDesC8& aResponseCertChain, const TTime aValidationTime) |
|
206 { |
|
207 // the response received can contain a chain of certificate, we need to extract the |
|
208 // responder certificate from the chain for further processing. If the certificate has been |
|
209 // retrieved from store then there would be no chain but the delegate certificate would be |
|
210 // retrieved. |
|
211 CX509Certificate* decodedResponseCert = OCSPUtils::GetResponderCertLC(aResponseCertChain); |
|
212 CleanupStack::Pop(decodedResponseCert); |
|
213 |
|
214 delete iResponseCert; |
|
215 iResponseCert = NULL; |
|
216 iResponseCert = decodedResponseCert; |
|
217 |
|
218 // First check the responder certificate in accordance to RFC 2560 for the following: |
|
219 // 1. Does it contain extension id-kp-OCSPSigning |
|
220 // 2. The responder id in the response matches the response certificate. |
|
221 // 3. The response is signed by the responder certificate |
|
222 if( OCSPUtils::DoesCertHaveOCSPSigningExtL(*iResponseCert) |
|
223 && OCSPUtils::DoesResponderIdMatchCertL(*iResponse, *iResponseCert) |
|
224 && OCSPUtils::IsResponseSignedByCertL(iResponse, *iResponseCert) ) |
|
225 { |
|
226 // construct a certificate chain containing the X -> R with T -> I as the intermediate. |
|
227 delete iPKIXResultBase; |
|
228 iPKIXResultBase = NULL; |
|
229 iPKIXResultBase = CPKIXValidationResultBase::NewL(); |
|
230 |
|
231 // get the intermediate which signed the EE |
|
232 const CX509Certificate& caCert = iRequest->CertInfo(0).Issuer(); |
|
233 |
|
234 // use the intermediate cert as the trusted root for |
|
235 // the purpose of building the chain. ("Intermediate" |
|
236 // in this context is the certificate which signed the EE.) |
|
237 iRespSignIntCert[0] = CONST_CAST(CX509Certificate*,&caCert); |
|
238 |
|
239 delete iRespSignCertChainBase; |
|
240 iRespSignCertChainBase = NULL; |
|
241 iRespSignCertChainBase = CPKIXCertChainBase::NewL(iCertStore, iResponseCert->Encoding(), iRespSignIntCert); |
|
242 |
|
243 // attempt to validate the chain. I.e. test that X = I. |
|
244 iRespSignCertChainBase->ValidateL(*iPKIXResultBase, aValidationTime, iStatus); |
|
245 |
|
246 iState = EOnChainValidation; |
|
247 SetActive(); |
|
248 } |
|
249 else |
|
250 { |
|
251 if(iValidateFromResponse) |
|
252 { |
|
253 User::RequestComplete(iClientStatus, KErrNone); |
|
254 return; |
|
255 } |
|
256 // this means that we are trying to retrieve the responder certificate from the store |
|
257 // and perform validation against it. As this certificate is not the valid responder |
|
258 // hence set the state to retrieve the next certificate from the store. |
|
259 else |
|
260 { |
|
261 iState = ERetrieveNext; |
|
262 TRequestStatus* status = &iStatus; |
|
263 User::RequestComplete(status,KErrNone); |
|
264 SetActive(); |
|
265 return; |
|
266 } |
|
267 } |
|
268 } |
|
269 |
|
270 void COCSPDelegateAuthorisationScheme::CancelValidate() |
|
271 /** |
|
272 Implement MOCSPAuthorisationScheme. This is an active |
|
273 object, and this function just calls Cancel(). See |
|
274 DoCancel() for information about the cancellation process. |
|
275 |
|
276 @see DoCancel |
|
277 */ |
|
278 { |
|
279 ASSERT(iRespSignCertChainBase != 0); |
|
280 iRespSignCertChainBase->CancelValidate(); |
|
281 } |
|
282 |
|
283 void COCSPDelegateAuthorisationScheme::RunL() |
|
284 { |
|
285 User::LeaveIfError(iStatus.Int()); |
|
286 switch(iState) |
|
287 { |
|
288 //Response validation after chain building. |
|
289 case EOnChainValidation: |
|
290 OnChainValidationL(); |
|
291 break; |
|
292 |
|
293 // state used to allocate sufficient memory for retrieving the next certificate |
|
294 case ERetrieveNext: |
|
295 OnRetrieveNextL(); |
|
296 break; |
|
297 |
|
298 // state used to retrieve the next certificate |
|
299 case ERetrievingEntry: |
|
300 OnRetrievingEntryL(); |
|
301 break; |
|
302 } |
|
303 } |
|
304 |
|
305 void COCSPDelegateAuthorisationScheme::OnChainValidationL() |
|
306 { |
|
307 TValidationError error = iPKIXResultBase->Error().iReason; |
|
308 if (error != EValidatedOK) |
|
309 { |
|
310 User::Leave(error); |
|
311 } |
|
312 *iOCSPStatus = OCSP::EValid; |
|
313 User::RequestComplete(iClientStatus, KErrNone); |
|
314 } |
|
315 |
|
316 void COCSPDelegateAuthorisationScheme::DoCancel() |
|
317 /** |
|
318 If a validation request is outstanding, then it is cancelled. |
|
319 This objects client, i.e. the owner of the TRequestStatus which |
|
320 was passed to Validate(), is completed with KErrCancel. |
|
321 */ |
|
322 { |
|
323 // object should be waiting for the chain builder to complete. |
|
324 // Therefore there must be a valid client request status pointer |
|
325 // and an instance of CPKIXCertChain. |
|
326 |
|
327 iRespSignCertChainBase->CancelValidate(); |
|
328 |
|
329 if (iClientStatus) |
|
330 { |
|
331 User::RequestComplete(iClientStatus, KErrCancel); |
|
332 } |
|
333 } |
|
334 |
|
335 TInt COCSPDelegateAuthorisationScheme::RunError(TInt aError) |
|
336 { |
|
337 User::RequestComplete(iClientStatus, aError); |
|
338 return KErrNone; |
|
339 } |
|
340 |
|
341 /** |
|
342 * Initiates request to retrieve the responder certificate from store. |
|
343 */ |
|
344 void COCSPDelegateAuthorisationScheme::ValidateFromRootsL() |
|
345 { |
|
346 iCertCount = -1; |
|
347 |
|
348 iCertStoreEntries.Close(); |
|
349 iCertStore.List(iCertStoreEntries, *iCertFilter, iStatus); |
|
350 |
|
351 delete iEncodedCert; |
|
352 iEncodedCert = NULL; |
|
353 iEncodedCert = HBufC8::NewL(CERT_SIZE); |
|
354 |
|
355 iState = ERetrieveNext; |
|
356 SetActive(); |
|
357 } |
|
358 |
|
359 /** |
|
360 * For list of certificate entries in the store retrieve each certificate. |
|
361 */ |
|
362 void COCSPDelegateAuthorisationScheme::OnRetrieveNextL() |
|
363 { |
|
364 if(++iCertCount < iCertStoreEntries.Count() ) |
|
365 { |
|
366 iState = ERetrievingEntry; |
|
367 TInt size = iCertStoreEntries[iCertCount]->Size(); |
|
368 if( size > iEncodedCert->Des().MaxLength() ) |
|
369 { |
|
370 delete iEncodedCert; |
|
371 iEncodedCert = NULL; |
|
372 iEncodedCert = HBufC8::NewL(size); |
|
373 } |
|
374 TPtr8 encodedCertDesc = iEncodedCert->Des(); |
|
375 iCertStore.Retrieve(*iCertStoreEntries[iCertCount], encodedCertDesc,iStatus); |
|
376 SetActive(); |
|
377 } |
|
378 else |
|
379 { |
|
380 User::RequestComplete(iClientStatus,OCSP::EResponseSignatureValidationFailure); |
|
381 } |
|
382 } |
|
383 |
|
384 /** |
|
385 * Once the certificate has been retrieved from the store, it should be sent for chain validation. |
|
386 * This intermediate state is required so that we can retrieve the certificate from the store. |
|
387 */ |
|
388 void COCSPDelegateAuthorisationScheme::OnRetrievingEntryL() |
|
389 { |
|
390 ValidateDelegateCertL(*iEncodedCert, iValidationTime); |
|
391 } |
|
392 |
|
393 /** |
|
394 * Returns the responder certificate. |
|
395 */ |
|
396 const CX509Certificate* COCSPDelegateAuthorisationScheme::ResponderCert() const |
|
397 { |
|
398 return iResponseCert; |
|
399 } |