os/security/securityanddataprivacytools/securitytools/certapp/encdec/x509utils.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 /*
     2 * Copyright (c) 2008-2010 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 "openssl/x509.h"
    20 #include "openssl/x509v3.h"
    21 #include "openssl/pem.h"
    22 #include "encdec.h"
    23 #include "x509utils.h"
    24 #include "logger.h"
    25 //
    26 // TKeyIdentifier
    27 //
    28 void EncodeHuman(REncodeWriteStream& aStream,const KeyIdentifierObject  &aKeyId)
    29 {
    30 	if(aKeyId.iAutoKey)
    31 		{
    32 		aStream.WriteCStr("auto");
    33 		if(aKeyId.iHash.Length() == 0)
    34 			{
    35 			return; // Empty value so no point in including it in a comment...
    36 			}
    37 		if(!aStream.Verbose())
    38 			{
    39 			return; // auto, and not in verbose mode so do not write value in comment
    40 			}
    41 		
    42 		aStream.WriteCStr(" # ");
    43 		}
    44 	aStream.WriteByte('\'');
    45 	const TUint8 *ptr = aKeyId.iHash.Ptr();
    46 	TInt len = aKeyId.iHash.Length();
    47 	while(len--)
    48 		{
    49 		TUint8 byte = *ptr++;
    50 		TUint8 buf[2];
    51 
    52 		TUint8 ch = ((byte & 0xf0) >> 4);
    53 		ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
    54 
    55 		// Write MSB char of byte
    56 		buf[0] = ch;
    57 
    58 		ch = (byte & 0x0f);
    59 		ch = (ch<=9) ? (ch +'0') : (ch - 10 +'A');
    60 
    61 		// Write LSB char of byte
    62 		buf[1] = ch;
    63 
    64 		aStream.WriteBin(buf, sizeof(buf));
    65 		if(len)
    66 			{
    67 			aStream.WriteByte(':');
    68 			}
    69 		
    70 		}
    71 	aStream.WriteByte('\'');
    72 }
    73 void DecodeHuman(RDecodeReadStream& aStream, KeyIdentifierObject &aKeyId)
    74 {
    75 	aStream.ReadNextToken();
    76 	std::string tok = aStream.Token();
    77 
    78 	if(tok == "auto")
    79 		{
    80 		aKeyId.iAutoKey = true;
    81 		aKeyId.iHash.SetLength(0);
    82 		return;
    83 		}
    84 	aKeyId.iAutoKey = false;
    85 
    86 	if((tok[0] != '\'') || (tok[tok.size()-1] != '\'') || (tok.size() < 2))
    87 		{
    88 		dbg << Log::Indent() << "KeyIdentifier not enclosed in single quotes, or contains spaces - " << tok << Log::Endl();
    89 		FatalError();
    90 		}
    91 	
    92 	tok.erase(0,1);
    93 	tok.erase(tok.size()-1,1);
    94 
    95 	if(tok.size() == 0)
    96 		{
    97 		aKeyId.iHash.SetLength(0);
    98 		return;
    99 		}
   100 	
   101 	if(TInt(tok.size()) != (aKeyId.iHash.MaxLength()*2) + (aKeyId.iHash.MaxLength()-1))
   102 		{
   103 		dbg << Log::Indent() << "WARNING: KeyIdentifier length not " << aKeyId.iHash.MaxLength()*2 << " hex digits" << Log::Endl();
   104 		dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
   105 		}
   106 
   107 	bool bad = false;
   108 	TInt bytesRead = 0;
   109 	const char *hexDigit = tok.data();
   110 	TInt charsToRead = tok.size();
   111 	TUint8 *dest = const_cast<TUint8 *>(aKeyId.iHash.Ptr());
   112 	while(charsToRead)
   113 		{
   114 		// Read MSB char
   115 		TUint8 byte = fromHex(*hexDigit++);
   116 		byte <<= 4;
   117 		--charsToRead;
   118 				
   119 		// Read LSB char
   120 		if(charsToRead == 0)
   121 			{
   122 			bad = true;
   123 			break;
   124 			}
   125 		byte |= fromHex(*hexDigit++);
   126 		--charsToRead;
   127 
   128 		// Save decoded byte
   129 		*dest++ = byte;
   130 		++bytesRead;
   131 
   132 		if(charsToRead != 0)
   133 			{
   134 			// Consume : separator
   135 			if(*hexDigit++ != ':')
   136 				{
   137 				bad = true;
   138 				break;
   139 				}
   140 			--charsToRead;
   141 			}
   142 		}
   143 
   144 	if(bytesRead > aKeyId.iHash.MaxLength())
   145 		{
   146 		dbg << Log::Indent() << "Key Identifiier is too long" << Log::Endl();
   147 		bad = true;
   148 		}
   149 	
   150 
   151 	if(bad)
   152 		{
   153 		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();
   154 		dbg << Log::Indent() << "This field should normally be set to auto or omitted" << Log::Endl();
   155 		dbg << Log::Indent() << "KeyIdentifier is '" << tok << "'" << Log::Endl();
   156 		FatalError();
   157 		}
   158 
   159 	aKeyId.iHash.SetLength(bytesRead);
   160 
   161 
   162 	return;
   163 }
   164 
   165 RWriteStream& operator<<(RWriteStream& aStream,const KeyIdentifierObject& aKeyId)
   166 {
   167 	aStream << aKeyId.iHash;
   168 	return aStream;
   169 }
   170 
   171 RReadStream& operator>>(RReadStream& aStream, KeyIdentifierObject& aKeyId)
   172 {
   173 	aKeyId.iAutoKey = false;
   174 	aStream >> aKeyId.iHash;
   175 	return aStream;
   176 }
   177 
   178 // It is illegal to pass a "X **" ptr to a function taking a "const X
   179 // **" argument. This is because the function could change the callers
   180 // pointer to point at a const object which the caller might then
   181 // accidentally write to!
   182 //
   183 // Unfortunately openssl 0.9.7* defines d2i_X509 to take an "unsigned
   184 // char **" and 0.9.8 takes "const unsigned char **", so neither
   185 // caller choice will compile for both....
   186 
   187 #if OPENSSL_VERSION_NUMBER >= 0x00908000L
   188 #define D2I_CONST const
   189 #else
   190 #define D2I_CONST
   191 #endif
   192 
   193 bool X509SubjectKeyId(EUseCertificateExtension aUseExtension, bool aUseRfc3280Algorithm,
   194 					  bool aIsCa, const std::string &aCert, 
   195 					  std::string &aSubject, TKeyIdentifier &aSubjectKeyId)
   196 {
   197 	bool done = false;
   198 	prog << Log::Indent() << "X509SubjectKeyId - aUseExtension " << aUseExtension << " aUseRfc3280Algorithm " << aUseRfc3280Algorithm << " :-" << Log::Endl();
   199 	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   200 
   201 	// decode DER certificate into X509 structure
   202 	D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert.data();
   203 	X509 *x509 = d2i_X509(NULL, &p, aCert.size());
   204 	if(!x509 || ((const char *)p != aCert.data() + aCert.size()))
   205 		{
   206 		dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
   207 		FatalError();
   208 		}
   209 
   210 	// Return the Subject Name
   211 	prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
   212 	aSubject = std::string(x509->name);
   213 	TUint32 ver = X509_get_version(x509);
   214 	prog << Log::Indent() << "Cert version is '" << ver << "'" << Log::Endl();
   215 	
   216 	// 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.
   217 	bool treatAsCa = false;  
   218 	if ( ver < 3 || aIsCa )
   219 		{
   220 		treatAsCa = true;
   221 		}
   222 	
   223 	if(treatAsCa && aUseExtension)
   224 		{
   225 		// Attempt to read Subject Key Id extension
   226 		ASN1_OCTET_STRING *subKeyId = (ASN1_OCTET_STRING *) X509_get_ext_d2i(x509, NID_subject_key_identifier, NULL, NULL);
   227 		if(subKeyId)
   228 			{
   229 			prog << Log::Indent() << "Found SubjectKeyId extension" << Log::Endl();
   230 			if(subKeyId->length <= aSubjectKeyId.MaxLength())
   231 				{
   232 				aSubjectKeyId = TPtrC8(subKeyId->data, subKeyId->length);
   233 				done = true;
   234 				}
   235 			else
   236 				{
   237 				prog << Log::Indent() << "but SubjectKeyId > 160 bits so ignoring it" << Log::Endl();
   238 				}
   239 			ASN1_OCTET_STRING_free(subKeyId);
   240 			}
   241 		}
   242 	
   243 	if(!done)
   244 		{
   245 		// Subject Key Id extension was ignored, missing or too long...
   246 		if(aUseRfc3280Algorithm)
   247 			{
   248 			// We do not need to decode the public key just hash its
   249 			// data as per rfc3280 4.2.1.2 method 1
   250 			prog << Log::Indent() << "Calculating SubjectKeyId using RFC3280 4.2.1.2 method 1" << Log::Endl();
   251 			unsigned char sha1hash[SHA_DIGEST_LENGTH];
   252 			
   253 			SHA1(x509->cert_info->key->public_key->data, x509->cert_info->key->public_key->length,
   254 				 sha1hash);
   255 			aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
   256 			done = true;
   257 			}
   258 		else
   259 			{
   260 			// Calculate SubjectKeyId via Symbian algorithm
   261 			prog << Log::Indent() << "Calculating SubjectKeyId using Symbian algorithm" << Log::Endl();
   262 			EVP_PKEY *key = X509_PUBKEY_get(x509->cert_info->key);
   263 			if(!key)
   264 				{
   265 				dbg << Log::Indent() << "openssl failed to decode certificate public key" << Log::Endl();
   266 				FatalError();
   267 				}
   268 
   269 			switch(key->type)
   270 				{
   271 				case EVP_PKEY_RSA:
   272 					{
   273 					TUint32 len = key->pkey.rsa->n->top*sizeof(BN_ULONG);
   274 					TUint8 *buf = new TUint8[len];
   275 					for(TUint32 i=0; i<len; ++i)
   276 						{
   277 						buf[i] = ((TUint8 *)key->pkey.rsa->n->d)[len-i-1];
   278 						}
   279 
   280 					unsigned char sha1hash[SHA_DIGEST_LENGTH];
   281 					SHA1(buf, len, sha1hash);
   282 					delete [] buf;
   283 					aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
   284 					done = true;
   285 					break;
   286 					}
   287 				case EVP_PKEY_DSA:
   288 					{
   289 					TUint32 len = key->pkey.dsa->pub_key->top*sizeof(BN_ULONG);
   290 					TUint8 *buf = new TUint8[len];
   291 					for(TUint32 i=0; i<len; ++i)
   292 						{
   293 						buf[i] = ((TUint8 *)key->pkey.dsa->pub_key->d)[len-i-1];
   294 						}
   295 
   296 					unsigned char sha1hash[SHA_DIGEST_LENGTH];
   297 					SHA1(buf, len, sha1hash);
   298 					delete [] buf;
   299 					aSubjectKeyId = TPtrC8(sha1hash, SHA_DIGEST_LENGTH);
   300 					done = true;
   301 					break;
   302 					}
   303 				default:
   304 					// Unknown public key type.
   305 					prog << Log::Indent() << "Unknown public key type " << key->type << Log::Endl();
   306 					break;
   307 				}
   308 			
   309 			EVP_PKEY_free(key);
   310 			}
   311 		}
   312 
   313 	X509_free(x509);
   314 	return done;
   315 }
   316 
   317 
   318 bool X509IssuerKeyId(EUseCertificateExtension aUseExtension,
   319 					 const TUint8 *aCert, TUint32 aCertLength, 
   320 					 std::string &aIssuer, TKeyIdentifier &aIssuerKeyId)
   321 {	
   322 	prog << Log::Indent() << "X509IssuerKeyId :-" << Log::Endl();
   323 	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   324 	bool done = false;
   325 
   326 	// decode DER certificate into X509 structure
   327 	D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aCert;
   328 	X509 *x509 = d2i_X509(NULL, &p, aCertLength);
   329 	if(!x509 || (p != aCert+aCertLength))
   330 		{
   331 		dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
   332 		FatalError();
   333 		}
   334 
   335 	// Return the Subject Name
   336 	prog << Log::Indent() << "Cert subject is '" << x509->name << "'" << Log::Endl();
   337 	char *issuerOne = X509_NAME_oneline(X509_get_issuer_name(x509),0,0);
   338 	prog << Log::Indent() << "Cert issuer is '" << issuerOne << "'" << Log::Endl();
   339 	aIssuer = issuerOne;
   340 	OPENSSL_free(issuerOne);
   341 
   342 	if(aUseExtension)
   343 		{
   344 		// Attempt to read Subject Key Id extension
   345 		AUTHORITY_KEYID *authKeyId = (AUTHORITY_KEYID *) X509_get_ext_d2i(x509, NID_authority_key_identifier, NULL, NULL);
   346 		if(authKeyId)
   347 			{
   348 			prog << Log::Indent() << "Found AuthorityKeyId extension" << Log::Endl();
   349 			if(authKeyId->keyid)
   350 				{
   351 				if(authKeyId->keyid->length <= aIssuerKeyId.MaxLength())
   352 					{
   353 					aIssuerKeyId = TPtrC8(authKeyId->keyid->data, authKeyId->keyid->length);
   354 					done = true;
   355 					}
   356 				else
   357 					{
   358 					prog << Log::Indent() << "but AuthroityKeyId > 160 bits so ignoring it" << Log::Endl();
   359 					}
   360 				}
   361 			else
   362 				{
   363 				prog << Log::Indent() << "but it does not include a key id, so ignoring it" << Log::Endl();
   364 				}
   365 			
   366 			AUTHORITY_KEYID_free(authKeyId);
   367 			}
   368 		}
   369 
   370 	X509_free(x509);
   371 	return done;
   372 }
   373 
   374 void Der2Pem(const std::string &aDerCert, std::string &aPemCert)
   375 {
   376 	prog << Log::Indent() << "Converting DER to PEM:-" << Log::Endl();
   377 	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   378 
   379 	// decode DER certificate into X509 structure
   380 	D2I_CONST unsigned char *p = (D2I_CONST unsigned char *)aDerCert.data();
   381 	X509 *x509 = d2i_X509(NULL, &p, aDerCert.size());
   382 	if(!x509 || ((const char *)p != aDerCert.data()+aDerCert.size()))
   383 		{
   384 		dbg << Log::Indent() << "openssl failed to decode certificate" << Log::Endl();
   385 		FatalError();
   386 		}
   387 
   388 	BIO *memBio = BIO_new(BIO_s_mem());
   389 BULLSEYE_OFF
   390 	if(!memBio)
   391 		{
   392 		dbg << Log::Indent() << "openssl failed to create BIO" << Log::Endl();
   393 		FatalError();
   394 		}
   395 	
   396 	if(!PEM_write_bio_X509(memBio, x509))
   397 		{
   398 		dbg << Log::Indent() << "openssl failed to convert to PEM" << Log::Endl();
   399 		FatalError();
   400 		}
   401 BULLSEYE_RESTORE
   402 	
   403 	long pemCertLen = 0;
   404 	char *pemCertData = 0;
   405 	pemCertLen = BIO_get_mem_data(memBio, &pemCertData);
   406 
   407 	// Return the PEM cert
   408 	aPemCert.assign(pemCertData, pemCertLen);
   409 	
   410 	BIO_free(memBio);
   411 
   412 	X509_free(x509);
   413 
   414 	prog << Log::Indent() << "Conversion ok" << Log::Endl();
   415 	return;
   416 }
   417 
   418 static const char utf8Header[] = 
   419 	{
   420 		0xef, 0xbb, 0xbf
   421 	};
   422 
   423 bool Pem2Der(const std::string &aPemCert, std::string &aDerCert)
   424 {
   425 	prog << Log::Indent() << "Try PEM to DER coversion :-" << Log::Endl();
   426 	AutoIndent ai(prog); // IncIndent, will DecIndent when it leaves scope
   427 
   428 	TUint32 pemLength=aPemCert.size();
   429 	const char *pemData=aPemCert.data();
   430 	
   431 	if((pemLength >= 3) && (memcmp(aPemCert.data(), utf8Header, sizeof(utf8Header)) == 0))
   432 		{
   433 		// PEM cert has a UTF8 header, so strip it
   434 		prog << Log::Indent() << "Certificate data file has a UTF-8 header" << Log::Endl();
   435 		pemLength -= sizeof(utf8Header);
   436 		pemData += sizeof(utf8Header);
   437 		}
   438 
   439 	//
   440 	// Read PEM to internal
   441 	//
   442 	BIO *memBioIn = BIO_new_mem_buf((void *)pemData, pemLength);
   443 BULLSEYE_OFF
   444 	if(!memBioIn)
   445 		{
   446 		dbg << Log::Indent() << "openssl failed to create BIO for reading PEM" << Log::Endl();
   447 		FatalError();
   448 		}
   449 BULLSEYE_RESTORE
   450 	
   451 	X509 *x509 = PEM_read_bio_X509(memBioIn, NULL, 0, NULL);
   452 	if(!x509)
   453 		{
   454 		prog << Log::Indent() << "Conversion failed - presumably DER" << Log::Endl();
   455 		return false;
   456 		}
   457 	BIO_free(memBioIn);
   458     memBioIn = 0;
   459 
   460 	//
   461 	// Write internal to DER
   462 	//
   463 	unsigned char *derCert = 0;
   464 	int derLen = i2d_X509(x509, &derCert);
   465 	if(derLen <=0 )
   466 		{
   467 		dbg << Log::Indent() << "openssl failed to convert to DER" << Log::Endl();
   468 		FatalError();
   469 		}
   470 
   471 	// Return the DER cert
   472 	aDerCert.assign((char *)derCert, derLen);
   473 
   474 	X509_free(x509);
   475 	prog << Log::Indent() << "Conversion ok" << Log::Endl();
   476 	return true;
   477 }
   478 
   479 
   480 
   481 // End of file