|
1 /* |
|
2 * Copyright (c) 2007-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "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". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: General utility methods for certificate handling |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 |
|
20 #include <x509cert.h> |
|
21 |
|
22 #include "ikecert.h" |
|
23 #include "ikev1pkiservice.h" |
|
24 #include "ikev2const.h" |
|
25 #include "ikecaelem.h" |
|
26 #include "ikecertconst.h" |
|
27 |
|
28 |
|
29 const TUint8 Pkcs1v15Sha1Header[15] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}; |
|
30 |
|
31 |
|
32 TUint8* IkeCert::BERGetLengthL(TUint8* aP, TInt &aLen) |
|
33 { |
|
34 ASSERT(aP); |
|
35 aP++; // skip tag |
|
36 if (*aP <= 127) |
|
37 { |
|
38 aLen = *aP; |
|
39 aP++; |
|
40 } |
|
41 else if (*aP == 0x81) |
|
42 { |
|
43 aP++; |
|
44 aLen = *aP; |
|
45 aP++; |
|
46 } |
|
47 else if (*aP == 0x82) |
|
48 { |
|
49 aP++; |
|
50 aLen = *aP; |
|
51 aP++; |
|
52 aLen *= 256; |
|
53 aLen += *aP; |
|
54 aP++; |
|
55 } |
|
56 else { |
|
57 User::Leave(KErrGeneral); |
|
58 } |
|
59 return aP; |
|
60 } |
|
61 |
|
62 |
|
63 EXPORT_C HBufC8* IkeCert::GetCertificateFieldDERL(const CX509Certificate* aCert, TInt aField) |
|
64 { |
|
65 if ( !aCert ) |
|
66 return NULL; |
|
67 const TPtrC8 SignedData = aCert->SignedDataL(); |
|
68 if ( SignedData.Length() == 0 ) |
|
69 return NULL; |
|
70 TUint8* Ptr = (TUint8*)SignedData.Ptr(); |
|
71 TUint8* FieldPtr; |
|
72 HBufC8* FieldBfr = NULL; |
|
73 TInt length = 0; |
|
74 // begin sequence |
|
75 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
76 // context specific a0 03 |
|
77 if (*Ptr==0xa0) |
|
78 Ptr += 2; |
|
79 // version |
|
80 if (*Ptr==2) |
|
81 { |
|
82 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
83 Ptr += length; |
|
84 } |
|
85 // seq number |
|
86 if (*Ptr==2) |
|
87 { |
|
88 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
89 Ptr += length; |
|
90 } |
|
91 // sign algorithm |
|
92 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
93 Ptr += length; |
|
94 // issuer name |
|
95 FieldPtr = Ptr; |
|
96 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
97 Ptr += length; |
|
98 if ( aField == KIssuerName ) |
|
99 { |
|
100 FieldBfr = HBufC8::NewL(Ptr - FieldPtr); |
|
101 FieldBfr->Des().Copy(FieldPtr, (Ptr - FieldPtr)); |
|
102 } |
|
103 // validity period |
|
104 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
105 Ptr += length; |
|
106 // subject name |
|
107 FieldPtr = Ptr; |
|
108 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
109 Ptr += length; |
|
110 if ( aField == KSubjectName ) |
|
111 { |
|
112 FieldBfr = HBufC8::NewL(Ptr - FieldPtr); |
|
113 FieldBfr->Des().Copy(FieldPtr, (Ptr - FieldPtr)); |
|
114 } |
|
115 // public key info |
|
116 FieldPtr = Ptr; |
|
117 Ptr = IkeCert::BERGetLengthL(Ptr, length); |
|
118 Ptr += length; |
|
119 if ( aField == KPublicKeyInfo ) |
|
120 { |
|
121 FieldBfr = HBufC8::NewL(Ptr - FieldPtr); |
|
122 FieldBfr->Des().Copy(FieldPtr, (Ptr - FieldPtr)); |
|
123 } |
|
124 |
|
125 return FieldBfr; |
|
126 } |
|
127 |
|
128 |
|
129 HBufC8* IkeCert::BuildPkcs1v15HashL(const TDesC8 &aHashIn) |
|
130 { |
|
131 // |
|
132 // Build Pkcs1v15 format ASN1 header for specified hash. |
|
133 // Current implementation supports only hash algorithm SHA1 so |
|
134 // the aHashIn length data MUST be exactly the length of SHA1 hash |
|
135 // (20 bytes) |
|
136 // |
|
137 HBufC8* Pkcs1v15Hash = NULL; |
|
138 |
|
139 ASSERT( aHashIn.Length() == 20 ); |
|
140 |
|
141 Pkcs1v15Hash = HBufC8::NewL(20 + sizeof(Pkcs1v15Sha1Header)); |
|
142 if ( Pkcs1v15Hash ) |
|
143 { |
|
144 Pkcs1v15Hash->Des().Copy((TUint8*)Pkcs1v15Sha1Header, sizeof(Pkcs1v15Sha1Header)); |
|
145 Pkcs1v15Hash->Des().Append(aHashIn); |
|
146 } |
|
147 return Pkcs1v15Hash; |
|
148 } |
|
149 |
|
150 |
|
151 EXPORT_C HBufC8* IkeCert::GetCertificateFieldDERL(HBufC8* aCertBfr, TInt aField) |
|
152 { |
|
153 if ( !aCertBfr ) |
|
154 return NULL; |
|
155 CX509Certificate* Cert = CX509Certificate::NewL(*aCertBfr); |
|
156 CleanupStack::PushL(Cert); |
|
157 HBufC8* Field = IkeCert::GetCertificateFieldDERL(Cert, aField); |
|
158 CleanupStack::PopAndDestroy(Cert); |
|
159 return Field; |
|
160 } |
|
161 |
|
162 |
|
163 TBool IkeCert::AltNameExistsL(const CX509Certificate *aX509Cert, const TDesC8 &aId) |
|
164 { |
|
165 ASSERT(aX509Cert); |
|
166 const CX509CertExtension *AltNameExt = aX509Cert->Extension(KSubjectAltName); |
|
167 CX509GeneralName *NameId = CX509GeneralName::NewLC(aId); |
|
168 TBool found = EFalse; |
|
169 if (AltNameExt) |
|
170 { |
|
171 CX509AltNameExt* AltExt = CX509AltNameExt::NewLC(AltNameExt->Data()); |
|
172 const CArrayPtrFlat<CX509GeneralName>&Names = AltExt->AltName(); |
|
173 TInt Count = Names.Count(); |
|
174 for (TInt i = 0; i < Count; i++) |
|
175 { |
|
176 const CX509GeneralName *Name = Names.At(i); |
|
177 if (NameId->Tag() == Name->Tag() && |
|
178 NameId->Data() == Name->Data()) |
|
179 { |
|
180 found = ETrue; |
|
181 break; |
|
182 } |
|
183 } |
|
184 CleanupStack::PopAndDestroy(AltExt); |
|
185 } |
|
186 CleanupStack::PopAndDestroy(NameId); |
|
187 return found; |
|
188 } |
|
189 |
|
190 |
|
191 EXPORT_C HBufC8* IkeCert::GetSubjectAltNameDataL(const CX509Certificate* aX509Cert, TUint8 aIkeIdType) |
|
192 { |
|
193 ASSERT(aX509Cert); |
|
194 HBufC8* Identity = NULL; |
|
195 const CX509CertExtension* AltNameExt = aX509Cert->Extension(KSubjectAltName); |
|
196 |
|
197 if ( AltNameExt ) |
|
198 { |
|
199 TGNType SubjAltNameType; |
|
200 switch ( aIkeIdType ) |
|
201 { |
|
202 case ID_IPV4_ADDR: |
|
203 SubjAltNameType = EX509IPAddress; |
|
204 break; |
|
205 case ID_FQDN: |
|
206 SubjAltNameType = EX509DNSName; |
|
207 break; |
|
208 case ID_RFC822_ADDR: |
|
209 SubjAltNameType = EX509RFC822Name; |
|
210 break; |
|
211 case ID_IPV6_ADDR: |
|
212 SubjAltNameType = EX509IPAddress; |
|
213 break; |
|
214 default: |
|
215 SubjAltNameType = EX509RFC822Name; |
|
216 break; |
|
217 } |
|
218 CX509AltNameExt* AltExt = CX509AltNameExt::NewLC(AltNameExt->Data()); |
|
219 const CArrayPtrFlat<CX509GeneralName>&Names = AltExt->AltName(); |
|
220 TInt count = Names.Count(); |
|
221 for (TInt i = 0; i < count; i++) |
|
222 { |
|
223 const CX509GeneralName *Name = Names.At(i); |
|
224 if ( Name->Tag() == SubjAltNameType ) |
|
225 { |
|
226 // |
|
227 // Allocate buffer and Copy subject alt name data to it (type tag and length is not copied !) |
|
228 // |
|
229 Identity = HBufC8::NewL(Name->Data().Length() - 2); |
|
230 Identity->Des().Copy(((TUint8*)(Name->Data().Ptr()) + 2), (Name->Data().Length() - 2)); |
|
231 break; |
|
232 } |
|
233 } |
|
234 CleanupStack::PopAndDestroy(AltExt); |
|
235 |
|
236 } |
|
237 return Identity; |
|
238 } |
|
239 |
|
240 |
|
241 TInt IkeCert::CheckValidityPeriod(const CX509Certificate& aCert, TInt aWarningMargin, TInt aErrorMargin ) |
|
242 { |
|
243 TInt Status = KErrNone; |
|
244 TTime current; |
|
245 current.UniversalTime(); |
|
246 TTimeIntervalSeconds ErrorMargin(aErrorMargin); |
|
247 TTime StartTime = aCert.ValidityPeriod().Start(); |
|
248 TTime FinishTime = aCert.ValidityPeriod().Finish(); |
|
249 if ( (current + ErrorMargin) < StartTime ) |
|
250 { |
|
251 Status = KCertVerifyErrNotValidYet; |
|
252 } |
|
253 else |
|
254 { |
|
255 if (current > (FinishTime + ErrorMargin) ) |
|
256 { |
|
257 Status = KCertVerifyErrExpired; |
|
258 } |
|
259 else |
|
260 { |
|
261 // |
|
262 // If a warning margin defined, check is the certificate within that |
|
263 // |
|
264 if ( aWarningMargin ) |
|
265 { |
|
266 TTimeIntervalSeconds WarningMargin(aWarningMargin); |
|
267 if ( (current + WarningMargin) > (FinishTime + ErrorMargin) ) { |
|
268 Status = KCertVerifyWithinMargin; |
|
269 } |
|
270 } |
|
271 } |
|
272 } |
|
273 |
|
274 return Status; |
|
275 } |
|
276 |
|
277 // |
|
278 // Verify certificate extensions |
|
279 // |
|
280 TInt IkeCert::VerifyCertExtensionsL(const CX509Certificate& aX509Cert) |
|
281 { |
|
282 TInt Status = KErrNone; |
|
283 const CArrayPtrFlat<CX509CertExtension>& CertExtensions = aX509Cert.Extensions(); |
|
284 CX509CertExtension* Extension; |
|
285 TInt Count = CertExtensions.Count(); |
|
286 TInt i = 0; |
|
287 |
|
288 while ( i < Count ) |
|
289 { |
|
290 Extension = CertExtensions.At(i); |
|
291 if ( Extension->Id() == KKeyUsage ) |
|
292 { |
|
293 // |
|
294 // KeyUsage extension MUST have either digitalSignature or |
|
295 // nonRepudiation bit set |
|
296 // |
|
297 CX509KeyUsageExt* KeyUsage = CX509KeyUsageExt::NewL(Extension->Data()); |
|
298 if ( !KeyUsage->IsSet(EX509DigitalSignature) && !KeyUsage->IsSet(EX509NonRepudiation) ) |
|
299 { |
|
300 delete KeyUsage; |
|
301 Status = KCertVerifyKeyUsageErr; |
|
302 break; |
|
303 } |
|
304 else delete KeyUsage; |
|
305 } |
|
306 else if ( Extension->Id() == KBasicConstraints ) |
|
307 { |
|
308 // |
|
309 // BasicConstraints extension MUST NOT have CA indicator |
|
310 // |
|
311 CX509BasicConstraintsExt* BasicConstraints = CX509BasicConstraintsExt::NewL(Extension->Data()); |
|
312 if ( BasicConstraints->IsCA() ) |
|
313 { |
|
314 delete BasicConstraints; |
|
315 Status = KCertVerifyCACertificate; |
|
316 break; |
|
317 } |
|
318 else delete BasicConstraints; |
|
319 } |
|
320 else if ( Extension->Id() != KSubjectAltName && Extension->Critical() ) |
|
321 { |
|
322 // |
|
323 // Unsupported critical section ==> Certificate NOT accepted |
|
324 // |
|
325 Status = KCertVerifyCriticalExt; |
|
326 break; |
|
327 } |
|
328 |
|
329 i++; |
|
330 } |
|
331 |
|
332 return Status; |
|
333 } |
|
334 |
|
335 |
|
336 void IkeCert::CleanupSequence(TAny* aArray) |
|
337 { |
|
338 ASSERT(aArray); |
|
339 CArrayPtrFlat<TASN1DecGeneric>* array = reinterpret_cast<CArrayPtrFlat<TASN1DecGeneric>*>(aArray); |
|
340 ASSERT(array); |
|
341 array->ResetAndDestroy(); |
|
342 delete array; |
|
343 } |
|
344 |