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