cryptoservices/asnpkcs/source/asnpkcs8.cpp
changeset 0 2c201484c85f
child 8 35751d3474b7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cryptoservices/asnpkcs/source/asnpkcs8.cpp	Wed Jul 08 11:25:26 2009 +0100
@@ -0,0 +1,506 @@
+/*
+* Copyright (c) 2004-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: 
+*
+*/
+
+
+#include <asn1dec.h>
+#include <x509keys.h>
+#include <x509cert.h>
+#include "asnpkcs.h"
+#include <pbe.h>
+#include <pbedata.h>
+#include <keyidentifierutil.h>
+
+/*static*/ EXPORT_C CDecPKCS8Data* TASN1DecPKCS8::DecodeDERL(const TDesC8& aBinaryData)
+{
+	return (CDecPKCS8Data::NewL(aBinaryData));
+}
+
+/*
+EncryptedPrivateKeyInfo ::= SEQUENCE {
+  encryptionAlgorithm EncryptionAlgorithmIdentifier,
+  encryptedData EncryptedData }
+EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+EncryptedData ::= OCTET STRING
+*/
+
+/*static*/ EXPORT_C CDecPKCS8Data* TASN1DecPKCS8::DecodeEncryptedDERL(const TDesC8& aBinaryData, const TDesC8& aPassword)
+{
+	TASN1DecGeneric seqGen(aBinaryData);
+	seqGen.InitL();
+	if (seqGen.Tag() != EASN1Sequence)
+		{
+		User::Leave(KErrArgument);
+		}
+	
+	TASN1DecSequence dec;
+	CArrayPtrFlat<TASN1DecGeneric>* theData = dec.DecodeDERLC(seqGen);
+	TInt seqIndex = 0;
+	TInt count = theData->Count();
+	if (seqIndex >= count)
+		{
+		User::Leave(KErrArgument);		
+		}
+
+//	Get the first part of the sequence -> PKCS5 data
+	TASN1DecGeneric* pkcs5 = theData->operator[](seqIndex);
+	TPtrC8 theContent(pkcs5->Encoding());	//	expect this to be a sequence
+	CPBEncryptParms* encryptParams = TASN1DecPKCS5::DecodeDERL(theContent);
+	CleanupStack::PushL(encryptParams);
+
+//	Create the decryptor	
+	CPBEncryptElement* encryptElement = CPBEncryptElement::NewLC(aPassword, *encryptParams);
+	CPBDecryptor* decryptor = encryptElement->NewDecryptLC();
+//	Decrypt the final part of the sequence -> encrypted PKCS8 object
+	TASN1DecGeneric* pkcs8 = theData->operator[](count-1);
+	if (pkcs8->Tag() != EASN1OctetString)
+		{
+		User::Leave(KErrArgument);
+		}
+	TPtrC8 encryptedKey(pkcs8->GetContentDER());
+	TUint encryptLength = encryptedKey.Length();
+	TUint maxDecryptLength = decryptor->MaxOutputLength(encryptLength);
+	if (maxDecryptLength<=0)
+		{
+		User::Leave(KErrGeneral);		
+		}
+	
+	HBufC8* decryptOutput = HBufC8::NewLC(encryptLength);
+	TPtr8 decryptOutputPtr(decryptOutput->Des());
+	decryptOutputPtr.FillZ();
+	decryptor->Process(encryptedKey, decryptOutputPtr);
+			
+	CDecPKCS8Data* pkcs8Data = CDecPKCS8Data::NewL(decryptOutputPtr);
+
+	//	decryptOutput decryptor encryptElement 
+	//	encryptParams theData
+	CleanupStack::PopAndDestroy(5, theData);
+	
+	return (pkcs8Data);
+}
+
+/*
+Sample cleartext pkcs8 data from pkcs8dsa.001:
+
+          SEQUENCE
+30          Tag: Constructed sequence
+81 c8       Length (may be one or more bytes)
+
+            INTEGER
+02            Tag: Integer
+01            Length: 1 byte
+00            Value: 0
+
+            SEQUENCE    
+30            Tag: Constructed sequence
+81 a9         Length (may be one or more bytes)
+
+              OID
+06              Tag: OID
+07              Length: 7 bytes
+2a 86 48 ce 38 04 01
+                Value: dsa (1 2 840 10040 4 1)
+*/
+
+_LIT8(KPKCS8DataVersion0, "\x02\x01\x00");
+_LIT8(KPKCS8DataOIDDSA, "\x06\x07\x2a\x86\x48\xce\x38\x04\x01");
+_LIT8(KPKCS8DataOIDRSA, "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01");
+
+/*static*/ EXPORT_C TBool TASN1DecPKCS8::IsPKCS8Data(const TDesC8& aBinaryData)
+	{
+	// We don't decode the data because we may only have the first few bytes -
+	// instead we check the ASN1 by hand.
+
+	ASSERT(aBinaryData.Length() >= KIsPKCS8DataMinLength);
+	TInt pos = 0;
+
+	return
+		IsASN1Sequence(aBinaryData, pos) &&
+		IsExpectedData(aBinaryData, pos, KPKCS8DataVersion0) &&
+		IsASN1Sequence(aBinaryData, pos) &&
+		(IsExpectedData(aBinaryData, pos, KPKCS8DataOIDDSA) ||
+		 IsExpectedData(aBinaryData, pos, KPKCS8DataOIDRSA));
+	}
+
+/*
+Sample encrypted pkcs8 data from encryptPK8rsa1.txt
+
+          SEQUENCE
+30          Tag: Constructed sequence
+82 01 a3    Length (may be one or more bytes)
+
+            SEQUENCE
+30            Tag: Constructed sequence
+3d            Length (may be one or more bytes)
+
+              OID
+06              Tag: OID
+09              Length: 9 bytes
+2a 86 48 86 f7 0d 01 05 0d
+                Value: pkcs5PBES2 (1 2 840 113549 1 5 13)
+
+              SEQUENCE
+30              Tag: Constructed sequence
+30              Length (may be one or more bytes)
+
+                SEQUENCE
+30                Tag: Constructed sequence
+1b                Length (may be one or more bytes)
+
+                  OID
+06                  Tag: OID
+09                  Length: 9 bytes
+2a 86 48 86 f7 0d 01 05 0c
+                    Value: pkcs5PBKDF2 (1 2 840 113549 1 5 12)
+*/
+
+_LIT8(KEncryptedPKCS8DataOIDpkcs5PBES2, "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x05\x0d");
+_LIT8(KEncryptedPKCS8DataOIDpkcs5PBKDF2, "\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x05\x0c");
+
+EXPORT_C TBool TASN1DecPKCS8::IsEncryptedPKCS8Data(const TDesC8& aBinaryData)
+	{
+	// We don't decode the data because we may only have the first few bytes -
+	// instead we check the ASN1 by hand.
+
+	ASSERT(aBinaryData.Length() >= KIsEncryptedPKCS8DataMinLength);
+	TInt pos = 0;
+
+	return
+		IsASN1Sequence(aBinaryData, pos) &&
+		IsASN1Sequence(aBinaryData, pos) &&
+		IsExpectedData(aBinaryData, pos, KEncryptedPKCS8DataOIDpkcs5PBES2) &&
+		IsASN1Sequence(aBinaryData, pos) &&
+		IsASN1Sequence(aBinaryData, pos) &&
+		IsExpectedData(aBinaryData, pos, KEncryptedPKCS8DataOIDpkcs5PBKDF2);
+	}
+
+/**
+ * Determine if the some data represents the start of an ASN1 sequence.  The
+ * data is specified by a descriptor and an offset.  If the data matches, the
+ * offset is advanced to point to the contents of the sequence.
+ */
+TBool TASN1DecPKCS8::IsASN1Sequence(const TDesC8& aBinaryData, TInt& aPos)
+	{
+	// Check we have enough data
+	if ((aPos + 2) >= aBinaryData.Length())
+		{
+		return EFalse;
+		}
+	// Check the outermost sequence is valid
+	if (aBinaryData[aPos++] != 0x30 /* constructed sequence */)
+		{
+		return EFalse;
+		}
+	// Skip sequence length
+	TInt length0 = aBinaryData[aPos++];
+	if (length0 & 0x80)
+		{
+		aPos += length0 & 0x7f;
+		}
+	return ETrue;
+	}
+
+/**
+ * Determine if some data starts with an expected string.  The data is specified
+ * by a descriptor and an offset.  If the data matches, the offset is advanced
+ * to point after the match.
+ */
+TBool TASN1DecPKCS8::IsExpectedData(const TDesC8& aBinaryData, TInt& aPos, const TDesC8& aExpectedData)
+	{
+	TInt length = aExpectedData.Length();
+	// Check we have enough data
+	if (aPos + length >= aBinaryData.Length())
+		{
+		return EFalse;
+		}
+	// Check data matches	
+	if (aBinaryData.Mid(aPos, length) != aExpectedData)
+		{
+		return EFalse;
+		}
+	aPos += length;
+	return ETrue;
+	}
+
+//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	
+//	PKCS#8 object data representation
+//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	
+
+/*static*/ CDecPKCS8Data* CDecPKCS8Data::NewL(const TDesC8& aData)
+{
+	CDecPKCS8Data* me = new (ELeave) CDecPKCS8Data();
+	CleanupStack::PushL(me);
+	me->ConstructL(aData);
+	CleanupStack::Pop(me);
+	return (me);
+}
+
+CDecPKCS8Data::CDecPKCS8Data()
+{}
+
+CDecPKCS8Data::~CDecPKCS8Data()
+{
+	if (iKeyPairData)
+	{
+		iKeyPairData->Release();
+		iKeyPairData = NULL;
+	}
+
+	if (iAttributes)
+	{
+		delete iAttributes;
+		iAttributes = NULL;
+	}
+}
+
+//	Because this is part of construction, don't rely on
+//	cleanup of partially constructed items...
+void CDecPKCS8Data::ConstructL(const TDesC8& aData)
+	{
+	TASN1DecGeneric seqGen(aData);
+	seqGen.InitL();
+	if (seqGen.Tag() != EASN1Sequence)
+		{
+		User::Leave(KErrArgument);
+		}
+
+	TASN1DecSequence seq;
+	CArrayPtrFlat<TASN1DecGeneric>* seqContents = seq.DecodeDERLC(seqGen);
+	if (seqContents->Count() < 3 || seqContents->Count() > 4)
+		{
+		User::Leave(KErrArgument);
+		}
+				
+//	VERSION
+	if (seqContents->At(0)->Tag() != EASN1Integer)
+		{
+		User::Leave(KErrArgument);
+		}
+	TASN1DecInteger intDecoder;
+	iVersion = intDecoder.DecodeDERShortL(*(seqContents->At(0)));
+	if (iVersion!=0)
+		{
+		User::Leave(KErrArgument);
+		}
+	
+//	ALGORITHM
+	CX509AlgorithmIdentifier* algorithm = CX509AlgorithmIdentifier::NewLC(seqContents->At(1)->Encoding());
+	iAlgorithmID = algorithm->Algorithm();
+		
+//	KEY DATA
+	switch (iAlgorithmID)
+		{//	Only support RSA, DSA, DH
+		case ERSA:
+			iKeyPairData = CPKCS8KeyPairRSA::NewL(*(seqContents->At(2)));
+			break;
+			
+		case EDSA:
+			{
+			TPtrC8 params(algorithm->EncodedParams());		
+			iKeyPairData = CPKCS8KeyPairDSA::NewL(params, *(seqContents->At(2)));
+			break;
+			}
+			
+		case EDH: // Currently not supported because no test data is available
+		default:
+			User::Leave(KErrNotSupported);
+			break;
+		}
+
+	CleanupStack::PopAndDestroy(algorithm);
+
+	if (seqContents->Count() == 4)
+		{
+		// I think we should check the tag is zero here, but we're going to be
+		// lenient due to lack of real test data
+		
+		//if (seqContents->At(3)->Tag() != 0) // Implicitly tagged
+		//	{
+		//	User::Leave(KErrArgument);
+		//	}
+		
+		iAttributes = seqContents->At(3)->Encoding().AllocL();
+		}
+
+	CleanupStack::PopAndDestroy(seqContents);
+	}
+
+//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	
+//	RSA decoding
+//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	
+
+/*static*/ MPKCS8DecodedKeyPairData* CPKCS8KeyPairRSA::NewL(const TASN1DecGeneric& aSource)
+{
+	CPKCS8KeyPairRSA* me = new (ELeave) CPKCS8KeyPairRSA();
+	CleanupStack::PushL(me);
+	me->ConstructL(aSource);
+	CleanupStack::Pop(me);
+
+	return (me);
+}
+
+MPKCS8DecodedKeyPairData::~MPKCS8DecodedKeyPairData()
+	{	
+	}
+
+CPKCS8KeyPairRSA::~CPKCS8KeyPairRSA()
+{
+	delete iPublicKey;
+	delete iPrivateKey;
+}
+
+void CPKCS8KeyPairRSA::Release()
+{
+	delete this;
+}
+
+void CPKCS8KeyPairRSA::GetKeyIdentifierL(TKeyIdentifier& aKeyIdentifier) const
+{
+	if (iPublicKey)
+		KeyIdentifierUtil::RSAKeyIdentifierL(*iPublicKey, aKeyIdentifier);	
+	else
+		User::Leave(KErrNotReady);
+}
+
+TUint CPKCS8KeyPairRSA::KeySize() const
+{
+	if (!iPublicKey)
+	{
+		ASSERT(EFalse);
+		return (0xffffffff);
+	}
+	
+	const TInteger& modulus = iPublicKey->N();
+
+	TUint size = modulus.BitCount();
+	return (size);
+}
+
+void CPKCS8KeyPairRSA::ConstructL(const TASN1DecGeneric& aSource)
+{
+	TPtrC8 theContent(aSource.GetContentDER());
+	TASN1DecRSAKeyPair keyPairDecoder;
+	TInt tempPos = 0;
+	keyPairDecoder.DecodeDERL(theContent, tempPos, iPublicKey, iPrivateKey);
+}
+
+
+//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	
+//	DSA decoding
+//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	//	\\	
+
+/*static*/ MPKCS8DecodedKeyPairData* CPKCS8KeyPairDSA::NewL(const TDesC8& aParamsData, const TASN1DecGeneric& aSource)
+{
+	CPKCS8KeyPairDSA* me = new (ELeave) CPKCS8KeyPairDSA();
+	CleanupStack::PushL(me);
+	me->ConstructL(aParamsData, aSource);
+	CleanupStack::Pop(me);
+
+	return (me);
+}
+
+CPKCS8KeyPairDSA::~CPKCS8KeyPairDSA()
+{
+	delete iPublicKey;
+	delete iPrivateKey;
+}
+
+void CPKCS8KeyPairDSA::Release()
+{
+	delete this;
+}
+
+void CPKCS8KeyPairDSA::GetKeyIdentifierL(TKeyIdentifier& aKeyIdentifier) const
+{
+	if (iPublicKey)
+		KeyIdentifierUtil::DSAKeyIdentifierL(*iPublicKey, aKeyIdentifier);	
+	else
+		User::Leave(KErrNotReady);
+}
+
+TUint CPKCS8KeyPairDSA::KeySize() const
+{
+	if (!iPublicKey)
+	{
+		ASSERT(EFalse);
+		return (0xffffffff);
+	}
+	
+	const TInteger& P = iPublicKey->P();
+
+	TUint size = P.BitCount();
+	return (size);
+}
+
+void CPKCS8KeyPairDSA::ConstructL(const TDesC8& aParamsData, const TASN1DecGeneric& aSource)
+{
+	TX509KeyFactory keyFactory;
+	CDSAParameters* params = keyFactory.DSAParametersL(aParamsData);
+	CleanupStack::PushL(params);
+
+	RInteger P = RInteger::NewL(params->P());
+	CleanupStack::PushL(P);
+
+	RInteger Q = RInteger::NewL(params->Q());
+	CleanupStack::PushL(Q);
+
+	RInteger G = RInteger::NewL(params->G());
+	CleanupStack::PushL(G);
+
+	if (aSource.Tag() != EASN1OctetString)
+		{
+		User::Leave(KErrArgument);
+		}
+	TASN1DecOctetString  octetDecoder;
+	HBufC8* wrapped = octetDecoder.DecodeDERL(aSource);
+	CleanupStack::PushL(wrapped);	
+
+	TASN1DecGeneric integer(*wrapped);
+	integer.InitL();
+	if (integer.Tag() != EASN1Integer)
+		{
+		User::Leave(KErrArgument);
+		}		
+	TASN1DecInteger decInt;
+	RInteger X = decInt.DecodeDERLongL(integer);
+	CleanupStack::PopAndDestroy(wrapped);
+	CleanupStack::PushL(X);
+			
+	RInteger P1 = RInteger::NewL(params->P());
+	CleanupStack::PushL(P1);
+	
+	RInteger Q1 = RInteger::NewL(params->Q());
+	CleanupStack::PushL(Q1);
+	
+	RInteger G1 = RInteger::NewL(params->G());
+	CleanupStack::PushL(G1);
+
+	RInteger Y = TInteger::ModularExponentiateL(G, X, P);
+	CleanupStack::PushL(Y);
+
+	iPublicKey = CDSAPublicKey::NewL(P1, Q1, G1, Y);
+
+	// Now have to pop everything owned by iPublicKey otherwise it will be
+	// deleted twice if the CDSAPrivateKey::NewL leaves
+
+	CleanupStack::Pop(4, &P1);	// now owned by iPublicKey
+	
+	iPrivateKey = CDSAPrivateKey::NewL(P, Q, G, X);
+
+	CleanupStack::Pop(4, &P);	// now owned by iPrivateKey
+		
+	CleanupStack::PopAndDestroy(params);
+}