pkiutilities/ocsp/test/resign/resign.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 15:20:08 +0200
changeset 0 164170e6151a
permissions -rw-r--r--
Revision: 201004

// Copyright (c) 2001-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
// App for resigning OCSP responses.
// 
//

#include <e32cons.h>
#include <f32file.h>
#include <asn1dec.h>
#include <asn1enc.h>
#include <x509cert.h>
#include <signed.h>
#include <bigint.h>
#include <asymmetric.h>
#include <hash.h>
#include <bacline.h>

#include "resign.h"

// CertTool command line parameters
_LIT(KResign, "-resign");
_LIT(KResignShort, "-r");

_LIT(KCreate, "-create");
_LIT(KCreateShort, "-c");

_LIT(KExtractResponse, "-e");
_LIT(KExtractResponseShort, "-extract");

CResign* CResign::NewLC()
	{
	CResign* resign = new(ELeave) CResign();
	CleanupStack::PushL(resign);
	resign->ConstructL();
	return resign;
	}
	
CResign* CResign::NewL()
	{
	CResign* resign = CResign::NewLC();
	CleanupStack::Pop(resign);
	return resign;
	}
		
CResign::CResign()
	{}
	
void CResign::ConstructL()
	{
	User::LeaveIfError(iFs.Connect());
	}

CResign::~CResign()
	{
	delete iArguments;
	iFs.Close();
	}

void CResign::ResignL(const TPtrC8& aSignedData,
			 TPtr8& aSignature,
			 RInteger& aModulus,
			 const RInteger& /*aPublicExponent*/,
			 RInteger& aPrivateExponent)
	{
	__UHEAP_MARK;
	// Build message digest
	CSHA1* hash = CSHA1::NewL();
	CleanupStack::PushL(hash);
	TPtrC8 digest = hash->Final(aSignedData);

	// Build ASN1 encoding of digestAlgId and digest..
	CASN1EncSequence* encAll = CASN1EncSequence::NewLC();

	// Build AlgID encoder (for SHA1)
	CASN1EncSequence* encAlgId = CASN1EncSequence::NewLC();

	CASN1EncObjectIdentifier* encObjId = CASN1EncObjectIdentifier::NewLC(KSHA1);
	encAlgId->AddChildL(encObjId);
	CleanupStack::Pop(); // encObjId, now owned by endAlgId

	CASN1EncNull* encNull = CASN1EncNull::NewLC();
	encAlgId->AddChildL(encNull);
	CleanupStack::Pop(); // encNull, now owned by endAlgId

	encAll->AddChildL(encAlgId);
	CleanupStack::Pop(); // endAlgId, now owned by encAll

	CASN1EncOctetString* encDigest = CASN1EncOctetString::NewLC(digest);
	encAll->AddChildL(encDigest);
	CleanupStack::Pop(); // encDigest, now owned by encAll

	HBufC8* digestInfo = HBufC8::NewMaxLC(encAll->LengthDER());
	TUint pos = 0;
	TPtr8 digestInfoPtr = digestInfo->Des();
	encAll->WriteDERL(digestInfoPtr, pos);

	__UHEAP_MARK;

	CRSAPrivateKeyStandard* rsaPriv = CRSAPrivateKeyStandard::NewLC(aModulus, aPrivateExponent);

	CRSAPKCS1v15Signer* signer = CRSAPKCS1v15Signer::NewLC(*rsaPriv);
	
	const CRSASignature* signature = signer->SignL(digestInfoPtr);
	
	HBufC8* theSignature = signature->S().BufferLC();
	aSignature.Copy(*theSignature);
	
	CleanupStack::PopAndDestroy(4, rsaPriv);

	__UHEAP_MARKEND;

	CleanupStack::PopAndDestroy(3, hash);
	
	__UHEAP_MARKEND;
	}

