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