--- /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);
+}