void CResign::DecodeDataL(TDes8& aResponse, const TDesC8& aKey)
	{
	__UHEAP_MARK;
	TASN1DecSequence seqDec;
	TInt pos = 0;

	// Get the signed data and signature portions of the response
	CArrayPtr<TASN1DecGeneric>* ocspResponseSeq = seqDec.DecodeDERLC(aResponse, pos, 2, 2);
	TPtrC8 responseBytes = ocspResponseSeq->At(1)->GetContentDER();
	CleanupStack::PopAndDestroy(ocspResponseSeq);

	pos = 0;
	CArrayPtr<TASN1DecGeneric>* responseBytesSeq = seqDec.DecodeDERLC(responseBytes, pos, 2, 2);
	TPtrC8 basicOCSPResponse = responseBytesSeq->At(1)->GetContentDER();
	CleanupStack::PopAndDestroy(responseBytesSeq);

	pos = 0;
	CArrayPtr<TASN1DecGeneric>* basicOCSPResponseSeq = seqDec.DecodeDERLC(basicOCSPResponse, pos, 3, 4);
	TPtrC8 responseData = basicOCSPResponseSeq->At(0)->Encoding();
	TPtrC8 signatureAlgorithm = basicOCSPResponseSeq->At(1)->Encoding();
	TPtrC8 signature = basicOCSPResponseSeq->At(2)->GetContentDER();
	CleanupStack::PopAndDestroy(basicOCSPResponseSeq);

	// Check that it's RSA with SHA1
	CX509SigningAlgorithmIdentifier* algId = CX509SigningAlgorithmIdentifier::NewLC(signatureAlgorithm);
	if (algId->DigestAlgorithm().Algorithm() != ESHA1
		|| algId->AsymmetricAlgorithm().Algorithm() != ERSA)
		{
		User::Leave(KErrNotSupported);
		}
	CleanupStack::PopAndDestroy(algId);

	// Get the modulus and private key portions of the key - only RSA supported

	// ASN1 .key files have 9 parts: version, modulus, public exponent, private exponent,
	// prime 1, prime2, exponent 1, exponent 2, coefficient
	// This is defined in PKCS1 (RFC 2313), the RSAPrivateKey ASN1 data structure
	pos = 0;
	CArrayPtr<TASN1DecGeneric>* keySeq = seqDec.DecodeDERLC(aKey, pos, 9, 9);
	TASN1DecInteger decInt;

	RInteger modulus = decInt.DecodeDERLongL(*keySeq->At(1));
	CleanupStack::PushL(modulus);

	RInteger publicExponent = decInt.DecodeDERLongL(*keySeq->At(2));
	CleanupStack::PushL(publicExponent);

	RInteger privateExponent = decInt.DecodeDERLongL(*keySeq->At(3));
	CleanupStack::PushL(privateExponent);

	// Cast constness off the signature - and skip the 'number of unused bits' octet
	TPtr8 sigEdit(CONST_CAST(TUint8*, signature.Ptr() + 1), signature.Length() - 1, signature.Length() - 1);

	// Pass data on for re-signing
	ResignL(responseData, sigEdit, modulus, publicExponent, privateExponent);

	CleanupStack::PopAndDestroy(3, &modulus); // The RIntegers
	CleanupStack::PopAndDestroy(keySeq);
	__UHEAP_MARKEND;
	}

void CResign::ProcessCommandLineL()
    {
	iArguments = CCommandLineArguments::NewL();
	
	if(iArguments->Count() > 1)
		{	
		TCommand command = ENone; 
		
		TPtrC args = iArguments->Arg(1);
		if ((args.Compare(KResign)==0) || (args.Compare(KResignShort)==0))
			{
			command = EResign;
			}	
		if ((args.Compare(KCreate)==0) || (args.Compare(KCreateShort)==0))
			{
			command = ECreate;
			}
		if ((args.Compare(KExtractResponse)==0) || (args.Compare(KExtractResponseShort)==0))
			{
			command = EExtract;
			}
		
		if(command != ENone)
			{
			HandleCommandL(command);
			}
		}
    }

void CResign::HandleCommandL(TCommand aCommand)
	{
	switch(aCommand)
		{
		case EResign:
			ResignFilesL();
			break;
			
		case ECreate:
			CreateDatFileL();
			break;
		
		case EExtract:
			ExtractResponseL();
			break;
		}
	}

