--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/secureswitools/swianalysistoolkit/source/chainvalidityandinstallfilestatustools/common/certificatechain.cpp Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,574 @@
+/*
+* Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+
+#include "certificatechain.h"
+using namespace std;
+
+CertificateChain::CertificateChain()
+ :iDevCertificate (false) ,
+ iDevCapabilities (0) ,
+ iValidationStatus (EValidationSuccessful)
+ {
+ }
+
+bool CertificateChain::ValidateChain(const Options& aOptions , const string& aFile , const SWICertStore& aStore )
+ {
+ X509* endEntity = NULL ;
+ X509* previous = NULL ;
+ X509* current = NULL;
+ ASN1_UTCTIME* from = NULL ;
+ ASN1_UTCTIME* to = NULL;
+ char buf[KLength];
+ int count = 0;
+ int verify = 0;
+ string issuer ;
+ bool isValid = false ;
+ const char* fileName = aFile.c_str();
+
+ FILE *fp = fopen(fileName,"r");
+ if(fp == NULL)
+ {
+ throw EUnableToOpenFile ;
+ }
+ do
+ {
+ current = PEM_read_X509 (fp, NULL, NULL, NULL);
+ //end entity will hold the first certificate in the chain which signs the sis file.
+ if(count == 0 && current)
+ {
+ endEntity = current;
+ }
+
+ if(current)
+ {
+ X509_NAME_oneline (X509_get_issuer_name (current) , buf , KLength);
+ iCertificateChain.push_back(buf);
+ X509_NAME_oneline (X509_get_subject_name (current), buf , KLength);
+ iCertificateChain.push_back(buf);
+
+ from = X509_get_notBefore (current);
+ to = X509_get_notAfter (current);
+ ExtractCertValidityPeriod(*from , *to);
+
+ //validity period status
+ int validFrom = X509_cmp_current_time(from);
+ int validTo = X509_cmp_current_time(to);
+
+ //check whether "Valid From" is less than the current time and
+ //"Valid To" is greater than current time for each certificate in the chain.
+ //If check fails,current time is not within certificate's validity period.
+ if(validFrom >= 0 || validTo <= 0)
+ {
+ iValidationStatus = iValidationStatus | EValidityCheckFailure ;
+ }
+
+ //count is greater than one when there is more than 1 certificate in the chain
+ //so that the public key of the next certificate is used to verify the signature of
+ //previous certificate in the chain.
+ if(count)
+ {
+ EVP_PKEY* pubKey = X509_get_pubkey(current);
+ if (!pubKey)
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << "no public key in certificate" << endl;
+#endif
+ fclose(fp);
+ return false;
+ }
+
+ //Signature validation.
+ if(count == 1)
+ {
+ verify = X509_verify(endEntity,pubKey);
+ }
+
+ else
+ {
+ verify = X509_verify(previous,pubKey);
+ }
+
+ if(!verify)
+ {
+ iValidationStatus = iValidationStatus | EBrokenChain ;
+ }
+
+ if(previous)
+ {
+ X509_free (previous);
+ }
+
+ previous = current;
+ }
+
+ count++;
+ }
+ }while(current);
+
+ fclose(fp);
+ CheckForChainValidity(iValidationStatus);
+
+ //check whether end entity is devcert,self signed or others.
+ iDevCertificate = CheckIfDevCert(*endEntity);
+ if(iDevCertificate)
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << "SIS File is signed with a Developer Certificate with Constraints :\n" << endl;
+ cout << iDevConstraints << endl;
+#endif
+ }
+
+ // end entity certificate is self signed
+ else if(CheckIfSelfSigned(*endEntity))
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << ": SIS File is signed by a Self Signed Certificate " << endl;
+#endif
+ }
+
+ //check whether the last certificate in the chain is validated against the certstore.
+ //if there is only 1 certificate in the chain .
+ if(count == 1)
+ {
+ isValid = ValidateWithCertstore(*endEntity , aStore , issuer);
+ }
+ else
+ {
+ isValid = ValidateWithCertstore(*previous , aStore , issuer);
+ }
+
+ if(previous)
+ {
+ X509_free (previous);
+ previous = NULL ;
+ }
+
+ if(endEntity)
+ {
+ X509_free (endEntity);
+ endEntity = NULL ;
+ }
+
+ if(isValid)
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << "Is Validated By : " << issuer << endl << endl;
+#endif
+ return true;
+ }
+
+ return false;
+ }
+
+void CertificateChain::ExtractCertValidityPeriod(const ASN1_UTCTIME& aFrom , const ASN1_UTCTIME& aTo)
+ {
+ char date [12] = "xx/xx/xx";
+ date [0] = aFrom . data [4];
+ date [1] = aFrom . data [5];
+ date [3] = aFrom . data [2];
+ date [4] = aFrom . data [3];
+ date [6] = aFrom . data [0];
+ date [7] = aFrom . data [1];
+ iCertificateChain.push_back(date);
+
+ date [0] = aTo . data [4];
+ date [1] = aTo . data [5];
+ date [3] = aTo . data [2];
+ date [4] = aTo . data [3];
+ date [6] = aTo . data [0];
+ date [7] = aTo . data [1];
+ iCertificateChain.push_back(date);
+ }
+
+void CertificateChain::DisplaySigningChain(const StringVector& aSigningChain)
+ {
+ if(aSigningChain.size())
+ {
+ std::vector<std::string>::const_iterator chainIter = aSigningChain.begin();
+ do
+ {
+ cout << "Issued By : " << (*chainIter) << endl;
+ ++chainIter;
+ cout << "Issued To : " << (*chainIter) << endl;
+ ++chainIter;
+ cout << "Valid From : " << (*chainIter) << endl;
+ ++chainIter;
+ cout << "Valid To : " << (*chainIter) << endl << endl;
+ ++chainIter;
+ }while(chainIter != aSigningChain.end());
+ }
+ }
+
+bool CertificateChain::CheckIfDevCert(X509& aEndEntity)
+ {
+ X509_EXTENSION* extension = NULL;
+ ASN1_OCTET_STRING* extData = NULL;
+ ASN1_OBJECT * asnObj = NULL;
+ int constraintCount = X509_get_ext_count(&aEndEntity);
+ for(int k = 0 ; k < constraintCount ; k++)
+ {
+ extension = X509_get_ext(&aEndEntity, k);
+ extData = X509_EXTENSION_get_data(extension);
+
+ asnObj = X509_EXTENSION_get_object(extension);
+
+ char buf[KLength];
+ i2t_ASN1_OBJECT(buf, KLength, asnObj);
+
+ //all the constraints in the vector are compared against the predefined ones.
+ if(KDeviceIdListConstraint.compare(buf) == 0)
+ {
+ iDevConstraints.append("DeviceId Constraints :\n\n");
+ string deviceId = ExtractDeviceConstaints(extData);
+ //only if device id constraint is present in devcert,append value to iDevConstraints
+ if(deviceId.length())
+ {
+ iDevConstraints.append(deviceId);
+ }
+ else
+ {
+ iDevConstraints.append("Empty Device Constraints List. \n");
+ }
+ }
+
+ else if(KSidListConstraint.compare(buf) == 0)
+ {
+ iDevConstraints.append("\nSID Constraints :\n\n");
+ string sid = ExtractASN1IntegerData(extData);
+ if(sid.length())
+ {
+ iDevConstraints.append(sid);
+ }
+ else
+ {
+ iDevConstraints.append("Empty SID Constraints List. \n");
+ }
+ }
+
+ else if(KVidListConstraint.compare(buf) == 0)
+ {
+ iDevConstraints.append("\nVID Constraints :\n\n");
+ string vid = ExtractASN1IntegerData(extData);
+ if(vid.length())
+ {
+ iDevConstraints.append(vid);
+ }
+ else
+ {
+ iDevConstraints.append("Empty VID Constraints List.\n");
+ }
+ }
+
+ else if(KCapabilitiesConstraint.compare(buf) == 0)
+ {
+ iDevConstraints.append("\nCapabilities Constraints :\n\n");
+ string capabilities = ExtractCapabilities(extData);
+ if(capabilities.length())
+ {
+ iDevConstraints.append(capabilities);
+ }
+ else
+ {
+ iDevConstraints.append("Empty Capabilitites Constraints List. \n");
+ }
+ }
+ }
+
+ //if there are constaints with oid matching the predefined ones,they are devcerts
+ if(iDevConstraints.length())
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+bool CertificateChain::CheckIfSelfSigned(X509& aEndEntity)
+ {
+ char buf[KLength] ;
+ string issuer ;
+ string subject ;
+ X509_NAME_oneline (X509_get_issuer_name (&aEndEntity), buf, KLength);
+ issuer = buf;
+ X509_NAME_oneline (X509_get_subject_name (&aEndEntity), buf, KLength);
+ subject = buf;
+
+ if((issuer.compare(subject)) == 0)
+ {
+ EVP_PKEY* pubKey = X509_get_pubkey(&aEndEntity);
+ if (!pubKey)
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << "no public key in certificate" << endl;
+#endif
+ return false;
+ }
+
+ int verify = X509_verify(&aEndEntity, pubKey);
+ if(verify)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+bool CertificateChain::ValidateWithCertstore(X509& aPrevious, const SWICertStore& aStore , string& aIssuer)
+ {
+ int verifyWithRoot = 0;
+ const vector <X509*>& certStoreVector = aStore.GetRootCertVector();
+ if(certStoreVector.size())
+ {
+ char buf[KLength];
+ for(std::vector<X509*>::const_iterator root = certStoreVector.begin(); root != certStoreVector.end(); ++root)
+ {
+ EVP_PKEY* pubkey = X509_get_pubkey(*root);
+ if (!pubkey)
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << "no public key in certificate" << endl;
+#endif
+ return false;
+ }
+
+ verifyWithRoot = X509_verify(&aPrevious, pubkey);
+ if(verifyWithRoot)
+ {
+ X509_NAME_oneline (X509_get_issuer_name (*root),buf, KLength);
+ char* issued = strstr (buf, "/CN=");
+ if (issued)
+ {
+ issued += 4;
+ char* end = strchr (issued, '/');
+ if (end)
+ {
+ *end = 0;
+ }
+ aIssuer=issued;
+ string rootSignature((const char*)(aPrevious.signature->data) , (aPrevious.signature->length));
+ iValidatedRootSignatures = rootSignature;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+void CertificateChain::CheckForChainValidity(const int& aValidationStatus)
+ {
+#ifdef DUMPCHAINVALIDITYTOOL
+ cout << "Certificate Chain :" << endl << endl;
+
+ switch(aValidationStatus)
+ {
+ case 0:
+ {
+ DisplaySigningChain(iCertificateChain);
+ break;
+ }
+
+ case 1:
+ {
+ DisplaySigningChain(iCertificateChain);
+ cout << "Invalid Certificate Chain : Validity Period Check Failed" << endl;
+ break;
+ }
+
+ case 2:
+ {
+ DisplaySigningChain(iCertificateChain);
+ cout << "Broken Chain : Signature Verification Failed" << endl;
+ break;
+ }
+
+ case 3:
+ {
+ DisplaySigningChain(iCertificateChain);
+ cout << "Signature Verification and Validity Period Check Failed " << endl;
+ break;
+ }
+ default:
+ {
+ cout << "Error" << endl;
+ break;
+ }
+ }
+#endif
+ }
+
+string CertificateChain ::ExtractCapabilities(const ASN1_BIT_STRING* aString)
+ {
+ string capabilities;
+ unsigned char* buf = aString->data;
+ int dataLen = strlen((const char*)buf);
+ if(dataLen)
+ {
+ // check the type is "bitstring",where 3 represents bitstring.
+ if (*(buf++) != KDerBitStringTag)
+ {
+ throw ENotADerBitString;
+ }
+
+ // Find the length of the bitstring contained in this buffer
+ unsigned long len = GetBitStringLength(&buf);
+
+ // skip the padding length field
+ buf++;
+
+ int capability = 0;
+ while (len--)
+ {
+ char c = *(buf++);
+ for (int j = KByteLength-1 ; j >= 0; --j)
+ {
+ if (c & (1 << j))
+ {
+ if (capability < KNumberOfCaps)
+ {
+ capabilities.append(CapabilityList[capability]);
+ capabilities.append("\n");
+ }
+ else
+ {
+ capabilities.append("Unknown"+capability);
+ }
+
+ iDevCapabilities += pow(2,capability);
+ }
+ capability++;
+ }
+ }
+ }
+ cout<<endl;
+ return capabilities;
+ }
+
+string CertificateChain ::ExtractASN1IntegerData(const ASN1_BIT_STRING* aString)
+ {
+ string sid;
+ char buf[KByteLength];
+ unsigned char* buffer = aString->data;
+ int dataLen = strlen((const char*)buffer);
+ if(dataLen)
+ {
+ // check the type is "sequence".
+ if (*(buffer++) != KDerSequenceTag)
+ {
+ throw ENotADerSequence;
+ }
+
+ // Find the length of the bitstring contained in this buffer
+ unsigned long len = GetBitStringLength(&buffer);
+
+ //each value(sid or vid) has length is 6,so divide total length by 6,to get the no of (sid or vid)values.
+ int dataLen = len/(KByteLength-2);
+ int data = 0;
+ while (dataLen--)
+ {
+ //check whether it is a DER Integer tag
+ if(*(buffer++) != KDerIntegerTag)
+ {
+ throw ENotADerInteger;
+ }
+
+ //skip the integer header
+ buffer++;
+
+ //read the 4 bytes containing the actual data(sid or vid)
+ for (int j = (KByteLength-5); j >= 0; --j)
+ {
+ char c = *(buffer++);
+ sprintf(buf,"%2.2x",c);
+ sid.append(buf);
+ }
+
+ sid.append("\n");
+ }
+ }
+ return sid;
+ }
+
+string CertificateChain::ExtractDeviceConstaints(const ASN1_BIT_STRING* aString)
+ {
+ string deviceId;
+ unsigned char* buffer = aString->data;
+ int dataLength = strlen((const char*)buffer);
+ if(dataLength)
+ {
+ // check whether the type is "sequence".
+ if (*(buffer++) != KDerSequenceTag)
+ {
+ throw ENotADerSequence;
+ }
+
+ // Find the length of the bitstring contained in this buffer
+ unsigned long len = GetBitStringLength(&buffer);
+
+ //each device id length is 22,so divide total length of the utf8 string by 22,to get the number of device id's.
+ int noOfDeviceId = len/KDeviceIdLength;
+ int data = 0;
+ while (noOfDeviceId--)
+ {
+ if(*(buffer++) != KDerUtf8StringTag)
+ {
+ throw ENotADerUtf8String;
+ }
+ //skip the data length.
+ buffer++;
+
+ //read the 20 bytes containing the actual data(device id)
+ char buf[KDeviceIdLength-2];
+ for (int j = 0; j < (KDeviceIdLength-2); j++)
+ {
+ buf[j] = *(buffer++);
+ }
+
+ deviceId.append(buf);
+ deviceId.append("\n");
+ }
+ }
+ return deviceId;
+ }
+
+unsigned long CertificateChain::GetBitStringLength(unsigned char** aASN1BitStringData)
+ {
+ unsigned long len = 0;
+ if (**aASN1BitStringData & 0x80)
+ {
+ // long form of the length field
+ int lenlen = *((*aASN1BitStringData)++) & 0x0F; // bottom 4 bits
+ len = *((*aASN1BitStringData)++);
+ while (--lenlen)
+ {
+ len = len << KByteLength;
+ len += *((*aASN1BitStringData)++);
+ }
+ }
+ else
+ {
+ // short form
+ len = *((*aASN1BitStringData)++);
+ }
+ return len;
+ }
+
+CertificateChain::~CertificateChain()
+ {
+ }