os/security/securityanddataprivacytools/securitytools/certapp/encdec/x509utils.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/security/securityanddataprivacytools/securitytools/certapp/encdec/x509utils.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,481 @@
     1.4 +/*
     1.5 +* Copyright (c) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
     1.6 +* All rights reserved.
     1.7 +* This component and the accompanying materials are made available
     1.8 +* under the terms of the License "Eclipse Public License v1.0"
     1.9 +* which accompanies this distribution, and is available
    1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.11 +*
    1.12 +* Initial Contributors:
    1.13 +* Nokia Corporation - initial contribution.
    1.14 +*
    1.15 +* Contributors:
    1.16 +*
    1.17 +* Description: 
    1.18 +*
    1.19 +*/
    1.20 +
    1.21 +
    1.22 +#include "openssl/x509.h"
    1.23 +#include "openssl/x509v3.h"
    1.24 +#include "openssl/pem.h"
    1.25 +#include "encdec.h"
    1.26 +#include "x509utils.h"
    1.27 +#include "logger.h"
    1.28 +//
    1.29 +// TKeyIdentifier
    1.30 +//
    1.31 +void EncodeHuman(REncodeWriteStream& aStream,const KeyIdentifierObject  &aKeyId)
    1.32 +{
    1.33 +	if(aKeyId.iAutoKey)
    1.34 +		{
    1.35 +		aStream.WriteCStr("auto");
    1.36 +		if(aKeyId.iHash.Length() == 0)
    1.37 +			{
    1.38 +			return; // Empty value so no point in including it in a comment...
    1.39 +			}
    1.40 +		if(!aStream.Verbose())
    1.41 +			{
    1.42 +			return; // auto, and not in verbose mode so do not write value in comment
    1.43 +			}
    1.44 +		
    1.45 +		aStream.WriteCStr(" # ");
    1.46 +		}
    1.47 +	aStream.WriteByte('\'');
    1.48 +	const TUint8 *ptr = aKeyId.iHash.Ptr();
    1.49 +	TInt len = aKeyId.iHash.Length();
    1.50 +	while(len--)
    1.51 +		{
    1.52 +		TUint8 byte = *ptr++;
    1.53 +		TUint8 buf[2];
    1.54 +
    1.55 +		TUint8 ch = ((byte & 0xf0) >> 4);
    1.56 +		ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
    1.57 +
    1.58 +		// Write MSB char of byte
    1.59 +		buf[0] = ch;
    1.60 +
    1.61 +		ch = (byte & 0x0f);
    1.62 +		ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
    1.63 +
    1.64 +		// Write LSB char of byte
    1.65 +		buf[1] = ch;
    1.66 +
    1.67 +		aStream.WriteBin(buf, sizeof(buf));
    1.68 +		if(len)
    1.69 +			{
    1.70 +			aStream.WriteByte(':');
    1.71 +			}
    1.72 +		
    1.73 +		}
    1.74 +	aStream.WriteByte('\'');
    1.75 +}
    1.76 +void DecodeHuman(RDecodeReadStream& aStream, KeyIdentifierObject &aKeyId)
    1.77 +{
    1.78 +	aStream.ReadNextToken();
    1.79 +	std::string tok = aStream.Token();
    1.80 +
    1.81 +	if(tok == "auto")
    1.82 +		{
    1.83 +		aKeyId.iAutoKey = true;
    1.84 +		aKeyId.iHash.SetLength(0);
    1.85 +		return;
    1.86 +		}
    1.87 +	aKeyId.iAutoKey = false;
    1.88 +
    1.89 +	if((tok[0] != '\'') || (tok[tok.size()-1] != '\'') || (tok.size() < 2))
    1.90 +		{
    1.91 +		dbg << Log::Indent() << "KeyIdentifier not enclosed in single quotes, or contains spaces - " << tok << Log::Endl();
    1.92 +		FatalError();
    1.93 +		}
    1.94 +	
    1.95 +	tok.erase(0,1);
    1.96 +	tok.erase(tok.size()-1,1);
    1.97 +
    1.98 +	if(tok.size() == 0)
    1.99 +		{
   1.100 +		aKeyId.iHash.SetLength(0);
   1.101 +		return;
   1.102 +		}
   1.103 +	
   1.104 +	if(TInt(tok.size()) != (aKeyId.iHash.MaxLength()*2) + (aKeyId.iHash.MaxLength()-1))
   1.105 +		{
   1.106 +		dbg << Log::Indent() << "WARNING: KeyIdentifier length not " << aKeyId.iHash.MaxLength()*2 << " hex digits" << Log::Endl();
   1.107 +		dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
   1.108 +		}
   1.109 +
   1.110 +	bool bad = false;
   1.111 +	TInt bytesRead = 0;
   1.112 +	const char *hexDigit = tok.data();
   1.113 +	TInt charsToRead = tok.size();
   1.114 +	TUint8 *dest = const_cast<TUint8 *>(aKeyId.iHash.Ptr());
   1.115 +	while(charsToRead)
   1.116 +		{
   1.117 +		// Read MSB char
   1.118 +		TUint8 byte = fromHex(*hexDigit++);
   1.119 +		byte <<= 4;
   1.120 +		--charsToRead;
   1.121 +				
   1.122 +		// Read LSB char
   1.123 +		if(charsToRead == 0)
   1.124 +			{
   1.125 +			bad = true;
   1.126 +			break;
   1.127 +			}
   1.128 +		byte |= fromHex(*hexDigit++);
   1.129 +		--charsToRead;
   1.130 +
   1.131 +		// Save decoded byte
   1.132 +		*dest++ = byte;
   1.133 +		++bytesRead;
   1.134 +
   1.135 +		if(charsToRead != 0)
   1.136 +			{
   1.137 +			// Consume : separator
   1.138 +			if(*hexDigit++ != ':')
   1.139 +				{
   1.140 +				bad = true;
   1.141 +				break;
   1.142 +				}
   1.143 +			--charsToRead;
   1.144 +			}
   1.145 +		}
   1.146 +
   1.147 +	if(bytesRead > aKeyId.iHash.MaxLength())
   1.148 +		{
   1.149 +		dbg << Log::Indent() << "Key Identifiier is too long" << Log::Endl();
   1.150 +		bad = true;
   1.151 +		}
   1.152 +	
   1.153 +
   1.154 +	if(bad)
   1.155 +		{
   1.156 +		dbg << Log::Indent() << "KeyIdentifier invalid - It should be a single quoted string containing a series of 0 or more 2 digit hex numbers separated by : chars." << Log::Endl();
   1.157 +		dbg << Log::Indent() << "This field should normally be set to auto or omitted" << Log::Endl();
   1.158 +		dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
   1.159 +		FatalError();
   1.160 +		}
   1.161 +
   1.162 +	aKeyId.iHash.SetLength(bytesRead);
   1.163 +
   1.164 +
   1.165 +	return;
   1.166 +}
   1.167 +
   1.168 +RWriteStream& operator<<(RWriteStream& aStream,const KeyIdentifierObject& aKeyId)
   1.169 +{
   1.170 +	aStream << aKeyId.iHash;
   1.171 +	return aStream;
   1.172 +}
   1.173 +
   1.174 +RReadStream& operator>>(RReadStream& aStream, KeyIdentifierObject& aKeyId)
   1.175 +{
   1.176 +	aKeyId.iAutoKey = false;
   1.177 +	aStream >> aKeyId.iHash;
   1.178 +	return aStream;
   1.179 +}
   1.180 +
   1.181 +// It is illegal to pass a "X **" ptr to a function taking a "const X
   1.182 +// **" argument. This is because the function could change the callers
   1.183 +// pointer to point at a const object which the caller might then
   1.184 +// accidentally write to!
   1.185 +//
   1.186 +// Unfortunately openssl 0.9.7* defines d2i_X509 to take an "unsigned
   1.187 +// char **" and 0.9.8 takes "const unsigned char **", so neither
   1.188 +// caller choice will compile for both....
   1.189 +
   1.190 +#if OPENSSL_VERSION_NUMBER >= 0x00908000L
   1.191 +#define D2I_CONST const
   1.192 +#else
   1.193 +#define D2I_CONST
   1.194 +#endif
   1.195 +
   1.196 +bool X509SubjectKeyId(EUseCertificateExtension aUseExtension, bool aUseRfc3280Algorithm,
   1.197 +					  bool aIsCa, const std::string &aCert, 
   1.198 +					  std::string &aSubject, TKeyIdentifier &aSubjectKeyId)
   1.199 +{
   1.200 +	bool done = false;
   1.201 +	prog << Log::Indent() << "X509SubjectKeyId - aUseExtension " << aUseExtension << " aUseRfc3280Algorithm " << aUseRfc3280Algorithm << " :-" << Log::Endl();
   1.202 +	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   1.203 +
   1.204 +	// decode DER certificate into X509 structure
   1.205 +	D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert.data();
   1.206 +	X509 *x509 = d2i_X509(NULL, &p, aCert.size());
   1.207 +	if(!x509 || ((const char *)p != aCert.data() + aCert.size()))
   1.208 +		{
   1.209 +		dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
   1.210 +		FatalError();
   1.211 +		}
   1.212 +
   1.213 +	// Return the Subject Name
   1.214 +	prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
   1.215 +	aSubject = std::string(x509->name);
   1.216 +	TUint32 ver = X509_get_version(x509);
   1.217 +	prog << Log::Indent() << "Cert version is '" << ver << "'" << Log::Endl();
   1.218 +	
   1.219 +	// if the ver is a v1 or v2 type then there is no way of knowing which is a CA, treat all certs as CA as done in the certificate recognizer.
   1.220 +	bool treatAsCa = false;  
   1.221 +	if ( ver < 3 || aIsCa )
   1.222 +		{
   1.223 +		treatAsCa = true;
   1.224 +		}
   1.225 +	
   1.226 +	if(treatAsCa && aUseExtension)
   1.227 +		{
   1.228 +		// Attempt to read Subject Key Id extension
   1.229 +		ASN1_OCTET_STRING *subKeyId = (ASN1_OCTET_STRING *) X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL);
   1.230 +		if(subKeyId)
   1.231 +			{
   1.232 +			prog << Log::Indent() << "Found SubjectKeyId extension" << Log::Endl();
   1.233 +			if(subKeyId->length <= aSubjectKeyId.MaxLength())
   1.234 +				{
   1.235 +				aSubjectKeyId = TPtrC8(subKeyId->data, subKeyId->length);
   1.236 +				done = true;
   1.237 +				}
   1.238 +			else
   1.239 +				{
   1.240 +				prog << Log::Indent() << "but SubjectKeyId > 160 bits so ignoring it" << Log::Endl();
   1.241 +				}
   1.242 +			ASN1_OCTET_STRING_free(subKeyId);
   1.243 +			}
   1.244 +		}
   1.245 +	
   1.246 +	if(!done)
   1.247 +		{
   1.248 +		// Subject Key Id extension was ignored, missing or too long...
   1.249 +		if(aUseRfc3280Algorithm)
   1.250 +			{
   1.251 +			// We do not need to decode the public key just hash its
   1.252 +			// data as per rfc3280 4.2.1.2 method 1
   1.253 +			prog << Log::Indent() << "Calculating SubjectKeyId using RFC3280 4.2.1.2 method 1" << Log::Endl();
   1.254 +			unsigned char sha1hash[SHA_DIGEST_LENGTH];
   1.255 +			
   1.256 +			SHA1(x509->cert_info->key->public_key->data, x509->cert_info->key->public_key->length,
   1.257 +				 sha1hash);
   1.258 +			aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
   1.259 +			done = true;
   1.260 +			}
   1.261 +		else
   1.262 +			{
   1.263 +			// Calculate SubjectKeyId via Symbian algorithm
   1.264 +			prog << Log::Indent() << "Calculating SubjectKeyId using Symbian algorithm" << Log::Endl();
   1.265 +			EVP_PKEY *key = X509_PUBKEY_get(x509->cert_info->key);
   1.266 +			if(!key)
   1.267 +				{
   1.268 +				dbg << Log::Indent() << "openssl failed to decode certificate public key" << Log::Endl();
   1.269 +				FatalError();
   1.270 +				}
   1.271 +
   1.272 +			switch(key->type)
   1.273 +				{
   1.274 +				case EVP_PKEY_RSA:
   1.275 +					{
   1.276 +					TUint32 len = key->pkey.rsa->n->top*sizeof(BN_ULONG);
   1.277 +					TUint8 *buf = new TUint8[len];
   1.278 +					for(TUint32 i=0; i<len; ++i)
   1.279 +						{
   1.280 +						buf[i] = ((TUint8 *)key->pkey.rsa->n->d)[len-i-1];
   1.281 +						}
   1.282 +
   1.283 +					unsigned char sha1hash[SHA_DIGEST_LENGTH];
   1.284 +					SHA1(buf, len, sha1hash);
   1.285 +					delete [] buf;
   1.286 +					aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
   1.287 +					done = true;
   1.288 +					break;
   1.289 +					}
   1.290 +				case EVP_PKEY_DSA:
   1.291 +					{
   1.292 +					TUint32 len = key->pkey.dsa->pub_key->top*sizeof(BN_ULONG);
   1.293 +					TUint8 *buf = new TUint8[len];
   1.294 +					for(TUint32 i=0; i<len; ++i)
   1.295 +						{
   1.296 +						buf[i] = ((TUint8 *)key->pkey.dsa->pub_key->d)[len-i-1];
   1.297 +						}
   1.298 +
   1.299 +					unsigned char sha1hash[SHA_DIGEST_LENGTH];
   1.300 +					SHA1(buf, len, sha1hash);
   1.301 +					delete [] buf;
   1.302 +					aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
   1.303 +					done = true;
   1.304 +					break;
   1.305 +					}
   1.306 +				default:
   1.307 +					// Unknown public key type.
   1.308 +					prog << Log::Indent() << "Unknown public key type " << key->type << Log::Endl();
   1.309 +					break;
   1.310 +				}
   1.311 +			
   1.312 +			EVP_PKEY_free(key);
   1.313 +			}
   1.314 +		}
   1.315 +
   1.316 +	X509_free(x509);
   1.317 +	return done;
   1.318 +}
   1.319 +
   1.320 +
   1.321 +bool X509IssuerKeyId(EUseCertificateExtension aUseExtension,
   1.322 +					 const TUint8 *aCert, TUint32 aCertLength, 
   1.323 +					 std::string &aIssuer, TKeyIdentifier &aIssuerKeyId)
   1.324 +{	
   1.325 +	prog << Log::Indent() << "X509IssuerKeyId :-" << Log::Endl();
   1.326 +	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   1.327 +	bool done = false;
   1.328 +
   1.329 +	// decode DER certificate into X509 structure
   1.330 +	D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert;
   1.331 +	X509 *x509 = d2i_X509(NULL, &p, aCertLength);
   1.332 +	if(!x509 || (p != aCert+aCertLength))
   1.333 +		{
   1.334 +		dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
   1.335 +		FatalError();
   1.336 +		}
   1.337 +
   1.338 +	// Return the Subject Name
   1.339 +	prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
   1.340 +	char *issuerOne = X509_NAME_oneline(X509_get_issuer_name(x509),0,0);
   1.341 +	prog << Log::Indent() << "Cert issuer is '" << issuerOne << "'" << Log::Endl();
   1.342 +	aIssuer = issuerOne;
   1.343 +	OPENSSL_free(issuerOne);
   1.344 +
   1.345 +	if(aUseExtension)
   1.346 +		{
   1.347 +		// Attempt to read Subject Key Id extension
   1.348 +		AUTHORITY_KEYID *authKeyId = (AUTHORITY_KEYID *) X509_get_ext_d2i(x509, NID_authority_key_identifier, NULL, NULL);
   1.349 +		if(authKeyId)
   1.350 +			{
   1.351 +			prog << Log::Indent() << "Found AuthorityKeyId extension" << Log::Endl();
   1.352 +			if(authKeyId->keyid)
   1.353 +				{
   1.354 +				if(authKeyId->keyid->length <= aIssuerKeyId.MaxLength())
   1.355 +					{
   1.356 +					aIssuerKeyId = TPtrC8(authKeyId->keyid->data, authKeyId->keyid->length);
   1.357 +					done = true;
   1.358 +					}
   1.359 +				else
   1.360 +					{
   1.361 +					prog << Log::Indent() << "but AuthroityKeyId > 160 bits so ignoring it" << Log::Endl();
   1.362 +					}
   1.363 +				}
   1.364 +			else
   1.365 +				{
   1.366 +				prog << Log::Indent() << "but it does not include a key id, so ignoring it" << Log::Endl();
   1.367 +				}
   1.368 +			
   1.369 +			AUTHORITY_KEYID_free(authKeyId);
   1.370 +			}
   1.371 +		}
   1.372 +
   1.373 +	X509_free(x509);
   1.374 +	return done;
   1.375 +}
   1.376 +
   1.377 +void Der2Pem(const std::string &aDerCert, std::string &aPemCert)
   1.378 +{
   1.379 +	prog << Log::Indent() << "Converting DER to PEM:-" << Log::Endl();
   1.380 +	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   1.381 +
   1.382 +	// decode DER certificate into X509 structure
   1.383 +	D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aDerCert.data();
   1.384 +	X509 *x509 = d2i_X509(NULL, &p, aDerCert.size());
   1.385 +	if(!x509 || ((const char *)p != aDerCert.data()+aDerCert.size()))
   1.386 +		{
   1.387 +		dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
   1.388 +		FatalError();
   1.389 +		}
   1.390 +
   1.391 +	BIO *memBio = BIO_new(BIO_s_mem());
   1.392 +BULLSEYE_OFF
   1.393 +	if(!memBio)
   1.394 +		{
   1.395 +		dbg << Log::Indent() << "openssl failed to create BIO" << Log::Endl();
   1.396 +		FatalError();
   1.397 +		}
   1.398 +	
   1.399 +	if(!PEM_write_bio_X509(memBio, x509))
   1.400 +		{
   1.401 +		dbg << Log::Indent() << "openssl failed to convert to PEM" << Log::Endl();
   1.402 +		FatalError();
   1.403 +		}
   1.404 +BULLSEYE_RESTORE
   1.405 +	
   1.406 +	long pemCertLen = 0;
   1.407 +	char *pemCertData = 0;
   1.408 +	pemCertLen = BIO_get_mem_data(memBio, &pemCertData);
   1.409 +
   1.410 +	// Return the PEM cert
   1.411 +	aPemCert.assign(pemCertData, pemCertLen);
   1.412 +	
   1.413 +	BIO_free(memBio);
   1.414 +
   1.415 +	X509_free(x509);
   1.416 +
   1.417 +	prog << Log::Indent() << "Conversion ok" << Log::Endl();
   1.418 +	return;
   1.419 +}
   1.420 +
   1.421 +static const char utf8Header[] = 
   1.422 +	{
   1.423 +		0xef, 0xbb, 0xbf
   1.424 +	};
   1.425 +
   1.426 +bool Pem2Der(const std::string &aPemCert, std::string &aDerCert)
   1.427 +{
   1.428 +	prog << Log::Indent() << "Try PEM to DER coversion :-" << Log::Endl();
   1.429 +	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   1.430 +
   1.431 +	TUint32 pemLength=aPemCert.size();
   1.432 +	const char *pemData=aPemCert.data();
   1.433 +	
   1.434 +	if((pemLength >= 3) && (memcmp(aPemCert.data(), utf8Header, sizeof(utf8Header)) == 0))
   1.435 +		{
   1.436 +		// PEM cert has a UTF8 header, so strip it
   1.437 +		prog << Log::Indent() << "Certificate data file has a UTF-8 header" << Log::Endl();
   1.438 +		pemLength -= sizeof(utf8Header);
   1.439 +		pemData += sizeof(utf8Header);
   1.440 +		}
   1.441 +
   1.442 +	//
   1.443 +	// Read PEM to internal
   1.444 +	//
   1.445 +	BIO *memBioIn = BIO_new_mem_buf((void *)pemData, pemLength);
   1.446 +BULLSEYE_OFF
   1.447 +	if(!memBioIn)
   1.448 +		{
   1.449 +		dbg << Log::Indent() << "openssl failed to create BIO for reading PEM" << Log::Endl();
   1.450 +		FatalError();
   1.451 +		}
   1.452 +BULLSEYE_RESTORE
   1.453 +	
   1.454 +	X509 *x509 = PEM_read_bio_X509(memBioIn, NULL, 0, NULL);
   1.455 +	if(!x509)
   1.456 +		{
   1.457 +		prog << Log::Indent() << "Conversion failed - presumably DER" << Log::Endl();
   1.458 +		return false;
   1.459 +		}
   1.460 +	BIO_free(memBioIn);
   1.461 +    memBioIn = 0;
   1.462 +
   1.463 +	//
   1.464 +	// Write internal to DER
   1.465 +	//
   1.466 +	unsigned char *derCert = 0;
   1.467 +	int derLen = i2d_X509(x509, &derCert);
   1.468 +	if(derLen <=0 )
   1.469 +		{
   1.470 +		dbg << Log::Indent() << "openssl failed to convert to DER" << Log::Endl();
   1.471 +		FatalError();
   1.472 +		}
   1.473 +
   1.474 +	// Return the DER cert
   1.475 +	aDerCert.assign((char *)derCert, derLen);
   1.476 +
   1.477 +	X509_free(x509);
   1.478 +	prog << Log::Indent() << "Conversion ok" << Log::Endl();
   1.479 +	return true;
   1.480 +}
   1.481 +
   1.482 +
   1.483 +
   1.484 +// End of file