void CResign::ResignFilesL()
	{
	__UHEAP_MARK;
	
	TPtrC responseFile(iArguments->Arg(2));
	TPtrC keyFile(iArguments->Arg(3));
	
	// Load the key
	RFile file;
	User::LeaveIfError(file.Open(iFs, keyFile, EFileRead | EFileShareAny));
	CleanupClosePushL(file);
	
	TInt keySize;
	User::LeaveIfError(file.Size(keySize));
	HBufC8* keyBuf = HBufC8::NewLC(keySize);
	TPtr8 key = keyBuf->Des();

	User::LeaveIfError(file.Read(key));
	
	// Open the logged response file

	RFileReadStream readStream;
	User::LeaveIfError(readStream.Open(iFs, responseFile, EFileRead | EFileShareAny));
	CleanupClosePushL(readStream);
	MStreamBuf* readBuf = readStream.Source();

	RFileWriteStream writeStream;
	User::LeaveIfError(writeStream.Open(iFs, responseFile, EFileWrite | EFileShareAny));
	CleanupClosePushL(writeStream);
	MStreamBuf* writeBuf = writeStream.Sink();

	TInt totalResponses = readStream.ReadUint32L();

	for (TInt i = 0 ; i < totalResponses ; ++i)
		{
		TInt responseSize = readStream.ReadUint32L();
		TStreamPos pos = readBuf->TellL(MStreamBuf::ERead);

		HBufC8* responseBuf = HBufC8::NewLC(responseSize);

		TPtr8 response = responseBuf->Des();
		readStream.ReadL(response);

		// resigning response
		DecodeDataL(response, key);

		writeBuf->SeekL(MStreamBuf::EWrite, pos);
		writeStream.WriteL(response);

		CleanupStack::PopAndDestroy(responseBuf);
		}
	
	CleanupStack::PopAndDestroy(4,&file); // close writeStream, readStream, keyBuf and file
	
	__UHEAP_MARKEND;
	}

void CResign::CreateDatFileL()
	{
	__UHEAP_MARK;
	
	
	// this path will fail if the directories are not already present.
	_LIT(KDatFile,"c:\\system\\tocsp\\responses\\response.dat");
	RFileWriteStream writeStream;
	User::LeaveIfError(writeStream.Replace(iFs, KDatFile, EFileWrite | EFileShareAny));
	CleanupClosePushL(writeStream);
	
	TInt fileCount = iArguments->Count(); 
	
	// total no of responses in this dat file
	writeStream.WriteInt32L(fileCount-2);
	RFile file;
	for(TInt i=2;i<fileCount;i++)
		{
		TPtrC responseFile(iArguments->Arg(i));
		// Load the response file
		User::LeaveIfError(file.Open(iFs, responseFile, EFileRead | EFileShareAny));
		CleanupClosePushL(file);
		
		TInt respSize = 0;
		User::LeaveIfError(file.Size(respSize));
		HBufC8* respBuf = HBufC8::NewLC(respSize);
		TPtr8 resp = respBuf->Des();
		User::LeaveIfError(file.Read(resp));
		
		writeStream.WriteInt32L(respSize);
		writeStream.WriteL(resp);
		
		CleanupStack::PopAndDestroy(2,&file); // respBuf and file
		}
	
	CleanupStack::PopAndDestroy(&writeStream);
	
	__UHEAP_MARKEND;
	}

void CResign::ExtractResponseL()
	{
	__UHEAP_MARK;
	
	TPtrC responseFile(iArguments->Arg(2));
	
	// Load the response file
	RFileReadStream respFile;
	User::LeaveIfError(respFile.Open(iFs, responseFile, EFileRead | EFileShareAny));
	CleanupClosePushL(respFile);
	
	// reading the transaction value.
	respFile.ReadInt32L();
	TInt respSize = respFile.ReadInt32L();
	
	HBufC8* respBuf = HBufC8::NewLC(respSize);
	TPtr8 resp = respBuf->Des();

	respFile.ReadL(resp, respSize);
	
	// this path will fail if the directories are not already present.
	_LIT(KDerFile,"c:\\system\\tocsp\\responses\\response.der");
	RFileWriteStream writeStream;
	User::LeaveIfError(writeStream.Replace(iFs, KDerFile, EFileWrite | EFileShareAny));
	CleanupClosePushL(writeStream);
	writeStream.WriteL(resp);
	
	CleanupStack::PopAndDestroy(3,&respFile); // writeStream, respBuf and respFile
	
	__UHEAP_MARKEND;

	}