/*
* Copyright (c) 2002-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:
* Implements PKCS#10 certificate request class.
*
*/
#include <pkcs10.h>
#include <asn1enc.h>
#include <e32std.h>
#include <e32def.h>
#include <x500dn.h>
#include <x509keys.h>
#include <hash.h>
#include <pkcs10attr.h>
#include "keyhelper.h"
#include <mctkeystoreuids.h>
void Panic(TInt aError)
{
_LIT(KCategory, "PKCS10");
User::Panic(KCategory, aError);
}
// CPKCS10Request Class Implementation
CPKCS10Request::CPKCS10Request(const CX500DistinguishedName* aDN,
const CCTKeyInfo* aKeyInfo,
CPKCS10Attributes* aAttr) :
CActive(EPriorityNormal),
iDN(aDN),
iKeyInfo(aKeyInfo),
iAttributes(aAttr),
iDigestId(ESHA1)
{
CActiveScheduler::Add(this);
}
EXPORT_C CPKCS10Request* CPKCS10Request::NewLC(const CX500DistinguishedName& aDN,
const CCTKeyInfo& aKeyInfo,
CPKCS10Attributes* aAttr/* = NULL*/)
{
// Sanity Check the input parameters
if (&aDN == NULL)
{
User::Leave(KErrArgument);
}
if (&aKeyInfo == NULL)
{
User::Leave(KErrArgument);
}
CPKCS10Request* self = new (ELeave) CPKCS10Request(&aDN, &aKeyInfo, aAttr);
CleanupStack::PushL(self);
return self;
}
EXPORT_C CPKCS10Request* CPKCS10Request::NewL(const CX500DistinguishedName& aDN,
const CCTKeyInfo& aKeyInfo,
CPKCS10Attributes* aAttr/* = NULL*/)
{
CPKCS10Request* self = NewLC(aDN, aKeyInfo, aAttr);
CleanupStack::Pop(self);
return self;
}
EXPORT_C CPKCS10Request::~CPKCS10Request()
{
Cancel();
delete iAttributes;
Reset();
}
void CPKCS10Request::Reset()
{
delete iExportedKey;
iExportedKey = NULL;
delete iTBSData;
iTBSData = NULL;
delete iKeyHelper;
iKeyHelper = NULL;
if (iKeyStore)
{
iKeyStore->Release();
iKeyStore = NULL;
}
iState = EIdle;
}
EXPORT_C void CPKCS10Request::SetDistinguishedNameL(const CX500DistinguishedName& aDN)
{
// Sanity check
if (&aDN == NULL)
{
User::Leave(KErrArgument);
}
iDN = &aDN;
}
EXPORT_C void CPKCS10Request::SetKeyInfoL(const CCTKeyInfo& aKeyInfo)
{
// Sanity check
if (&aKeyInfo == NULL)
{
User::Leave(KErrArgument);
}
iKeyInfo = &aKeyInfo;
}
EXPORT_C void CPKCS10Request::SetAttributes(CPKCS10Attributes* aAttr)
{
delete iAttributes;
iAttributes = aAttr;
}
EXPORT_C void CPKCS10Request::SetDigestAlgL(TAlgorithmId aDigestId)
{
if (aDigestId != EMD2 && aDigestId != EMD5 && aDigestId != ESHA1)
{
User::Leave(KErrArgument);
}
if (iKeyInfo->Algorithm() == CCTKeyInfo::EDSA && aDigestId != ESHA1)
{
User::Leave(KErrArgument);
}
iDigestId = aDigestId;
}
EXPORT_C void CPKCS10Request::CreateEncoding(HBufC8*& aResult, TRequestStatus& aStatus)
{
ASSERT(iState == EIdle);
iClientStatus = &aStatus;
iResult = &aResult;
aResult = NULL;
aStatus = KRequestPending;
iState = EInitialize;
SetActive();
TRequestStatus* status = &iStatus;
User::RequestComplete(status, KErrNone);
}
TInt CPKCS10Request::RunError(TInt aErr)
{
User::RequestComplete(iClientStatus, aErr);
iState = EIdle;
return KErrNone;
}
void CPKCS10Request::DoCancel()
{
switch (iState)
{
case EGetKeyStore:
iKeyInfo->Token().CancelGetInterface();
break;
case EGetPublicKey:
iKeyStore->CancelExportPublic();
break;
case EOpenSigner:
iKeyHelper->CancelOpenSigner();
break;
case ESign:
iKeyHelper->CancelSignDigest();
break;
default:
// do nothing, keep compiler happy
break;
}
if (iClientStatus)
User::RequestComplete(iClientStatus, KErrCancel);
iState = EIdle;
}
void CPKCS10Request::RunL()
{
User::LeaveIfError(iStatus.Int());
switch (iState)
{
case EInitialize:
// Get keystore interface
if (iKeyStore)
{
iKeyStore->Release();
iKeyStore = NULL;
}
iKeyInfo->Token().GetInterface(TUid::Uid(KInterfaceKeyStore),
*reinterpret_cast<MCTTokenInterface**>(&iKeyStore),
iStatus);
iState = EGetKeyStore;
SetActive();
break;
case EGetKeyStore:
// Fetch the public key
delete iExportedKey;
iKeyStore->ExportPublic(*iKeyInfo, iExportedKey, iStatus);
iState = EGetPublicKey;
SetActive();
break;
case EGetPublicKey:
// Create key helper object
delete iKeyHelper;
iKeyHelper = CPKCS10KeyHelper::CreateKeyHelperL(*iKeyStore, *iKeyInfo, *iExportedKey, iDigestId);
EncodeTBSDataL();
// Open signing object
iKeyHelper->OpenSigner(iStatus);
iState = EOpenSigner;
SetActive();
break;
case EOpenSigner:
// Create digest
{
CMessageDigest* digest = NULL;
switch (iDigestId)
{
case EMD2:
digest = CMD2::NewL();
break;
case EMD5:
digest = CMD5::NewL();
break;
case ESHA1:
digest = CSHA1::NewL();
break;
default:
User::Invariant();
}
CleanupStack::PushL(digest);
// Hash data and sign
digest->Update(*iTBSData);
iKeyHelper->SignDigestL(digest->Final(), iStatus);
CleanupStack::PopAndDestroy(digest); // keystore copies data to be signed
iState = ESign;
SetActive();
}
break;
case ESign:
CreateFinalEncodingL();
Reset();
break;
default:
User::Invariant();
}
}
CASN1EncBase* CPKCS10Request::MakeAttrEncLC()
{
if (iAttributes)
{
CASN1EncBase* result = iAttributes->TakeEncodingLC();
delete iAttributes;
iAttributes = NULL;
return result;
}
else
{
CASN1EncSequence* contextSpecific = CASN1EncSequence::NewLC();
contextSpecific->SetTag(0);
return contextSpecific;
}
}
CASN1EncSequence* CPKCS10Request::MakeCertRequestInfoEncLC()
{
// Top-level sequence contains distinguished name and other
// stuff. This is what gets signed with the entity's private key.
CASN1EncSequence* certRequestInfo = CASN1EncSequence::NewLC();
// Encode version number, which is 0.
CASN1EncInt* version = CASN1EncInt::NewLC(0);
certRequestInfo->AddAndPopChildL(version);
// Encode distinguished name.
CASN1EncBase* distinguishedName = iDN->EncodeASN1LC();
certRequestInfo->AddAndPopChildL(distinguishedName);
// Encode SubjectPublicKeyInfo.
CASN1EncBase* subjectPubKeyInfo = iKeyHelper->EncodeKeyLC();
certRequestInfo->AddAndPopChildL(subjectPubKeyInfo);
// Encode attributes, if any.
CASN1EncBase* attr = MakeAttrEncLC();
certRequestInfo->AddAndPopChildL(attr);
return certRequestInfo;
}
void CPKCS10Request::EncodeTBSDataL()
{
// The data we provide for signing is the certRequestInfo object.
CASN1EncBase* certRequestInfo = MakeCertRequestInfoEncLC();
// Write DER of it to the buffer.
delete iTBSData;
iTBSData = HBufC8::NewMaxL(certRequestInfo->LengthDER());
TPtr8 dataPtr = iTBSData->Des();
TUint pos = 0;
certRequestInfo->WriteDERL(dataPtr, pos);
CleanupStack::PopAndDestroy(certRequestInfo);
}
void CPKCS10Request::CreateFinalEncodingL()
{
// the root sequence contains all other components of a X509 signed object
CASN1EncSequence* root = CASN1EncSequence::NewLC();
// wrap data to be signed in a sequence and add it to the root
CASN1EncEncoding* encenc = CASN1EncEncoding::NewLC(*iTBSData);
root->AddAndPopChildL(encenc);
// encode signature algorithm and parameters and add them to the root
CASN1EncSequence* sigalg = iKeyHelper->EncodeSignatureAlgorithmLC();
root->AddAndPopChildL(sigalg);
// Create ASN.1 bit string from the signature
CASN1EncBitString* encSig = iKeyHelper->EncodeSignatureLC();
root->AddAndPopChildL(encSig);
// encode the object in a DER encoding
HBufC8* der = HBufC8::NewMaxLC(root->LengthDER());
TPtr8 pder(der->Des());
TUint pos = 0;
root->WriteDERL(pder, pos);
CleanupStack::Pop(der);
CleanupStack::PopAndDestroy(root);
*iResult = der;
User::RequestComplete(iClientStatus, KErrNone);
}