|
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 // Define generic methods which can be used accross the ocsp component. |
|
15 // |
|
16 // |
|
17 |
|
18 #include <asymmetrickeys.h> |
|
19 #include <ocsp.h> |
|
20 #include "oids.h" |
|
21 #include <x509certext.h> |
|
22 #include "ocsprequestandresponse.h" |
|
23 |
|
24 namespace OCSPUtils |
|
25 { |
|
26 /** |
|
27 Checks whether the supplied certificate matches the responder |
|
28 ID in the supplied response. This implements S3.2.3 of RFC2560. |
|
29 Note this does <em>not</em> mean the response is signed by the |
|
30 certificate's subject. It just checks that the certificate's |
|
31 name, or a hash of its key, is present in the response's plaintext. |
|
32 |
|
33 @param aResponse Response whose responder ID should describe |
|
34 <var>aCert</var>. |
|
35 @param aCert Certificate whose name or key should be |
|
36 in the response. |
|
37 @return ETrue if the response's responder ID matches <var>aCert</var>, |
|
38 EFalse otherwise. |
|
39 */ |
|
40 TBool DoesResponderIdMatchCertL( const COCSPResponse& aResponse, |
|
41 const CX509Certificate& aResponderCert) |
|
42 { |
|
43 return (DoesIssuerKeyMatchL(aResponse,aResponderCert) || DoesDNNameMatchL(aResponse,aResponderCert)); |
|
44 } |
|
45 |
|
46 TBool DoesDNNameMatchL( const COCSPResponse& aResponse, const CX509Certificate& aCert) |
|
47 { |
|
48 TBool result = EFalse; |
|
49 const TPtrC8* nameData = aResponse.DataElementEncoding(COCSPResponse::EResponderIDName); |
|
50 if (nameData) |
|
51 { |
|
52 CX500DistinguishedName* name = CX500DistinguishedName::NewLC(*nameData); |
|
53 if (aCert.SubjectName().ExactMatchL(*name)) |
|
54 { |
|
55 result = ETrue; |
|
56 } |
|
57 CleanupStack::PopAndDestroy(name); |
|
58 } |
|
59 return result; |
|
60 } |
|
61 |
|
62 TBool DoesIssuerKeyMatchL(const COCSPResponse& aResponse, const CX509Certificate& aCert) |
|
63 { |
|
64 TBool result = EFalse; |
|
65 // check the responder's issuer key and verify that it is the same as the issuer cert |
|
66 const TPtrC8* keyHash = aResponse.DataElementEncoding(COCSPResponse::EResponderIDKeyHash); |
|
67 const TPtrC8* keyEncoding = aCert.DataElementEncoding(CX509Certificate::ESubjectPublicKeyInfo); |
|
68 if (!keyHash || !keyEncoding) |
|
69 { |
|
70 return result; |
|
71 } |
|
72 |
|
73 TASN1DecSequence decSeq; |
|
74 TInt pos = 0; |
|
75 CArrayPtrFlat<TASN1DecGeneric>* seq = decSeq.DecodeDERLC(*keyEncoding, pos, 2, KMaxTInt); |
|
76 |
|
77 TASN1DecBitString decBitStr; |
|
78 HBufC8* toHashData = decBitStr.ExtractOctetStringL(*seq->At(1)); |
|
79 CleanupStack::PushL(toHashData); |
|
80 |
|
81 CSHA1* hashAlg = CSHA1::NewL(); |
|
82 CleanupStack::PushL(hashAlg); |
|
83 TPtrC8 certKeyHash = hashAlg->Hash(*toHashData); |
|
84 |
|
85 if (certKeyHash == *keyHash) |
|
86 { |
|
87 // Public key hashes match |
|
88 result = ETrue; |
|
89 } |
|
90 CleanupStack::PopAndDestroy(3, seq); // hashAlg, toHashData, seq |
|
91 |
|
92 return result; |
|
93 } |
|
94 |
|
95 TBool IsResponseSignedByCertL( |
|
96 COCSPResponse* aResponse, const CX509Certificate& aCert) |
|
97 /** |
|
98 Checks whether the supplied response is signed by the supplied |
|
99 certificate. |
|
100 |
|
101 @param aResponse Response which should be signed by aCert. |
|
102 @param aCert Candidate certificate which may have been |
|
103 used to sign <var>aResponse</var>. |
|
104 @return ETrue if <var>aCert</var> is used to sign <var>aResponse</var>, |
|
105 EFalse otherwise. Note <code>COCSPResponse</code> derives |
|
106 from <code>CSignedObject</code>. |
|
107 */ |
|
108 { |
|
109 if (aCert.PublicKey().AlgorithmId() == EDSA) |
|
110 { |
|
111 TX509KeyFactory factory; |
|
112 CDSAParameters* theDSAParams = factory.DSAParametersL(aCert.PublicKey().EncodedParams()); |
|
113 CleanupStack::PushL(theDSAParams); |
|
114 |
|
115 CSigningKeyParameters* params = CSigningKeyParameters::NewLC(); |
|
116 params->SetDSAParamsL(*theDSAParams); |
|
117 |
|
118 aResponse->SetParametersL(*params); |
|
119 CleanupStack::PopAndDestroy(2, theDSAParams); |
|
120 } |
|
121 |
|
122 return aResponse->VerifySignatureL(aCert.PublicKey().KeyData()); |
|
123 } |
|
124 |
|
125 TBool DoesCertHaveOCSPNoCheckExt(const CX509Certificate& aCert) |
|
126 /** |
|
127 Test whether the supplied certificate has id-pkix-ocsp-nocheck |
|
128 in an extendedKeyUsage extension. |
|
129 |
|
130 @param aCert Certificate to test for id-kp-OCSPSigning. |
|
131 |
|
132 @return ETrue if the supplied certificate has an |
|
133 extendedKeyUsage extension which contains |
|
134 id-pkix-ocsp-nocheck. |
|
135 */ |
|
136 { |
|
137 return aCert.Extension(KOCSPOidNoCheck) != NULL; |
|
138 } |
|
139 |
|
140 /** |
|
141 * Determine the URI of the OCSP server to use for a certificate. Checks to see |
|
142 * if there is an authority info access extension containing an access |
|
143 * description with method oid id-ad-ocsp. If so, it copies the access |
|
144 * description into a new descriptor which it stores in iURI. Otherwise it |
|
145 * returns iDefaultURI, which may be null. |
|
146 */ |
|
147 |
|
148 TDesC8* ServerUriL(const CX509Certificate& aCert, const COCSPParameters* aParameters ) |
|
149 { |
|
150 |
|
151 TDesC8* uri = NULL; |
|
152 |
|
153 // check whether AIA is present or not: |
|
154 // if present and UseAIA is enabled the get the AIA extension. |
|
155 // if AIA is absent the else if part will handle it |
|
156 // if present and UseAIA is disabled, means that iUseGlobalOCSPUri is enabled, |
|
157 // do not leave with an error in this case the next case will handle this condition. |
|
158 if( aParameters->UseAIA() && OCSPUtils::IsAIAForOCSPPresentL(aCert) ) |
|
159 { |
|
160 uri = OCSPUtils::GetAIAL(aCert); |
|
161 } |
|
162 else if( aParameters->DefaultURI() != KNullDesC8()) |
|
163 { |
|
164 uri = aParameters->DefaultURI().AllocL(); |
|
165 } |
|
166 // the extension is absent and iUseGlobalOCSPUri is disabled, it has reached this case |
|
167 // where neither is there an AIA in the cert nor is the global ocsp responder available, |
|
168 // this should leave with EInvalidURI but this has been done to maintain backward compatibility. |
|
169 else |
|
170 { |
|
171 User::Leave(KErrArgument); |
|
172 } |
|
173 return uri; |
|
174 } |
|
175 |
|
176 // check whether AIA is present or not: |
|
177 // if present and UseAIA is enabled then return true. |
|
178 // if default uri is not null return true. |
|
179 TBool IsUriAvailableL(const CX509Certificate& aCert, const COCSPParameters* aParameters ) |
|
180 { |
|
181 return ( aParameters->UseAIA() && OCSPUtils::IsAIAForOCSPPresentL(aCert) ) || |
|
182 ( aParameters->DefaultURI() != KNullDesC8() ) ; |
|
183 } |
|
184 |
|
185 TBool IsAIAForOCSPPresentL(const CX509Certificate& aCert) |
|
186 { |
|
187 TBool found = EFalse; |
|
188 const CX509CertExtension* ext = aCert.Extension(KAuthorityInfoAccess); |
|
189 |
|
190 if(ext) |
|
191 { |
|
192 CX509AuthInfoAccessExt* auth = CX509AuthInfoAccessExt::NewLC(ext->Data()); |
|
193 for (TInt i = 0 ; i < auth->AccessDescriptions().Count() ; ++i) |
|
194 { |
|
195 const CX509AccessDescription* desc = auth->AccessDescriptions()[i]; |
|
196 if (desc->Method() == KAccessMethodOCSP && desc->Location().Tag() == EX509URI ) |
|
197 { |
|
198 found = ETrue; |
|
199 break; |
|
200 } |
|
201 } // end of for |
|
202 CleanupStack::PopAndDestroy(auth); |
|
203 } |
|
204 return found; |
|
205 } |
|
206 |
|
207 TDesC8* GetAIAL(const CX509Certificate& aCert) |
|
208 { |
|
209 TDesC8* uri = NULL; |
|
210 |
|
211 const CX509CertExtension* ext = aCert.Extension(KAuthorityInfoAccess); |
|
212 |
|
213 if (ext) |
|
214 { |
|
215 CX509AuthInfoAccessExt* auth = CX509AuthInfoAccessExt::NewLC(ext->Data()); |
|
216 for (TInt i = 0 ; i < auth->AccessDescriptions().Count() ; ++i) |
|
217 { |
|
218 const CX509AccessDescription* desc = auth->AccessDescriptions()[i]; |
|
219 if (desc->Method() == KAccessMethodOCSP) |
|
220 { |
|
221 const CX509GeneralName& gn = desc->Location(); |
|
222 if (gn.Tag() == EX509URI) |
|
223 { |
|
224 // Decode the general name rather than using CX509IPBaseURI, |
|
225 // as this currently rejects some URIs |
|
226 TASN1DecIA5String encStr; |
|
227 TInt pos = 0; |
|
228 HBufC* suri = encStr.DecodeDERL(gn.Data(), pos); |
|
229 CleanupStack::PushL(suri); |
|
230 HBufC8* buf = HBufC8::NewL(suri->Length()); |
|
231 uri = buf; |
|
232 buf->Des().Copy(*suri); |
|
233 CleanupStack::PopAndDestroy(suri); |
|
234 break; |
|
235 } |
|
236 } |
|
237 } |
|
238 CleanupStack::PopAndDestroy(auth); |
|
239 } |
|
240 return uri; |
|
241 } |
|
242 |
|
243 /** |
|
244 Test whether the supplied certificate has id-kp-OCSPSigning |
|
245 in an extendedKeyUsage extension. This tests whether the |
|
246 certificate is a trusted responder, as defined in RFC 2560 S4.2.2.2. |
|
247 |
|
248 @param aCert Certificate to test for id-kp-OCSPSigning. |
|
249 This should be the certificate which was |
|
250 immediately signed by the CA which issued |
|
251 the certificate in question. |
|
252 @return ETrue iff the supplied certificate has an |
|
253 extendedKeyUsage extension which contains |
|
254 id-kp-OCSPSigning. |
|
255 */ |
|
256 |
|
257 TBool DoesCertHaveOCSPSigningExtL(const CX509Certificate& aCert) |
|
258 { |
|
259 TBool found = EFalse; |
|
260 // check has extended key usage |
|
261 const CX509CertExtension* ekuExt = aCert.Extension(KExtendedKeyUsage); |
|
262 if (ekuExt != NULL) |
|
263 { |
|
264 // get set of extended key usages |
|
265 const TDesC8& extData = ekuExt->Data(); |
|
266 CX509ExtendedKeyUsageExt* extUses = CX509ExtendedKeyUsageExt::NewLC(extData); |
|
267 |
|
268 // check if id-kp-OCSPSigning is one of the uses |
|
269 const CArrayPtrFlat<HBufC>& usages = extUses->KeyUsages(); |
|
270 TInt usageCount = usages.Count(); |
|
271 for (TInt i = 0; i < usageCount; ++i) |
|
272 { |
|
273 if (*usages[i] == KOCSPOidOCSPSigning) |
|
274 { |
|
275 found = ETrue; |
|
276 break; |
|
277 } |
|
278 } |
|
279 CleanupStack::PopAndDestroy(extUses); |
|
280 } |
|
281 return found; |
|
282 } |
|
283 |
|
284 |
|
285 CX509Certificate* GetResponderCertLC(const TDesC8& aEncodedCerts) |
|
286 { |
|
287 // This specifies the position of the certificate which is to be retrieved from |
|
288 // the encoded certificate chain. As responder certificate would always be the |
|
289 // end entity certificate, here we are retrieving the first one from the chain. |
|
290 TInt pos = 0; |
|
291 return CX509Certificate::NewLC(aEncodedCerts, pos); |
|
292 } |
|
293 } // OCSPUtils namespace |