secureswitools/swianalysistoolkit/source/chainvalidityandinstallfilestatustools/common/certificatechain.cpp
changeset 0 ba25891c3a9e
child 25 7333d7932ef7
--- /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()
+	{
+	}