|
1 /* |
|
2 * Copyright (c) 2005-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 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". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "certificatechain.h" |
|
20 using namespace std; |
|
21 |
|
22 CertificateChain::CertificateChain() |
|
23 :iDevCertificate (false) , |
|
24 iDevCapabilities (0) , |
|
25 iValidationStatus (EValidationSuccessful) |
|
26 { |
|
27 } |
|
28 |
|
29 bool CertificateChain::ValidateChain(const Options& aOptions , const string& aFile , const SWICertStore& aStore ) |
|
30 { |
|
31 X509* endEntity = NULL ; |
|
32 X509* previous = NULL ; |
|
33 X509* current = NULL; |
|
34 ASN1_UTCTIME* from = NULL ; |
|
35 ASN1_UTCTIME* to = NULL; |
|
36 char buf[KLength]; |
|
37 int count = 0; |
|
38 int verify = 0; |
|
39 string issuer ; |
|
40 bool isValid = false ; |
|
41 const char* fileName = aFile.c_str(); |
|
42 |
|
43 FILE *fp = fopen(fileName,"r"); |
|
44 if(fp == NULL) |
|
45 { |
|
46 throw EUnableToOpenFile ; |
|
47 } |
|
48 do |
|
49 { |
|
50 current = PEM_read_X509 (fp, NULL, NULL, NULL); |
|
51 //end entity will hold the first certificate in the chain which signs the sis file. |
|
52 if(count == 0 && current) |
|
53 { |
|
54 endEntity = current; |
|
55 } |
|
56 |
|
57 if(current) |
|
58 { |
|
59 X509_NAME_oneline (X509_get_issuer_name (current) , buf , KLength); |
|
60 iCertificateChain.push_back(buf); |
|
61 X509_NAME_oneline (X509_get_subject_name (current), buf , KLength); |
|
62 iCertificateChain.push_back(buf); |
|
63 |
|
64 from = X509_get_notBefore (current); |
|
65 to = X509_get_notAfter (current); |
|
66 ExtractCertValidityPeriod(*from , *to); |
|
67 |
|
68 //validity period status |
|
69 int validFrom = X509_cmp_current_time(from); |
|
70 int validTo = X509_cmp_current_time(to); |
|
71 |
|
72 //check whether "Valid From" is less than the current time and |
|
73 //"Valid To" is greater than current time for each certificate in the chain. |
|
74 //If check fails,current time is not within certificate's validity period. |
|
75 if(validFrom >= 0 || validTo <= 0) |
|
76 { |
|
77 iValidationStatus = iValidationStatus | EValidityCheckFailure ; |
|
78 } |
|
79 |
|
80 //count is greater than one when there is more than 1 certificate in the chain |
|
81 //so that the public key of the next certificate is used to verify the signature of |
|
82 //previous certificate in the chain. |
|
83 if(count) |
|
84 { |
|
85 EVP_PKEY* pubKey = X509_get_pubkey(current); |
|
86 if (!pubKey) |
|
87 { |
|
88 #ifdef DUMPCHAINVALIDITYTOOL |
|
89 cout << "no public key in certificate" << endl; |
|
90 #endif |
|
91 fclose(fp); |
|
92 return false; |
|
93 } |
|
94 |
|
95 //Signature validation. |
|
96 if(count == 1) |
|
97 { |
|
98 verify = X509_verify(endEntity,pubKey); |
|
99 } |
|
100 |
|
101 else |
|
102 { |
|
103 verify = X509_verify(previous,pubKey); |
|
104 } |
|
105 |
|
106 if(!verify) |
|
107 { |
|
108 iValidationStatus = iValidationStatus | EBrokenChain ; |
|
109 } |
|
110 |
|
111 if(previous) |
|
112 { |
|
113 X509_free (previous); |
|
114 } |
|
115 |
|
116 previous = current; |
|
117 } |
|
118 |
|
119 count++; |
|
120 } |
|
121 }while(current); |
|
122 |
|
123 fclose(fp); |
|
124 CheckForChainValidity(iValidationStatus); |
|
125 |
|
126 //check whether end entity is devcert,self signed or others. |
|
127 iDevCertificate = CheckIfDevCert(*endEntity); |
|
128 if(iDevCertificate) |
|
129 { |
|
130 #ifdef DUMPCHAINVALIDITYTOOL |
|
131 cout << "SIS File is signed with a Developer Certificate with Constraints :\n" << endl; |
|
132 cout << iDevConstraints << endl; |
|
133 #endif |
|
134 } |
|
135 |
|
136 // end entity certificate is self signed |
|
137 else if(CheckIfSelfSigned(*endEntity)) |
|
138 { |
|
139 #ifdef DUMPCHAINVALIDITYTOOL |
|
140 cout << ": SIS File is signed by a Self Signed Certificate " << endl; |
|
141 #endif |
|
142 } |
|
143 |
|
144 //check whether the last certificate in the chain is validated against the certstore. |
|
145 //if there is only 1 certificate in the chain . |
|
146 if(count == 1) |
|
147 { |
|
148 isValid = ValidateWithCertstore(*endEntity , aStore , issuer); |
|
149 } |
|
150 else |
|
151 { |
|
152 isValid = ValidateWithCertstore(*previous , aStore , issuer); |
|
153 } |
|
154 |
|
155 if(previous) |
|
156 { |
|
157 X509_free (previous); |
|
158 previous = NULL ; |
|
159 } |
|
160 |
|
161 if(endEntity) |
|
162 { |
|
163 X509_free (endEntity); |
|
164 endEntity = NULL ; |
|
165 } |
|
166 |
|
167 if(isValid) |
|
168 { |
|
169 #ifdef DUMPCHAINVALIDITYTOOL |
|
170 cout << "Is Validated By : " << issuer << endl << endl; |
|
171 #endif |
|
172 return true; |
|
173 } |
|
174 |
|
175 return false; |
|
176 } |
|
177 |
|
178 void CertificateChain::ExtractCertValidityPeriod(const ASN1_UTCTIME& aFrom , const ASN1_UTCTIME& aTo) |
|
179 { |
|
180 char date [12] = "xx/xx/xx"; |
|
181 date [0] = aFrom . data [4]; |
|
182 date [1] = aFrom . data [5]; |
|
183 date [3] = aFrom . data [2]; |
|
184 date [4] = aFrom . data [3]; |
|
185 date [6] = aFrom . data [0]; |
|
186 date [7] = aFrom . data [1]; |
|
187 iCertificateChain.push_back(date); |
|
188 |
|
189 date [0] = aTo . data [4]; |
|
190 date [1] = aTo . data [5]; |
|
191 date [3] = aTo . data [2]; |
|
192 date [4] = aTo . data [3]; |
|
193 date [6] = aTo . data [0]; |
|
194 date [7] = aTo . data [1]; |
|
195 iCertificateChain.push_back(date); |
|
196 } |
|
197 |
|
198 void CertificateChain::DisplaySigningChain(const StringVector& aSigningChain) |
|
199 { |
|
200 if(aSigningChain.size()) |
|
201 { |
|
202 std::vector<std::string>::const_iterator chainIter = aSigningChain.begin(); |
|
203 do |
|
204 { |
|
205 cout << "Issued By : " << (*chainIter) << endl; |
|
206 ++chainIter; |
|
207 cout << "Issued To : " << (*chainIter) << endl; |
|
208 ++chainIter; |
|
209 cout << "Valid From : " << (*chainIter) << endl; |
|
210 ++chainIter; |
|
211 cout << "Valid To : " << (*chainIter) << endl << endl; |
|
212 ++chainIter; |
|
213 }while(chainIter != aSigningChain.end()); |
|
214 } |
|
215 } |
|
216 |
|
217 bool CertificateChain::CheckIfDevCert(X509& aEndEntity) |
|
218 { |
|
219 X509_EXTENSION* extension = NULL; |
|
220 ASN1_OCTET_STRING* extData = NULL; |
|
221 ASN1_OBJECT * asnObj = NULL; |
|
222 int constraintCount = X509_get_ext_count(&aEndEntity); |
|
223 for(int k = 0 ; k < constraintCount ; k++) |
|
224 { |
|
225 extension = X509_get_ext(&aEndEntity, k); |
|
226 extData = X509_EXTENSION_get_data(extension); |
|
227 |
|
228 asnObj = X509_EXTENSION_get_object(extension); |
|
229 |
|
230 char buf[KLength]; |
|
231 i2t_ASN1_OBJECT(buf, KLength, asnObj); |
|
232 |
|
233 //all the constraints in the vector are compared against the predefined ones. |
|
234 if(KDeviceIdListConstraint.compare(buf) == 0) |
|
235 { |
|
236 iDevConstraints.append("DeviceId Constraints :\n\n"); |
|
237 string deviceId = ExtractDeviceConstaints(extData); |
|
238 //only if device id constraint is present in devcert,append value to iDevConstraints |
|
239 if(deviceId.length()) |
|
240 { |
|
241 iDevConstraints.append(deviceId); |
|
242 } |
|
243 else |
|
244 { |
|
245 iDevConstraints.append("Empty Device Constraints List. \n"); |
|
246 } |
|
247 } |
|
248 |
|
249 else if(KSidListConstraint.compare(buf) == 0) |
|
250 { |
|
251 iDevConstraints.append("\nSID Constraints :\n\n"); |
|
252 string sid = ExtractASN1IntegerData(extData); |
|
253 if(sid.length()) |
|
254 { |
|
255 iDevConstraints.append(sid); |
|
256 } |
|
257 else |
|
258 { |
|
259 iDevConstraints.append("Empty SID Constraints List. \n"); |
|
260 } |
|
261 } |
|
262 |
|
263 else if(KVidListConstraint.compare(buf) == 0) |
|
264 { |
|
265 iDevConstraints.append("\nVID Constraints :\n\n"); |
|
266 string vid = ExtractASN1IntegerData(extData); |
|
267 if(vid.length()) |
|
268 { |
|
269 iDevConstraints.append(vid); |
|
270 } |
|
271 else |
|
272 { |
|
273 iDevConstraints.append("Empty VID Constraints List.\n"); |
|
274 } |
|
275 } |
|
276 |
|
277 else if(KCapabilitiesConstraint.compare(buf) == 0) |
|
278 { |
|
279 iDevConstraints.append("\nCapabilities Constraints :\n\n"); |
|
280 string capabilities = ExtractCapabilities(extData); |
|
281 if(capabilities.length()) |
|
282 { |
|
283 iDevConstraints.append(capabilities); |
|
284 } |
|
285 else |
|
286 { |
|
287 iDevConstraints.append("Empty Capabilitites Constraints List. \n"); |
|
288 } |
|
289 } |
|
290 } |
|
291 |
|
292 //if there are constaints with oid matching the predefined ones,they are devcerts |
|
293 if(iDevConstraints.length()) |
|
294 { |
|
295 return true; |
|
296 } |
|
297 |
|
298 return false; |
|
299 } |
|
300 |
|
301 bool CertificateChain::CheckIfSelfSigned(X509& aEndEntity) |
|
302 { |
|
303 char buf[KLength] ; |
|
304 string issuer ; |
|
305 string subject ; |
|
306 X509_NAME_oneline (X509_get_issuer_name (&aEndEntity), buf, KLength); |
|
307 issuer = buf; |
|
308 X509_NAME_oneline (X509_get_subject_name (&aEndEntity), buf, KLength); |
|
309 subject = buf; |
|
310 |
|
311 if((issuer.compare(subject)) == 0) |
|
312 { |
|
313 EVP_PKEY* pubKey = X509_get_pubkey(&aEndEntity); |
|
314 if (!pubKey) |
|
315 { |
|
316 #ifdef DUMPCHAINVALIDITYTOOL |
|
317 cout << "no public key in certificate" << endl; |
|
318 #endif |
|
319 return false; |
|
320 } |
|
321 |
|
322 int verify = X509_verify(&aEndEntity, pubKey); |
|
323 if(verify) |
|
324 { |
|
325 return true; |
|
326 } |
|
327 } |
|
328 return false; |
|
329 } |
|
330 |
|
331 bool CertificateChain::ValidateWithCertstore(X509& aPrevious, const SWICertStore& aStore , string& aIssuer) |
|
332 { |
|
333 int verifyWithRoot = 0; |
|
334 const vector <X509*>& certStoreVector = aStore.GetRootCertVector(); |
|
335 if(certStoreVector.size()) |
|
336 { |
|
337 char buf[KLength]; |
|
338 for(std::vector<X509*>::const_iterator root = certStoreVector.begin(); root != certStoreVector.end(); ++root) |
|
339 { |
|
340 EVP_PKEY* pubkey = X509_get_pubkey(*root); |
|
341 if (!pubkey) |
|
342 { |
|
343 #ifdef DUMPCHAINVALIDITYTOOL |
|
344 cout << "no public key in certificate" << endl; |
|
345 #endif |
|
346 return false; |
|
347 } |
|
348 |
|
349 verifyWithRoot = X509_verify(&aPrevious, pubkey); |
|
350 if(verifyWithRoot) |
|
351 { |
|
352 X509_NAME_oneline (X509_get_issuer_name (*root),buf, KLength); |
|
353 char* issued = strstr (buf, "/CN="); |
|
354 if (issued) |
|
355 { |
|
356 issued += 4; |
|
357 char* end = strchr (issued, '/'); |
|
358 if (end) |
|
359 { |
|
360 *end = 0; |
|
361 } |
|
362 aIssuer=issued; |
|
363 string rootSignature((const char*)(aPrevious.signature->data) , (aPrevious.signature->length)); |
|
364 iValidatedRootSignatures = rootSignature; |
|
365 } |
|
366 return true; |
|
367 } |
|
368 } |
|
369 } |
|
370 return false; |
|
371 } |
|
372 |
|
373 |
|
374 void CertificateChain::CheckForChainValidity(const int& aValidationStatus) |
|
375 { |
|
376 #ifdef DUMPCHAINVALIDITYTOOL |
|
377 cout << "Certificate Chain :" << endl << endl; |
|
378 |
|
379 switch(aValidationStatus) |
|
380 { |
|
381 case 0: |
|
382 { |
|
383 DisplaySigningChain(iCertificateChain); |
|
384 break; |
|
385 } |
|
386 |
|
387 case 1: |
|
388 { |
|
389 DisplaySigningChain(iCertificateChain); |
|
390 cout << "Invalid Certificate Chain : Validity Period Check Failed" << endl; |
|
391 break; |
|
392 } |
|
393 |
|
394 case 2: |
|
395 { |
|
396 DisplaySigningChain(iCertificateChain); |
|
397 cout << "Broken Chain : Signature Verification Failed" << endl; |
|
398 break; |
|
399 } |
|
400 |
|
401 case 3: |
|
402 { |
|
403 DisplaySigningChain(iCertificateChain); |
|
404 cout << "Signature Verification and Validity Period Check Failed " << endl; |
|
405 break; |
|
406 } |
|
407 default: |
|
408 { |
|
409 cout << "Error" << endl; |
|
410 break; |
|
411 } |
|
412 } |
|
413 #endif |
|
414 } |
|
415 |
|
416 string CertificateChain ::ExtractCapabilities(const ASN1_BIT_STRING* aString) |
|
417 { |
|
418 string capabilities; |
|
419 unsigned char* buf = aString->data; |
|
420 int dataLen = strlen((const char*)buf); |
|
421 if(dataLen) |
|
422 { |
|
423 // check the type is "bitstring",where 3 represents bitstring. |
|
424 if (*(buf++) != KDerBitStringTag) |
|
425 { |
|
426 throw ENotADerBitString; |
|
427 } |
|
428 |
|
429 // Find the length of the bitstring contained in this buffer |
|
430 unsigned long len = GetBitStringLength(&buf); |
|
431 |
|
432 // skip the padding length field |
|
433 buf++; |
|
434 |
|
435 int capability = 0; |
|
436 while (len--) |
|
437 { |
|
438 char c = *(buf++); |
|
439 for (int j = KByteLength-1 ; j >= 0; --j) |
|
440 { |
|
441 if (c & (1 << j)) |
|
442 { |
|
443 if (capability < KNumberOfCaps) |
|
444 { |
|
445 capabilities.append(CapabilityList[capability]); |
|
446 capabilities.append("\n"); |
|
447 } |
|
448 else |
|
449 { |
|
450 capabilities.append("Unknown"+capability); |
|
451 } |
|
452 |
|
453 iDevCapabilities += pow(2,capability); |
|
454 } |
|
455 capability++; |
|
456 } |
|
457 } |
|
458 } |
|
459 cout<<endl; |
|
460 return capabilities; |
|
461 } |
|
462 |
|
463 string CertificateChain ::ExtractASN1IntegerData(const ASN1_BIT_STRING* aString) |
|
464 { |
|
465 string sid; |
|
466 char buf[KByteLength]; |
|
467 unsigned char* buffer = aString->data; |
|
468 int dataLen = strlen((const char*)buffer); |
|
469 if(dataLen) |
|
470 { |
|
471 // check the type is "sequence". |
|
472 if (*(buffer++) != KDerSequenceTag) |
|
473 { |
|
474 throw ENotADerSequence; |
|
475 } |
|
476 |
|
477 // Find the length of the bitstring contained in this buffer |
|
478 unsigned long len = GetBitStringLength(&buffer); |
|
479 |
|
480 //each value(sid or vid) has length is 6,so divide total length by 6,to get the no of (sid or vid)values. |
|
481 int dataLen = len/(KByteLength-2); |
|
482 int data = 0; |
|
483 while (dataLen--) |
|
484 { |
|
485 //check whether it is a DER Integer tag |
|
486 if(*(buffer++) != KDerIntegerTag) |
|
487 { |
|
488 throw ENotADerInteger; |
|
489 } |
|
490 |
|
491 //skip the integer header |
|
492 buffer++; |
|
493 |
|
494 //read the 4 bytes containing the actual data(sid or vid) |
|
495 for (int j = (KByteLength-5); j >= 0; --j) |
|
496 { |
|
497 char c = *(buffer++); |
|
498 sprintf(buf,"%2.2x",c); |
|
499 sid.append(buf); |
|
500 } |
|
501 |
|
502 sid.append("\n"); |
|
503 } |
|
504 } |
|
505 return sid; |
|
506 } |
|
507 |
|
508 string CertificateChain::ExtractDeviceConstaints(const ASN1_BIT_STRING* aString) |
|
509 { |
|
510 string deviceId; |
|
511 unsigned char* buffer = aString->data; |
|
512 int dataLength = strlen((const char*)buffer); |
|
513 if(dataLength) |
|
514 { |
|
515 // check whether the type is "sequence". |
|
516 if (*(buffer++) != KDerSequenceTag) |
|
517 { |
|
518 throw ENotADerSequence; |
|
519 } |
|
520 |
|
521 // Find the length of the bitstring contained in this buffer |
|
522 unsigned long len = GetBitStringLength(&buffer); |
|
523 |
|
524 //each device id length is 22,so divide total length of the utf8 string by 22,to get the number of device id's. |
|
525 int noOfDeviceId = len/KDeviceIdLength; |
|
526 int data = 0; |
|
527 while (noOfDeviceId--) |
|
528 { |
|
529 if(*(buffer++) != KDerUtf8StringTag) |
|
530 { |
|
531 throw ENotADerUtf8String; |
|
532 } |
|
533 //skip the data length. |
|
534 buffer++; |
|
535 |
|
536 //read the 20 bytes containing the actual data(device id) |
|
537 char buf[KDeviceIdLength-2]; |
|
538 for (int j = 0; j < (KDeviceIdLength-2); j++) |
|
539 { |
|
540 buf[j] = *(buffer++); |
|
541 } |
|
542 |
|
543 deviceId.append(buf); |
|
544 deviceId.append("\n"); |
|
545 } |
|
546 } |
|
547 return deviceId; |
|
548 } |
|
549 |
|
550 unsigned long CertificateChain::GetBitStringLength(unsigned char** aASN1BitStringData) |
|
551 { |
|
552 unsigned long len = 0; |
|
553 if (**aASN1BitStringData & 0x80) |
|
554 { |
|
555 // long form of the length field |
|
556 int lenlen = *((*aASN1BitStringData)++) & 0x0F; // bottom 4 bits |
|
557 len = *((*aASN1BitStringData)++); |
|
558 while (--lenlen) |
|
559 { |
|
560 len = len << KByteLength; |
|
561 len += *((*aASN1BitStringData)++); |
|
562 } |
|
563 } |
|
564 else |
|
565 { |
|
566 // short form |
|
567 len = *((*aASN1BitStringData)++); |
|
568 } |
|
569 return len; |
|
570 } |
|
571 |
|
572 CertificateChain::~CertificateChain() |
|
573 { |
|
574 } |