/*
Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Nokia Corporation nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Description:
*/
#include "createx509.h"
X509* CX509_Initializer::CreateX509(CX509Certificate* X509Cert)
{
X509* ret = X509_new();
TBool serail = ETrue;
if(ret != NULL)
{
//validity
X509_VAL_free(ret->cert_info->validity);
ret->cert_info->validity = CreateX509_VAL(X509Cert);
//issuer
const CX500DistinguishedName& IssName = X509Cert->IssuerName();
X509_NAME_free(ret->cert_info->issuer);
ret->cert_info->issuer = CreateX509_NAME(IssName);
//subject
const CX500DistinguishedName& SubName = X509Cert->SubjectName();
X509_NAME_free(ret->cert_info->subject);
ret->cert_info->subject = CreateX509_NAME(SubName);
// const HBufC * name = SubName.DisplayNameL();
//signature
const TPtrC8* sig_alg_ptr = X509Cert->DataElementEncoding(CX509Certificate::EAlgorithmId);
X509_ALGOR_free(ret->cert_info->signature);
ret->cert_info->signature = CreateX509_ALGOR(sig_alg_ptr);
//serialnumber
const TPtrC8 sernum = X509Cert->SerialNumber();
ASN1_INTEGER_free(ret->cert_info->serialNumber);
ret->cert_info->serialNumber = CreateASN1_STRING(sernum.Length(),V_ASN1_INTEGER,(unsigned char *)sernum.Ptr(),0);
if((sernum.Length()== 1) && sernum[0]==0)
serail = EFalse;
//version
TInt ver = X509Cert->Version();
unsigned char verVal = (unsigned char)(ver-1);
ASN1_INTEGER_free(ret->cert_info->version);
if( (verVal) || (!serail))// for X509 V1 certificates, version is null if any serial number present.
ret->cert_info->version = CreateASN1_STRING(1,V_ASN1_INTEGER,&verVal,0);
//issuerUID
const TPtrC8* issUID_enc = X509Cert->DataElementEncoding(CX509Certificate::EIssuerUID);
if(issUID_enc)
ret->cert_info->issuerUID = CreateASN1_STRING(issUID_enc->Length(),V_ASN1_BIT_STRING,(unsigned char *)issUID_enc->Ptr(),0);
//subjectUID
const TPtrC8* subUID_enc = X509Cert->DataElementEncoding(CX509Certificate::ESubjectUID);
if(subUID_enc)
ret->cert_info->subjectUID = CreateASN1_STRING(subUID_enc->Length(),V_ASN1_BIT_STRING,(unsigned char *)subUID_enc->Ptr(),0);
//key
X509_PUBKEY_free(ret->cert_info->key);
ret->cert_info->key = CreateX509_PUBKEY(X509Cert);
//extension
ret->cert_info->extensions = CreateSTACKOF_X509_EXTENSION(X509Cert);
//name
ret->name = X509_NAME_oneline(ret->cert_info->subject, NULL, 0);
//sig_alg
X509_ALGOR_free(ret->sig_alg);
ret->sig_alg = CreateX509_ALGOR(sig_alg_ptr);
//signature
const TPtrC8 sig = X509Cert->Signature();
ASN1_STRING_free(ret->signature);
ret->signature = CreateASN1_STRING(sig.Length(), V_ASN1_BIT_STRING, (unsigned char *)sig.Ptr(), ASN1_STRING_FLAG_BITS_LEFT);
}
return ret;
}
X509_ALGOR* CX509_Initializer::CreateX509_ALGOR(const TPtrC8* ptr)
{
X509_ALGOR* ret = X509_ALGOR_new();
TASN1DecGeneric dec((TDesC8 &)*ptr);
dec.InitL();
TASN1DecSequence encSeq;
CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC(dec);
TASN1DecGeneric& AlgorEncSeq = *(seq->At(0));
if (dec.LengthDERContent() > AlgorEncSeq.LengthDER()) // can also check for (seq->Count() > 1) alternatively
{
// parameter part is present in the encoding.
TASN1DecGeneric& ParameterEncSeq = *(seq->At(1));
// if param = 5, ie. ASN1 type NULL, then create a NULL ASN1 STRING
ret->parameter = ASN1_TYPE_new();
ret->parameter->type = (TInt)ParameterEncSeq.Encoding()[0];
if(ret->parameter->type != V_ASN1_NULL)
{
// we have some parameter
// add code to fill this stuff
}
}
else
{
//encoding does not contain parameter at all
//ret->parameter is anyway NULL when X509_ALGOR is created
//Not sure if we need to create a NULL ASN1 string or just leave parameter = NULL
}
const TDesC8& algor_data = AlgorEncSeq.GetContentDER();
char * ch_algor_data = (char *)algor_data.Ptr();
ret->algorithm = ASN1_OBJECT_new();
ret->algorithm->length = AlgorEncSeq.LengthDERContent();
ret->algorithm->data = (unsigned char *)OPENSSL_malloc(ret->algorithm->length);
if(ret->algorithm->data)
memcpy(ret->algorithm->data, ch_algor_data, ret->algorithm->length);
//else log error- cannot malloc
ret->algorithm->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA; // so that X509_ALGOR_free() frees all internally allocated data
CleanupStack::PopAndDestroy(); //seq
return ret;
}
X509_NAME* CX509_Initializer::CreateX509_NAME(const CX500DistinguishedName& DistName)
{
X509_NAME* ret = X509_NAME_new();
CASN1EncSequence * Asn1Seq = DistName.EncodeASN1LC();
HBufC8* octetData = HBufC8::NewMaxLC(5000);
TPtr8 oct(octetData->Des());
oct.FillZ();
oct.SetLength(KMaxNameLength);
TUint writePos = 0;
Asn1Seq->WriteDERL(oct, writePos);
TInt len = Fill_X509_NAME_ENTRY(ret, octetData->Des());
char *p = (char *)oct.PtrZ();
ret->bytes->data = (char *)OPENSSL_malloc(len); // no need to free this. BUF_MEM_free will free if not NULL
if(ret->bytes->data)
memcpy(ret->bytes->data, p, len);
//else log error- cannot malloc
ret->bytes->length = len;
ret->bytes->max = len;
ret->hash = 0; // for now filling zero. Not sure.
CleanupStack::PopAndDestroy(2); // Asn1Seq, octetData
return ret;
}
// return the length of the encoded sequence
TInt CX509_Initializer::Fill_X509_NAME_ENTRY(X509_NAME * name, const TDesC8& aBinaryData)
{
TInt aPos = 0;
TASN1DecGeneric dec(aBinaryData.Right(aBinaryData.Length() - aPos));
dec.InitL();
TInt end = aPos + dec.LengthDER();
aPos += dec.LengthDERHeader();
if (dec.Tag() != EASN1Sequence)
{
User::Leave(KErrArgument);
}
while (aPos < end)
{
TASN1DecGeneric rdn(aBinaryData.Right(aBinaryData.Length() - aPos));
rdn.InitL();
if (rdn.Tag() != EASN1Set)
{
User::Leave(KErrArgument);
}
TInt rdnEnd = rdn.LengthDER();
TInt rdnPos = rdn.LengthDERHeader();//add on header
while (rdnPos < rdnEnd)
{
const TDesC8& TypeValEnc = rdn.Encoding();
TASN1DecGeneric dec(TypeValEnc.Right(TypeValEnc.Length() - rdnPos));
dec.InitL();
TInt tvend = rdnPos + dec.LengthDER();
rdnPos += dec.LengthDERHeader();
//first element must be the id
TASN1DecObjectIdentifier encOID;
//iType = encOID.DecodeDERL(TypeValEnc, rdnPos);
TASN1DecGeneric first(TypeValEnc.Right(TypeValEnc.Length() - rdnPos));
first.InitL();
rdnPos += first.LengthDER();
if (first.Tag() != EASN1ObjectIdentifier)
{
User::Leave(KErrArgument);
}
const TDesC8& type = first.GetContentDER();
char * ch_type = (char *)type.Ptr();
//second is the data
TASN1DecGeneric second(TypeValEnc.Right(TypeValEnc.Length() - rdnPos));
second.InitL();
rdnPos += second.LengthDER();
const TDesC8& value = second.GetContentDER();
char * ch_value = (char *)value.Ptr();
X509_NAME_ENTRY* new_entry = CreateX509_NAME_ENTRY(ch_type,first.LengthDERContent(), ch_value, second.LengthDERContent(), second.Tag());
X509_NAME_add_entry(name, new_entry, -1, 0);
//we can free this, since add_entry makes a copy and adds
X509_NAME_ENTRY_free(new_entry);
//
if (rdnPos != tvend)
{
User::Leave(KErrArgument);
}
}
aPos += rdnEnd;
}
if (aPos != end)
{
User::Leave(KErrArgument);
}
return end;
}
X509_NAME_ENTRY * CX509_Initializer::CreateX509_NAME_ENTRY(char* type, int typeLen, char * value, int valueLen, int stringType)
{
X509_NAME_ENTRY * newEntry = X509_NAME_ENTRY_new();
newEntry->object->length = typeLen;
newEntry->object->data = (unsigned char *)OPENSSL_malloc(typeLen);
if(newEntry->object->data)
memcpy(newEntry->object->data, type, typeLen);
//else log error- cannot malloc
ASN1_STRING_free(newEntry->value);
newEntry->value = CreateASN1_STRING(valueLen, stringType, (unsigned char* )value, 0);
newEntry->object->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA; // so that X509_NAME_ENTRY_free() frees all internally allocated data
return newEntry;
}
X509_VAL * CX509_Initializer::CreateX509_VAL(CX509Certificate* X509Cert)
{
X509_VAL * ret = X509_VAL_new();
const CValidityPeriod& val = X509Cert->ValidityPeriod();
TBuf8<KCertMaxBuffer> numBuffer8;
char* numPtr = (char*)numBuffer8.PtrZ();
//start date (notBefore)
TDateTime dt = val.Start().DateTime();
numBuffer8.AppendNum(dt.Year());
if(numBuffer8.Length() > 2)
numBuffer8.Delete(0, numBuffer8.Length() - 2);
_LIT8(KCertTimeStampFormat, "%02d%02d%02d%02d%02dZ");
numBuffer8.AppendFormat(KCertTimeStampFormat,dt.Month()+1,dt.Day()+1,dt.Hour(),dt.Minute(),dt.Second()); //Month and Day - offset from zero, so add 1
numPtr[13]='\0';
ASN1_STRING_free(ret->notBefore);
ret->notBefore = CreateASN1_STRING(13,V_ASN1_UTCTIME,(unsigned char *)numPtr,0);
//finish date (notAfter)
dt = val.Finish().DateTime();
numBuffer8.Zero();
numBuffer8.AppendNum(dt.Year());
if(numBuffer8.Length() > 2)
numBuffer8.Delete(0, numBuffer8.Length() - 2);
numBuffer8.AppendFormat(KCertTimeStampFormat,dt.Month()+1,dt.Day()+1,dt.Hour(),dt.Minute(),dt.Second()); //Month and Day - offset from zero, so add 1
numPtr[13]='\0';
ASN1_STRING_free(ret->notAfter);
ret->notAfter = CreateASN1_STRING(13,V_ASN1_UTCTIME,(unsigned char *)numPtr,0);
return ret;
}
ASN1_STRING* CX509_Initializer::CreateASN1_STRING(int len, int type, unsigned char* data, long flags)
{
ASN1_STRING* ret = ASN1_STRING_new();
ret->length = len;
ret->type = type;
if(data!=NULL)
{
ret->data = (unsigned char *)OPENSSL_malloc(len);
if(ret->data)
memcpy(ret->data,data,len);
//else log error- cannot malloc
}
else
ret->data = NULL;
ret->flags |= flags;
return ret;
}
X509_PUBKEY* CX509_Initializer::CreateX509_PUBKEY(CX509Certificate* X509Cert)
{
X509_PUBKEY* ret = X509_PUBKEY_new();
//algor
const TPtrC8* ptr = X509Cert->DataElementEncoding(CX509Certificate::ESubjectPublicKeyInfo);
TInt aPos = 0;
TASN1DecGeneric dec(ptr->Right(ptr->Length() - aPos));
dec.InitL();
TInt end = aPos + dec.LengthDER();
aPos += dec.LengthDERHeader();
if (dec.Tag() != EASN1Sequence)
User::Leave(KErrArgument);
if (aPos < end)
{
TASN1DecGeneric rdn(ptr->Right(ptr->Length() - aPos));
rdn.InitL();
TPtrC8 newPtr = rdn.Encoding();
X509_ALGOR_free(ret->algor); // free the one allocated by X509_PUBKEY_new
ret->algor = CreateX509_ALGOR(&newPtr);
}
//public_key
const CSubjectPublicKeyInfo& pubkey = X509Cert->PublicKey();
const TPtrC8 keyDat = pubkey.KeyData();
ASN1_BIT_STRING_free(ret->public_key);
ret->public_key = CreateASN1_STRING(keyDat.Size(),V_ASN1_BIT_STRING,(unsigned char*)keyDat.Ptr(),0);
//pkey
ret->pkey = NULL; // we need not create this. Will be created later. Used to cache the computed data
return ret;
}
STACK_OF(X509_EXTENSION)* CX509_Initializer::CreateSTACKOF_X509_EXTENSION(CX509Certificate* X509Cert)
{
//STACK_OF(X509_EXTENSION) * ret = sk_X509_EXTENSION_new_null();
STACK_OF(X509_EXTENSION) * ret = NULL;
const TPtrC8* ptr = X509Cert->DataElementEncoding(CX509Certificate::EExtensionList);
const CArrayPtrFlat<CX509CertExtension>& extlist = X509Cert->Extensions();
TASN1DecSequence encSeq;
TInt pos = 0;
if(ptr != NULL) //There is an extension list
{
TASN1DecGeneric dec((TDesC8 &)*ptr);
ret = sk_X509_EXTENSION_new_null();
dec.InitL();
CArrayPtrFlat<TASN1DecGeneric>* seq = encSeq.DecodeDERLC( dec.GetContentDER(), pos);
TInt count = seq->Count(); // no of extensions in the ext list
for (TInt i = 0; i < count; i++)
{
X509_EXTENSION* ext = X509_EXTENSION_new();
ext->object = ASN1_OBJECT_new();
TASN1DecGeneric* gen = seq->At(i);
TASN1DecGeneric oid(seq->At(i)->GetContentDER());
oid.InitL();
const TDesC8& ext_obj_data = oid.GetContentDER();
char * ch_ext_obj_data = (char *)ext_obj_data.Ptr();
ext->object->length = oid.LengthDERContent();
ext->object->data = (unsigned char *)OPENSSL_malloc(ext->object->length);
if(ext->object->data)
memcpy(ext->object->data, ch_ext_obj_data, ext->object->length);
//else log error- cannot malloc
ext->object->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA; // so that X509_free() frees all internally allocated data
if(extlist.At(i)->Critical())
ext->critical = extlist.At(i)->Critical();
const TPtrC8 data = extlist.At(i)->Data();
TASN1DecGeneric value(data);
value.InitL();
const TDesC8& ext_value = value.GetContentDER();
char * ch_ext_value = (char *)ext_value.Ptr();
ASN1_STRING_free(ext->value);
ext->value = CreateASN1_STRING(value.LengthDERContent(), V_ASN1_OCTET_STRING, (unsigned char *)ch_ext_value, 0);
sk_X509_EXTENSION_push(ret,ext);
}
CleanupStack::PopAndDestroy();// seq
}
return ret;
}