diff -r 000000000000 -r 33413c0669b9 vpnengine/utlpkcs10/src/pkcs10.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/utlpkcs10/src/pkcs10.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,1375 @@ +/* +* Copyright (c) 2003 - 2007 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: Pkcs10 certificate request. +* +*/ + + + +#include +#include +#include +#include + +#include "pkcs10.h" +#include "base64.h" +#include "pkidefs.h" +#include "utlcrypto.h" + +////////////////////////////////////////////////////////////////////////////// +// Destructor +////////////////////////////////////////////////////////////////////////////// +EXPORT_C CPkcs10Req::~CPkcs10Req() +{ + delete iDNDerBuf; +} + +////////////////////////////////////////////////////////////////////////////// +// Set distinguished name parameter, Only CommonName given +////////////////////////////////////////////////////////////////////////////// +EXPORT_C TInt CPkcs10Req::SetDistinguishedName(const TDesC8 &aDistinguishedName) +{ + TInt status = KErrNone; + + // Set distiguished name + if(aDistinguishedName.Length() > 0) + { + iDistinguishedName.Set(aDistinguishedName); + iUseExtendedDistinguishedName = EFalse; + } + else + status = KErrArgument; + + return (status); +} + +////////////////////////////////////////////////////////////////////////////// +// SetDistinguishedNameExtended +////////////////////////////////////////////////////////////////////////////// +// Set distinguished name parameter. Complete distinguished name in format +// CN=xxx,O=abc,OU=def or CN=xxx/O=abc/OU=def +// OIds and corresponding texts (case insensitive): +// KX520CountryName "2.5.4.6" "c" {0x55, 0x04, 0x06} +// KX520OrganizationName "2.5.4.10" "o" {0x55, 0x04, 0x0a} +// KX520OrganizationalUnitName "2.5.4.11" "ou" {0x55, 0x04, 0x0b} +// KX520LocalityName "2.5.4.7" "l" {0x55, 0x04, 0x07} +// KX520StateOrProvinceName "2.5.4.8" "st" {0x55, 0x04, 0x08} +// KX520Title "2.5.4.12" "title" {0x55, 0x04, 0x0c} +// KX520CommonName "2.5.4.3" "cn" {0x55, 0x04, 0x03} +// KX520GivenName "2.5.4.42" "GivenName" {0x55, 0x04, 0x2a} +// KX520Surname "2.5.4.4" "sn" {0x55, 0x04, 0x04} +// KX520Initials "2.5.4.43" "initials" {0x55, 0x04, 0x2b} +// KX520GenerationQualifier "2.5.4.44" "generationQualifier" {0x55, 0x04, 0x2c} +// KX520DNQualifier "2.5.4.46" "dnQualifier" {0x55, 0x04, 0x2e} +// KX520SerialNumber "2.5.4.5" "serialNumber" {0x55, 0x04, 0x05} +//////////////////////////////////////////////////////////////////////////////////////// +EXPORT_C TInt CPkcs10Req::SetDistinguishedNameExtendedL(const TDesC8 &aDistinguishedName) +{ + TInt status = KErrNone; + + // Set distiguished name + if(aDistinguishedName.Length() > 0) + { + delete iDNDerBuf; // delete possible old buffer + iDNDerBuf = NULL; + TRAPD(status, BuildDistinguishedNameDerFromTextL(iDNDerBuf, aDistinguishedName, EFalse, KCountryName())); + if(status == KErrNone) + { + iUseExtendedDistinguishedName = ETrue; + } + } + else + status = KErrArgument; + + return (status); +} + +////////////////////////////////////////////////////////////////////////////// +// BuildDistinguishedNameDerFromText +////////////////////////////////////////////////////////////////////////////// +EXPORT_C void CPkcs10Req::BuildDistinguishedNameDerFromTextL( + HBufC8 *&derBuffer, + const TDesC8 &aDistinguishedName, + TBool aByPass, + const TDesC8 &aByPassType +) +{ + HBufC8 *shrinkedBuffer; + + CArrayFixFlat *valueIndexArray = + new (ELeave) CArrayFixFlat(1); + + CleanupStack::PushL(valueIndexArray); + + CArrayFixFlat *typeIndexArray = + new (ELeave) CArrayFixFlat(1); + + CleanupStack::PushL(typeIndexArray); + + CArrayFixFlat *valuePtrArray = + new (ELeave) CArrayFixFlat(1); + + CleanupStack::PushL(valuePtrArray); + + CArrayFixFlat *typePtrArray = + new (ELeave) CArrayFixFlat(1); + + CleanupStack::PushL(typePtrArray); + + // Scan input and build index arrays for types and values + CPkcs10Req::BuildIndexPairsLC( + shrinkedBuffer, + aDistinguishedName, + typeIndexArray, + valueIndexArray + ); + + CPkcs10Req::BuildDerL( + shrinkedBuffer, + derBuffer, + typeIndexArray, + valueIndexArray, + typePtrArray, + valuePtrArray, + aByPass, + aByPassType + ); + + CleanupStack::PopAndDestroy(5); +} + +////////////////////////////////////////////////////////////////////////////// +// AttributeTypeOidToText +////////////////////////////////////////////////////////////////////////////// +EXPORT_C TPtrC8 CPkcs10Req::AttributeTypeOidToText(const TDesC &aType) +{ + TPtrC8 temp; + + if(aType.Compare(KX520CountryName) == 0) + temp.Set(KCountryName); + else if(aType.Compare(KX520OrganizationName) == 0) + temp.Set(KOrganizationName); + else if(aType.Compare(KX520OrganizationalUnitName) == 0) + temp.Set(KOrganizationalUnitName); + else if(aType.Compare(KX520LocalityName) == 0) + temp.Set(KLocalityName); + else if(aType.Compare(KX520StateOrProvinceName) == 0) + temp.Set(KStateOrProvinceName); + else if(aType.Compare(KX520Title) == 0) + temp.Set(KTitle); + else if(aType.Compare(KX520CommonName) == 0) + temp.Set(KCommonName); + else if(aType.Compare(KX520GivenName) == 0) + temp.Set(KGivenName); + else if(aType.Compare(KX520Surname) == 0) + temp.Set(KSurname); + else if(aType.Compare(KX520Initials) == 0) + temp.Set(KInitials); + else if(aType.Compare(KX520GenerationQualifier) == 0) + temp.Set(KGenerationQualifier); + else if(aType.Compare(KX520DNQualifier) == 0) + temp.Set(KDNQualifier); + else if(aType.Compare(KX520SerialNumber) == 0) + temp.Set(KSerialNumber); + else if(aType.Compare(KRFC2247DomainComponent) == 0) + temp.Set(KDomainComponent); + else + { + // Return dotted oid, length = 0 + } + + return temp; +} + +////////////////////////////////////////////////////////////////////////////// +// BuildIndexPairsLC +////////////////////////////////////////////////////////////////////////////// +void CPkcs10Req::BuildIndexPairsLC( + HBufC8 *&buffer, + const TDesC8 &aDistinguishedName, + CArrayFixFlat *aTypePairArray, + CArrayFixFlat *aValuePairArray +) +{ + // Some rules: + // 1) Separator: ',' + // 2) Escape character '\' + // 3) Value may be quoted. Then separator in value is allowed + // (without preceding escape char) + + HBufC8 *sourceBuffer; + HBufC8 *targetBuffer; + TUint i, j; + targetBuffer = HBufC8::NewLC(aDistinguishedName.Length()); + targetBuffer->Des().SetLength(aDistinguishedName.Length()); + sourceBuffer = HBufC8::NewLC(aDistinguishedName.Length()); + sourceBuffer->Des().Copy(aDistinguishedName); + TPtr8 sourcePointer = sourceBuffer->Des(); + TPtr8 targetPointer = targetBuffer->Des(); + TBool insideQuotes = EFalse; + TIndexPair pair; + TUint elementStartPosition = 0; + TUint elementEndPosition = 0; + + // Delete leading and trailing spaces + sourcePointer.Trim(); + + // Scan text in order to collect name value pairs + for(i = 0, j = 0; (TInt)i < sourcePointer.Length(); i++) + { + if(sourcePointer[i] == '"') + { + // Toggle quote state + // Don't ignore quotes in order to ease trimming. + // (Spaces inside quotes are meaningful) + insideQuotes = !insideQuotes; + targetPointer[j] = sourcePointer[i]; + j++; + continue; + } + + if(insideQuotes) + { + // Allow any character + // Check for escape + if(sourcePointer[i] == '\\') + { + if((TInt)i < sourcePointer.Length() - 1) + { + targetPointer[j] = sourcePointer[i + 1]; + j++; + i++; // skip char + } + else + User::Leave(KErrArgument); + } + else + { + targetPointer[j] = sourcePointer[i]; + j++; + } + continue; + } + + // Check for escape + if(sourcePointer[i] == '\\') + { + if((TInt)i < sourcePointer.Length() - 1) + { + targetPointer[j] = sourcePointer[i + 1]; + j++; + i++; // skip char + } + else + User::Leave(KErrArgument); + + continue; + } + + // Check for pair separator + if(sourcePointer[i] == ',') + { + // End of type/value pair + elementEndPosition = j; + pair.startIndex = elementStartPosition; + pair.endIndex = elementEndPosition; + aValuePairArray->AppendL(pair); + elementStartPosition = j; + } + // Check for type/value separator + else if(sourcePointer[i] == '=') + { + elementEndPosition = j; + pair.startIndex = elementStartPosition; + pair.endIndex = elementEndPosition; + aTypePairArray->AppendL(pair); + elementStartPosition = j; + } + else + { + // Plain copy + targetPointer[j] = sourcePointer[i]; + j++; + } + } + + // Append final value + elementEndPosition = j; + pair.startIndex = elementStartPosition; + pair.endIndex = elementEndPosition; + aValuePairArray->AppendL(pair); + + // Ensure we have consistent type/value pairs + if(aTypePairArray->Count() != aValuePairArray->Count()) + { + User::Leave(KErrArgument); + } + + CleanupStack::PopAndDestroy(1); // source + // Return allocated shrinked buffer and OK status + targetBuffer->Des().SetLength(j); + buffer = targetBuffer; +} + +////////////////////////////////////////////////////////////////////////////// +// BuildDerLC +////////////////////////////////////////////////////////////////////////////// +void CPkcs10Req::BuildDerL( + HBufC8 *aSource, + HBufC8 *&derBuffer, + CArrayFixFlat *aTypeIndexArray, + CArrayFixFlat *aValueIndexArray, + CArrayFixFlat *aTypePtrArray, + CArrayFixFlat *aValuePtrArray, + TBool aByPass, + const TDesC8 &aByPassType +) +{ + TUint i; + // trim types and values and count required der length + TUint elementLength = 0; + TUint totalLength = 0; + TUint wholeLength = 0; + TUint setLength = 0; + TUint seqLength = 0; + TUint oidLength = COMMON_NAME_OID_LTH; + TUint stringLength = 0; + TUint contentLength = 0; + TUint oidIndex = 0; + TBuf8<32> dottedOidDer; + + CArrayFixFlat *elementArray = + new (ELeave) CArrayFixFlat(1); + + CleanupStack::PushL(elementArray); + + for(i = 0; (TInt)i < aTypeIndexArray->Count(); i++) // counts are equal + { + TPtr8 tempPtr( + CONST_CAST(TUint8*, aSource->Des().Ptr()) + aTypeIndexArray->At(i).startIndex, + aTypeIndexArray->At(i).endIndex - aTypeIndexArray->At(i).startIndex + ); + + tempPtr.SetLength( + aTypeIndexArray->At(i).endIndex - aTypeIndexArray->At(i).startIndex + ); + + aTypePtrArray->AppendL(tempPtr); + + tempPtr.Set( + CONST_CAST(TUint8*, aSource->Des().Ptr()) + aValueIndexArray->At(i).startIndex, + aValueIndexArray->At(i).endIndex - aValueIndexArray->At(i).startIndex, + aValueIndexArray->At(i).endIndex - aValueIndexArray->At(i).startIndex + ); + + tempPtr.SetLength(aValueIndexArray->At(i).endIndex - aValueIndexArray->At(i).startIndex); + + tempPtr.Trim(); + + if(0 == tempPtr.Length()) + { + User::Leave(KErrArgument); + } + + // Remove quotes. Spaces inside quotes are meaningful, + // so Trim must be called before this operation + if(tempPtr[0] == '"') + { + if(tempPtr[tempPtr.Length() -1 ] != '"') + { + User::Leave(KErrArgument); + } + else + { + TInt tempLength = tempPtr.Length() - 2; + tempPtr.Set(CONST_CAST(TUint8*, tempPtr.Ptr() + 1), tempLength, tempLength); + tempPtr.SetLength(tempLength); + } + } + + if(0 == tempPtr.Length()) + { + User::Leave(KErrArgument); + } + + aValuePtrArray->AppendL(tempPtr); + + aTypePtrArray->At(i).Trim(); + aTypePtrArray->At(i).UpperCase(); + + // Check if type is supported, it is ignored if not + if(CPkcs10Req::OidExistsL(aTypePtrArray->At(i), oidIndex, dottedOidDer)) + { + if(KNullAttributeIndex == oidIndex) + { + oidLength = dottedOidDer.Length() - ASN1_SHORT_TAG_HDR_LTH; + } + else if(KDomainComponentIndex == oidIndex) + { + oidLength = DC_OID_LTH; + } + else + { + oidLength = COMMON_NAME_OID_LTH; + } + + if(!(aByPass && aTypePtrArray->At(i).Compare(aByPassType) == 0)) + { + // Printable string + elementLength = aValuePtrArray->At(i).Length(); + stringLength = elementLength; + if(elementLength > 127) + elementLength++; + if(elementLength > 255) + elementLength++; + elementLength += ASN1_SHORT_TAG_HDR_LTH; + + elementLength += oidLength; // Oid + elementLength += ASN1_SHORT_TAG_HDR_LTH; + seqLength = elementLength; + if(elementLength > 127) + elementLength++; + if(elementLength > 255) + elementLength++; + elementLength += ASN1_SHORT_TAG_HDR_LTH; // Sequence + setLength = elementLength; + if(elementLength > 127) + elementLength++; + if(elementLength > 255) + elementLength++; + elementLength += ASN1_SHORT_TAG_HDR_LTH; // Set + wholeLength = elementLength; + + // Build one element + CPkcs10Req::BuildElementDerLC( + elementArray, + wholeLength, + setLength, + seqLength, + oidLength, + stringLength, + oidIndex, + dottedOidDer, + aValuePtrArray->At(i) + ); + + totalLength += elementLength; + } + } + } + + if( 0 == totalLength ) + { + User::Leave(KErrArgument); + } + + contentLength = totalLength; + if(totalLength > 127) // Sequence + totalLength++; + if(totalLength > 255) + totalLength++; + + totalLength += ASN1_SHORT_TAG_HDR_LTH; + + // Allocate buffer + derBuffer = HBufC8::NewL(totalLength); + derBuffer->Des().SetLength(totalLength); + TUint8 *derBufferPtr = (TUint8 *)derBuffer->Ptr(); + derBufferPtr+= CPkcs10Req::DERSetTag(derBufferPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + derBufferPtr+= CPkcs10Req::DERSetLength(derBufferPtr, contentLength); + + for(i = 0; (TInt)i < elementArray->Count(); i++) + { + derBufferPtr+= CPkcs10Req::ASNCopy( + derBufferPtr, + CONST_CAST(unsigned char *, elementArray->At(i)->Des().Ptr()), + elementArray->At(i)->Length() + ); + } + + CleanupStack::PopAndDestroy(elementArray->Count()); + CleanupStack::PopAndDestroy(elementArray); +} + +////////////////////////////////////////////////////////////////////////////// +// OidExistsLC +/////////////////////////////////////////////////////////////////////////////// +TBool CPkcs10Req::OidExistsL( + TPtr8 &aTypePtrArray, + TUint &aIndex, + TBuf8<32> &aDottedOidDer +) +{ +TBool validOId = EFalse; + + aIndex = KNullAttributeIndex; + + if(aTypePtrArray.Compare(KCountryName) == 0) + aIndex = KCountryNameIndex; + else if(aTypePtrArray.Compare(KOrganizationName) == 0) + aIndex = KOrganizationNameIndex; + else if(aTypePtrArray.Compare(KOrganizationalUnitName) == 0) + aIndex = KOrganizationalUnitNameIndex; + else if(aTypePtrArray.Compare(KLocalityName) == 0) + aIndex = KLocalityNameIndex; + else if(aTypePtrArray.Compare(KStateOrProvinceName) == 0) + aIndex = KStateOrProvinceNameIndex; + else if(aTypePtrArray.Compare(KTitle) == 0) + aIndex = KTitleIndex; + else if(aTypePtrArray.Compare(KCommonName) == 0) + aIndex = KCommonNameIndex; + else if(aTypePtrArray.Compare(KGivenName) == 0) + aIndex = KGivenNameIndex; + else if(aTypePtrArray.Compare(KSurname) == 0) + aIndex = KSurnameIndex; + else if(aTypePtrArray.Compare(KInitials) == 0) + aIndex = KInitialsIndex; + else if(aTypePtrArray.Compare(KGenerationQualifier) == 0) + aIndex = KGenerationQualifierIndex; + else if(aTypePtrArray.Compare(KDNQualifier) == 0) + aIndex = KDNQualifierIndex; + else if(aTypePtrArray.Compare(KSerialNumber) == 0) + aIndex = KSerialNumberIndex; + else if(aTypePtrArray.Compare(KDomainComponent) == 0) + aIndex = KDomainComponentIndex; + + if(KNullAttributeIndex != aIndex) + { + validOId = ETrue; + } + else + { + if(aTypePtrArray.Find(KDot) != KErrNotFound) + { + // Dotted oid notation + TUint offset = 0; + TBuf<32> tempOidBuf; + tempOidBuf.Copy(aTypePtrArray); + + CASN1EncObjectIdentifier *oidIdentifier = + CASN1EncObjectIdentifier::NewLC(tempOidBuf); + + // WriteDERL uses Length() instead of MaxLength() ? + aDottedOidDer.SetLength(32); + oidIdentifier->WriteDERL(aDottedOidDer, offset); + aDottedOidDer.SetLength(oidIdentifier->LengthDER()); + CleanupStack::PopAndDestroy(); // oidIdentifier + validOId = ETrue; + } + } + + return validOId; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// GetOid +//////////////////////////////////////////////////////////////////////////////////////// +TInt CPkcs10Req::GetOid(TUint aIndex, const TUint8 *&aValue) +{ + switch(aIndex) + { + case KCountryNameIndex: + aValue = KCountryNameOid; + break; + case KOrganizationNameIndex: + aValue = KOrganizationNameOid; + break; + case KOrganizationalUnitNameIndex: + aValue = KOrganizationalUnitNameOid; + break; + case KLocalityNameIndex: + aValue = KLocalityNameOid; + break; + case KStateOrProvinceNameIndex: + aValue = KStateOrProvinceNameOid; + break; + case KTitleIndex: + aValue = KTitleOid; + break; + case KCommonNameIndex: + aValue = KCommonNOid; + break; + case KGivenNameIndex: + aValue = KGivenNameOid; + break; + case KSurnameIndex: + aValue = KSurnameOid; + break; + case KInitialsIndex: + aValue = KInitialsOid; + break; + case KGenerationQualifierIndex: + aValue = KGenerationQualifierOid; + break; + case KDNQualifierIndex: + aValue = KDNQualifierOid; + break; + case KSerialNumberIndex: + aValue = KSerialNumberOid; + break; + case KDomainComponentIndex: + aValue = KDomainComponentOid; + break; + default: + return KErrArgument; + } + + return KErrNone; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// FillDer +//////////////////////////////////////////////////////////////////////////////////////// +void CPkcs10Req::BuildElementDerLC(CArrayFixFlat *aElementArray, TUint aWholeLength, TUint aSetLength, TUint aSeqLength, TUint aOidLength, TUint aStringLength, TUint aOidIndex, TBuf8<32> &aDottedOidDer, TPtr8 &aValuePtr) +{ + HBufC8 *elementBuffer = HBufC8::NewLC(aWholeLength); + elementBuffer->Des().SetLength(aWholeLength); + TUint8 *elementBufferPtr = (TUint8 *)elementBuffer->Ptr(); + const TUint8 *oidPtr; + + elementBufferPtr+= CPkcs10Req::DERSetTag(elementBufferPtr, (EASN1Set | ASN1_CONSTRUCTED)); + elementBufferPtr+= CPkcs10Req::DERSetLength(elementBufferPtr, aSetLength); + elementBufferPtr+= CPkcs10Req::DERSetTag(elementBufferPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + elementBufferPtr+= CPkcs10Req::DERSetLength(elementBufferPtr, aSeqLength); + if(aOidIndex != 0) + { + GetOid(aOidIndex, oidPtr); + elementBufferPtr+= CPkcs10Req::DERSetTag(elementBufferPtr, EASN1ObjectIdentifier); + elementBufferPtr+= CPkcs10Req::DERSetLength(elementBufferPtr, aOidLength); + elementBufferPtr+= CPkcs10Req::DERSetOid(elementBufferPtr, oidPtr, aOidLength); + } + else + { + // Dotted oid representation + elementBufferPtr+= CPkcs10Req::ASNCopy(elementBufferPtr, CONST_CAST(unsigned char *, aDottedOidDer.Ptr()), aDottedOidDer.Length()); + } + elementBufferPtr+= CPkcs10Req::DERSetTag(elementBufferPtr, EASN1UTF8String); + elementBufferPtr+= CPkcs10Req::DERSetLength(elementBufferPtr, aStringLength); + elementBufferPtr+= CPkcs10Req::ASNCopy(elementBufferPtr, CONST_CAST(unsigned char *, aValuePtr.Ptr()), aValuePtr.Length()); + + aElementArray->AppendL(elementBuffer); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Set subject alt name extension, email +//////////////////////////////////////////////////////////////////////////////////////// +EXPORT_C TInt CPkcs10Req::SetSubjectAltNameRfc822(const TDesC8 &aSubjectAltNameRfc822) +{ + TInt status = KErrNone; + + // Set subject alt name + if(aSubjectAltNameRfc822.Length() > 0) + iSubjectAltName.Set(aSubjectAltNameRfc822); + + return (status); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Set DNSname extension +//////////////////////////////////////////////////////////////////////////////////////// +EXPORT_C TInt CPkcs10Req::SetDNSName(const TDesC8 &aDNSName) +{ + TInt status = KErrNone; + + // Set subject alt name + if(aDNSName.Length() > 0) + iDNSName.Set(aDNSName); + + return (status); // DNS name is optional +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Set challenge password extension +//////////////////////////////////////////////////////////////////////////////////////// +EXPORT_C TInt CPkcs10Req::SetChallengePw(const TDesC8 &aChallengePw) +{ + // Set challenge password + if(aChallengePw.Length() > 0) + iChallengePW.Set(aChallengePw); + + return KErrNone; // ChallengePw is optional +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Set public key and key type (only RSA supported!) +//////////////////////////////////////////////////////////////////////////////////////// +EXPORT_C TInt CPkcs10Req::SetPublicKey(const TDesC8 &aPublicKeyBits) +{ + TInt status = KErrNone; + + // Build public key DER + if(aPublicKeyBits.Length() > 0 ) + { + iPublicKey.Set(aPublicKeyBits); + } + else + { + status = KErrArgument; + } + + return (status); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Create the request +// +// In: THashType aSignatureAlgorithmId, MD5 os SHA1 +// const TDesC& aPrivateKeyFilename, keyid used in kmdserver +// TBool doPEM, set ETrue if PEM encoding requested, otherwise out format is DER +// TSignCallback aSignCallBackL, signing callback function address, defined in pkcs10.h +// Out: HBufC8 * containing the request. Remember to pop it out of the cleanupstack and delete it when not needed anymore. +// +// Output format is either der (in example below, ASN1 decoding done) +// +// 0:d=0 hl=4 l= 431 cons: SEQUENCE +// 4:d=1 hl=4 l= 281 cons: SEQUENCE +// 8:d=2 hl=2 l= 1 prim: INTEGER :00 +// 11:d=2 hl=2 l= 52 cons: SEQUENCE : Distinguished name +// 13:d=3 hl=2 l= 16 cons: SET +// 15:d=4 hl=2 l= 14 cons: SEQUENCE +// 17:d=5 hl=2 l= 3 prim: OBJECT :commonName +// 22:d=5 hl=2 l= 7 prim: PRINTABLESTRING :subject +// 31:d=3 hl=2 l= 32 cons: SET // +// 33:d=4 hl=2 l= 30 cons: SEQUENCE +// 35:d=5 hl=2 l= 9 prim: OBJECT :emailAddress +// 46:d=5 hl=2 l= 17 prim: IA5STRING +// 65:d=2 hl=3 l= 138 cons: SEQUENCE +// 68:d=3 hl=2 l= 13 cons: SEQUENCE +// 70:d=4 hl=2 l= 9 prim: OBJECT :rsaEncryption +// 81:d=4 hl=2 l= 0 prim: NULL +// 83:d=3 hl=2 l= 121 prim: BIT STRING +// 206:d=2 hl=2 l= 81 cons: cont [ 0 ] +// 208:d=3 hl=2 l= 26 cons: SEQUENCE +// 210:d=4 hl=2 l= 9 prim: OBJECT :challengePassword +// 221:d=4 hl=2 l= 13 cons: SET +// 223:d=5 hl=2 l= 11 prim: PRINTABLESTRING :challengepw +// 236:d=3 hl=2 l= 51 cons: SEQUENCE +// 238:d=4 hl=2 l= 9 prim: OBJECT :Extension Request +// 249:d=4 hl=2 l= 38 cons: SET +// 251:d=5 hl=2 l= 36 cons: SEQUENCE +// 253:d=6 hl=2 l= 34 cons: SEQUENCE +// 255:d=7 hl=2 l= 9 prim: OBJECT :emailAddress +// 266:d=7 hl=2 l= 21 prim: OCTET STRING +// 289:d=1 hl=2 l= 13 cons: SEQUENCE +// 291:d=2 hl=2 l= 9 prim: OBJECT :sha1WithRSAEncryption +// 302:d=2 hl=2 l= 0 prim: NULL +// 304:d=1 hl=3 l= 129 prim: BIT STRING +// +// or PEM, which looks like this +// -----BEGIN NEW CERTIFICATE REQUEST----- +// MIIBqDCCARECAQAwFjEUMBIGA1UEAxMLa3Vra3VsdXVydXUwgZ8wDQYJKoZIhvcN +// AQEBBQADgY0AMIGJAoGBALCCSQ305j5l/qh4TeIJeaLdLEtKe9IpAPTCvZqZhIv/ +// VpVRw3fdq2qgBMe6zsO0kctBYkvdZ67Yq3Gdmzx1Ofx4S0F4BUDT6TGH2uP3zi04 +// zMb3IoDxF9BAp9drc6BKzMnhN5HBIV0RUoTLhQ8HKbFcwmqIAo0uJkUx2gbr+aOD +// AgMBAAGgUjAXBgkqhkiG9w0BCQcxChMIcGFzc3dvcmQwNwYJKoZIhvcNAQkOMSow +// KDAmBgNVHREEHzAdggtrdWtrdWx1dXJ1dYEOanVraUBub2tpYS5jb20wDQYJKoZI +// hvcNAQEEBQADgYEAbltAG0DNe2NuyMHoJPg4QQExhUb9rep6eSJvrACbyT2qVgJe +// vrdpOesdw7//MsOhgTP+MU36DlTVRbpoUJ8xO1wM10ljrxkWcwNfdEU4kf3NOiWB +// 4i+V99jPK2nbK/ofNpJU7m2nMvi4Z66WlM/kDEwVJe5GWwIzOIyOlfkiyc8= +// -----END NEW CERTIFICATE REQUEST----- +// +/////////////////////////////////////////////////////////////////////////////// +EXPORT_C HBufC8* CPkcs10Req::CreateCertificateRequestLC( +THashType aSignatureAlgorithmId, +const TPKIKeyIdentifier& aPrivateKeyFilename, +TBool aDoBase64, +TBool aDoPEM, +TSignCallback aSignCallBackL, +TAny* aSignCallBackContext) +{ + // Build certificate request + HBufC8 *CRInfoBuffer = NULL; + HBufC8 *CRBuffer = NULL; + TBuf8 hash; + TBase64Codec base64Codec; + HBufC8 *encodedCRBuffer = NULL; + HBufC8 *tempBuffer = NULL; + TBuf8 signature; + + // Create CertificationRequestInfo consisting + // + // - subject distinguished name + // - public key + // - Attributes + // -Subject alt name Rfc822 + // -Challenge PW + + CRInfoBuffer = ASNEncodeCertificationRequestInfoLC(); + if(CRInfoBuffer) + { + // Sign CertificationRequestInfo + // + // - create hash using the defined signature algorithm + // - create digital signature of the hash by calling the given callback function + + DigestL(CRInfoBuffer->Des(), hash, aSignatureAlgorithmId); + + // Capsulate hash in digest info + TUint digestInfoLength = hash.Length() + 5 * ASN1_SHORT_TAG_HDR_LTH + MD5SIGNATURE_OID_LTH; + HBufC8 *digestInfo = HBufC8::NewLC(digestInfoLength); + + digestInfo->Des().SetLength(digestInfoLength); + TUint8 *digestInfoPtr = (TUint8*)digestInfo->Ptr(); + digestInfoPtr+= CPkcs10Req::DERSetTag(digestInfoPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + digestInfoPtr+= CPkcs10Req::DERSetLength(digestInfoPtr, digestInfoLength - ASN1_SHORT_TAG_HDR_LTH); + digestInfoPtr+= CPkcs10Req::DERSetTag(digestInfoPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + digestInfoPtr+= CPkcs10Req::DERSetLength(digestInfoPtr, MD5SIGNATURE_OID_LTH + 2 * ASN1_SHORT_TAG_HDR_LTH); + digestInfoPtr+= CPkcs10Req::DERSetTag(digestInfoPtr, EASN1ObjectIdentifier); + digestInfoPtr+= CPkcs10Req::DERSetLength(digestInfoPtr, MD5SIGNATURE_OID_LTH); + digestInfoPtr+= CPkcs10Req::DERSetOid(digestInfoPtr, (TUint8*)KMd5SignatureOid, MD5SIGNATURE_OID_LTH); + digestInfoPtr+= CPkcs10Req::DERSetTag(digestInfoPtr, EASN1Null); + digestInfoPtr+= CPkcs10Req::DERSetLength(digestInfoPtr, 0); + digestInfoPtr+= CPkcs10Req::DERSetTag(digestInfoPtr, EASN1OctetString); + digestInfoPtr+= CPkcs10Req::DERSetLength(digestInfoPtr, hash.Length()); + digestInfoPtr+= CPkcs10Req::ASNCopy(digestInfoPtr, CONST_CAST(TUint8 *, hash.Ptr()), hash.Length()); + + // Sign it + // Only RSA supported by now + iSignatureAlgorithmId = aSignatureAlgorithmId; + aSignCallBackL( + *digestInfo, + signature, + aPrivateKeyFilename, + aSignCallBackContext); + CleanupStack::PopAndDestroy( digestInfo ); + + // Collect CertificationRequestInfo, signature algoritm identifier (NO OPTIONS) + // and signature into PKCS#10 CertificationRequest + + CRBuffer = ASNEncodeCertificationRequestLC(CRInfoBuffer->Des(), signature); + if(CRBuffer) + { + if(aDoBase64) + { + // Base64 encode the CertificationRequest + tempBuffer = base64Codec.Base64EncodeLC(CRBuffer->Des()); + if(tempBuffer != NULL) + { + // Append PEM header and trailer + encodedCRBuffer = HBufC8::NewL(tempBuffer->Length() + HEADERLENGTH + TRAILERLENGTH + 1); + TPtr8 encodedCRBufferPtr = encodedCRBuffer->Des(); + if(aDoPEM) + { + encodedCRBufferPtr.Copy(HEADER); + encodedCRBufferPtr.Append(tempBuffer->Des()); + encodedCRBufferPtr.Append(TRAILER); + } + else + encodedCRBufferPtr.Copy(tempBuffer->Des()); + + CleanupStack::PopAndDestroy(3); //tempBuffer, CRInfoBuffer, CRBuffer + CleanupStack::PushL(encodedCRBuffer); + } + else + CleanupStack::PopAndDestroy(2); //CRInfoBuffer, CRBuffer + } + else + { + CleanupStack::Pop(1); + CleanupStack::PopAndDestroy(1); //CRInfoBuffer + CleanupStack::PushL(CRBuffer); + return (CRBuffer); + } + } + else + CleanupStack::PopAndDestroy(1); //CRInfoBuffer + } + + return (encodedCRBuffer); +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// MD5 hash function +//////////////////////////////////////////////////////////////////////////////////////// +void CPkcs10Req::MD5_HashL(const TDesC8 &aInData, TDes8& aOutData) +{ + CUtlMessageDigest* md5 = TUtlCrypto::MakeMessageDigesterL(TUtlCrypto::EUtlMessageDigestMd5); + aOutData.Copy(md5->Final(aInData)); + delete md5; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// SHA1 hash function +//////////////////////////////////////////////////////////////////////////////////////// +void CPkcs10Req::SHA1_HashL(const TDesC8 &aInData, TDes8& aOutData) +{ + CUtlMessageDigest* sha1 = TUtlCrypto::MakeMessageDigesterL(TUtlCrypto::EUtlMessageDigestSha1); + aOutData.Copy(sha1->Final(aInData)); + delete sha1; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Digest function, calling either MD5 or SHA1 hash +//////////////////////////////////////////////////////////////////////////////////////// +void CPkcs10Req::DigestL(const TDesC8& aData, TDes8& aDigest, THashType aHashType) +{ + if (aHashType == HASH_TYPE_MD5) + MD5_HashL(aData, aDigest); + else + SHA1_HashL(aData, aDigest); +} + + +//////////////////////////////////////////////////////////////////////////// +// +// ASN1 related operations +// +/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////// +// Set ASN1 length +//////////////////////////////////////////////////////////////////////////////////////// +TInt CPkcs10Req::DERSetLength(TUint8 *p, TInt aLen) +{ +/*------------------------------------------------------ + * + * Encode length value (max value supported 65535) + * + *------------------------------------------------------*/ + TInt CodingLth; + aLen &= ASN1_MAX_LENGTH; + if ( aLen > 127 ) + { + if ( aLen < 256 ) + { + *p = 0x81; + CodingLth = 2; + } + else + { + *p = 0x82; + p ++; + *p = (TUint8)((aLen >> 8) & 0xff); + CodingLth = 3; + } + p ++; + } + else + { + CodingLth = 1; + } + *p = (TUint8)(aLen & 0xff); + + return CodingLth; +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Set ASN1 integer +//////////////////////////////////////////////////////////////////////////////////////// +TInt CPkcs10Req::DERSetInteger(TUint8 *p, TInt aValue) +{ +/*------------------------------------------------------ + * + * Encode Integer value (max value supported 65535) + * + *------------------------------------------------------*/ + TInt CodingLth; + aValue &= ASN1_MAX_INTEGER; + if ( aValue > 255 ) + CodingLth = CPkcs10Req::DERSetLength(p, 2); + else CodingLth = CPkcs10Req::DERSetLength(p, 1); + + p += CodingLth; //skip length + if ( aValue > 255 ) { + *p = (TUint8)((aValue >> 8) & 0xff); + p ++; + CodingLth ++; + } + *p = (TUint8)(aValue & 0xff); + CodingLth ++; + + return CodingLth; +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// Encode Certification Request Info +//////////////////////////////////////////////////////////////////////////////////////// +HBufC8* CPkcs10Req::ASNEncodeCertificationRequestInfoLC() +{ + // Build certificate request info + HBufC8 *CRInfoBuffer = NULL; + TUint8 *CRInfoPtr; + TUint requestInfoLength; + TUint coverSeqHeaderLength = 2; + + HBufC8 *commonAttributes; + HBufC8 *pkiInfo; + HBufC8 *extendedAttributes; + + // Build components + // Encode common attributes + commonAttributes = ASNEncodeCommonAttributesLC(); + // Encode private key + pkiInfo = ASNEncodePKIinfoLC(); + // Encode common attributes + extendedAttributes = ASNEncodeExtendedAttributesLC(); + + requestInfoLength = commonAttributes->Length() + pkiInfo->Length() + extendedAttributes->Length() + ASN1_SHORT_TAG_HDR_LTH + 1; // Fixed one byte version number! + + if(requestInfoLength > 127) + coverSeqHeaderLength++; + if(requestInfoLength > 255) + coverSeqHeaderLength++; + + requestInfoLength = requestInfoLength + coverSeqHeaderLength; + + if(requestInfoLength > 0) + { + CRInfoBuffer = HBufC8::NewL(requestInfoLength); + CRInfoBuffer->Des().SetLength(requestInfoLength); + CRInfoPtr = (TUint8*)CRInfoBuffer->Ptr(); + + // Build header + CRInfoPtr += CPkcs10Req::DERSetTag(CRInfoPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + CRInfoPtr += CPkcs10Req::DERSetLength(CRInfoPtr, requestInfoLength - coverSeqHeaderLength); + CRInfoPtr += CPkcs10Req::DERSetTag(CRInfoPtr, EASN1Integer); + CRInfoPtr += DERSetInteger(CRInfoPtr, 0); // version + + // Copy component ders + if(commonAttributes != NULL && commonAttributes->Length() > 0) + CRInfoPtr += CPkcs10Req::ASNCopy(CRInfoPtr, CONST_CAST(TUint8 *, commonAttributes->Ptr()), commonAttributes->Length()); + + if(pkiInfo != NULL && pkiInfo->Length() > 0) + CRInfoPtr += CPkcs10Req::ASNCopy(CRInfoPtr, CONST_CAST(TUint8 *, pkiInfo->Ptr()), pkiInfo->Length()); + + if(extendedAttributes != NULL && extendedAttributes->Length() > 0) + CRInfoPtr += CPkcs10Req::ASNCopy(CRInfoPtr, CONST_CAST(TUint8 *, extendedAttributes->Ptr()), extendedAttributes->Length()); + } + + if(extendedAttributes != NULL) + { + delete extendedAttributes; + CleanupStack::Pop(1); + } + if(pkiInfo != NULL) + { + delete pkiInfo; + CleanupStack::Pop(1); + } + if(commonAttributes != NULL) + { + delete commonAttributes; + CleanupStack::Pop(1); + } + if(CRInfoBuffer != NULL) + CleanupStack::PushL(CRInfoBuffer); // to be destroyed by the caller + + return (CRInfoBuffer); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Encode Certification Request +//////////////////////////////////////////////////////////////////////////////////////// +HBufC8* CPkcs10Req::ASNEncodeCertificationRequestLC(const TDes8 &CRInfoBuffer, const TDes8 &signature) +{ + HBufC8 *CRBuffer = NULL; + TUint8 *CRPtr; + TUint requestLength; + TUint coverSeqHeaderLength = 2; + TUint signatureLength = 0; + + if(CRInfoBuffer.Length() > 0 && signature.Length() > 0) + { + signatureLength = signature.Length() + 1; // No padding zero added + if(signatureLength > 127) + signatureLength++; + if(signatureLength > 255) + signatureLength++; + + if(iSignatureAlgorithmId == HASH_TYPE_MD5) + requestLength = CRInfoBuffer.Length() + signatureLength + 4 * ASN1_SHORT_TAG_HDR_LTH + MD5WITHRSA_OID_LTH; + else + requestLength = CRInfoBuffer.Length() + signatureLength + 4 * ASN1_SHORT_TAG_HDR_LTH + SHA1WITHRSA_OID_LTH; + + if(requestLength > 127) + coverSeqHeaderLength++; + if(requestLength > 255) + coverSeqHeaderLength++; + + requestLength = requestLength + coverSeqHeaderLength; + + CRBuffer = HBufC8::NewL(requestLength); + CRBuffer->Des().SetLength(requestLength); + CRPtr = (TUint8*)CRBuffer->Ptr(); + + CRPtr += CPkcs10Req::DERSetTag(CRPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + CRPtr += CPkcs10Req::DERSetLength(CRPtr, requestLength - coverSeqHeaderLength); + + // Copy Certificate request info + CRPtr += CPkcs10Req::ASNCopy(CRPtr, CONST_CAST(TUint8 *, CRInfoBuffer.Ptr()), CRInfoBuffer.Length()); + CRPtr += CPkcs10Req::DERSetTag(CRPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + CRPtr += CPkcs10Req::DERSetLength(CRPtr, MD5WITHRSA_OID_LTH + 2 * ASN1_SHORT_TAG_HDR_LTH); // null included! + // Set algorithm identifier + CRPtr += CPkcs10Req::DERSetTag(CRPtr, EASN1ObjectIdentifier); + if(iSignatureAlgorithmId == HASH_TYPE_MD5) + { + CRPtr += CPkcs10Req::DERSetLength(CRPtr, MD5WITHRSA_OID_LTH); + CRPtr += CPkcs10Req::DERSetOid(CRPtr, (TUint8*)KMd5WithRSAEncryptionOid, MD5WITHRSA_OID_LTH); + } + else + { + CRPtr += CPkcs10Req::DERSetLength(CRPtr, SHA1WITHRSA_OID_LTH); + CRPtr += CPkcs10Req::DERSetOid(CRPtr, (TUint8*)KSha1WithRSAEncryptionOid, SHA1WITHRSA_OID_LTH); + } + + CRPtr += CPkcs10Req::DERSetTag(CRPtr, EASN1Null); + CRPtr += CPkcs10Req::DERSetLength(CRPtr, 0); + // Copy signature + CRPtr += CPkcs10Req::DERSetTag(CRPtr, EASN1BitString); + CRPtr += CPkcs10Req::DERSetLength(CRPtr, signature.Length() + 1); + CRPtr += CPkcs10Req::DERSetLength(CRPtr, 0); // add 0, no padding + CRPtr += CPkcs10Req::ASNCopy(CRPtr, CONST_CAST(TUint8 *, signature.Ptr()), signature.Length()); + + CleanupStack::PushL(CRBuffer); // to be destroyed by the caller + } + return (CRBuffer); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Encode common attributes +//////////////////////////////////////////////////////////////////////////////////////// +HBufC8* CPkcs10Req::ASNEncodeCommonAttributesLC() +{ + HBufC8 *commonAttributesBuffer = NULL; + TUint8 *commonAttributesPtr; + TUint distinguishedNameAttrLength; + TUint totalLength = 0; + TUint coverSeqHeaderLength = 2; + + if(iUseExtendedDistinguishedName) + { + if(iDNDerBuf != NULL && iDNDerBuf->Des().Length() > 0) + commonAttributesBuffer = iDNDerBuf->Des().AllocLC(); + return (commonAttributesBuffer); + } + if(iDistinguishedName.Length() == 0) + distinguishedNameAttrLength = 0; + else + distinguishedNameAttrLength = 4 * ASN1_SHORT_TAG_HDR_LTH + COMMON_NAME_OID_LTH + iDistinguishedName.Length(); + + totalLength = distinguishedNameAttrLength; + if(totalLength > 127) + coverSeqHeaderLength++; + if(totalLength > 255) + coverSeqHeaderLength++; + + totalLength = totalLength + coverSeqHeaderLength; + + if(totalLength > 0) + { + commonAttributesBuffer = HBufC8::NewL(totalLength); + commonAttributesBuffer->Des().SetLength(totalLength); + commonAttributesPtr = (TUint8*)commonAttributesBuffer->Ptr(); + + commonAttributesPtr+= CPkcs10Req::DERSetTag(commonAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + commonAttributesPtr+= CPkcs10Req::DERSetLength(commonAttributesPtr, totalLength - coverSeqHeaderLength); + + if(iDistinguishedName.Length() > 0) + { + // Encode common name + commonAttributesPtr+= CPkcs10Req::DERSetTag(commonAttributesPtr, (EASN1Set | ASN1_CONSTRUCTED)); + commonAttributesPtr+= CPkcs10Req::DERSetLength(commonAttributesPtr, distinguishedNameAttrLength - ASN1_SHORT_TAG_HDR_LTH); + commonAttributesPtr+= CPkcs10Req::DERSetTag(commonAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + commonAttributesPtr+= CPkcs10Req::DERSetLength(commonAttributesPtr, distinguishedNameAttrLength - 2 * ASN1_SHORT_TAG_HDR_LTH); + commonAttributesPtr+= CPkcs10Req::DERSetTag(commonAttributesPtr, EASN1ObjectIdentifier); + commonAttributesPtr+= CPkcs10Req::DERSetLength(commonAttributesPtr, COMMON_NAME_OID_LTH); + commonAttributesPtr+= CPkcs10Req::DERSetOid(commonAttributesPtr, (TUint8*)KCommonNameOid, COMMON_NAME_OID_LTH); + commonAttributesPtr+= CPkcs10Req::DERSetTag(commonAttributesPtr, EASN1UTF8String); + commonAttributesPtr+= CPkcs10Req::DERSetLength(commonAttributesPtr, iDistinguishedName.Length()); + commonAttributesPtr+= CPkcs10Req::ASNCopy(commonAttributesPtr, CONST_CAST(TUint8 *, iDistinguishedName.Ptr()), iDistinguishedName.Length()); + } + CleanupStack::PushL(commonAttributesBuffer); + } + return (commonAttributesBuffer); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Encode Private key information +//////////////////////////////////////////////////////////////////////////////////////// +HBufC8* CPkcs10Req::ASNEncodePKIinfoLC() +{ + HBufC8 *pkiInfoBuffer = NULL; + TUint8 *pkiInfoPtr; + TUint entityLength; + TUint bitStringHeaderLength = 2; + TUint coverSeqHeaderLength = 2; + + if(iPublicKey.Length() == 0) + entityLength = 0; + else + { + entityLength = iPublicKey.Length() + 1; // Add 1 for padding indicator byte + if(entityLength > 127) + bitStringHeaderLength++; + if(entityLength > 255) + bitStringHeaderLength++; + + entityLength = entityLength + bitStringHeaderLength + 3 * ASN1_SHORT_TAG_HDR_LTH + KEY_TYPE_RSA_OID_LTH; + + if(entityLength > 127) + coverSeqHeaderLength++; + if(entityLength > 255) + coverSeqHeaderLength++; + + entityLength = entityLength + coverSeqHeaderLength; + } + + if(entityLength > 0) + { + pkiInfoBuffer = HBufC8::NewL(entityLength); + pkiInfoBuffer->Des().SetLength(entityLength); + pkiInfoPtr = (TUint8*)pkiInfoBuffer->Ptr(); + + pkiInfoPtr+= CPkcs10Req::DERSetTag(pkiInfoPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + pkiInfoPtr+= CPkcs10Req::DERSetLength(pkiInfoPtr, entityLength - coverSeqHeaderLength); + pkiInfoPtr+= CPkcs10Req::DERSetTag(pkiInfoPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + pkiInfoPtr+= CPkcs10Req::DERSetLength(pkiInfoPtr, KEY_TYPE_RSA_OID_LTH + ASN1_SHORT_TAG_HDR_LTH + ASN1_NULL_LTH); + // Only RSA key supported + pkiInfoPtr+= CPkcs10Req::DERSetTag(pkiInfoPtr, EASN1ObjectIdentifier); + pkiInfoPtr+= CPkcs10Req::DERSetLength(pkiInfoPtr, KEY_TYPE_RSA_OID_LTH); + pkiInfoPtr+= CPkcs10Req::DERSetOid(pkiInfoPtr, (TUint8*)KKeyTypeRSAOid, KEY_TYPE_RSA_OID_LTH); + pkiInfoPtr+= CPkcs10Req::DERSetTag(pkiInfoPtr, EASN1Null); + pkiInfoPtr+= CPkcs10Req::DERSetLength(pkiInfoPtr, 0); + pkiInfoPtr+= CPkcs10Req::DERSetTag(pkiInfoPtr, EASN1BitString); + pkiInfoPtr+= CPkcs10Req::DERSetLength(pkiInfoPtr, iPublicKey.Length() + 1); + pkiInfoPtr+= CPkcs10Req::DERSetLength(pkiInfoPtr, 0); // add 0, no padding + pkiInfoPtr+= CPkcs10Req::ASNCopy(pkiInfoPtr, CONST_CAST(TUint8 *, iPublicKey.Ptr()), iPublicKey.Length()); + + CleanupStack::PushL(pkiInfoBuffer); + } + return (pkiInfoBuffer); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// Encode Extended attributes +//////////////////////////////////////////////////////////////////////////////////////// +HBufC8* CPkcs10Req::ASNEncodeExtendedAttributesLC() +{ + HBufC8 *extendedAttributesBuffer = NULL; + TUint8 *extendedAttributesPtr; + TUint challengePWLength; + TUint DNSLength; + TUint SubAltNLength; + TUint emailExtensionLength; + TUint totalLength; + TUint tempLength; + TUint coverTagLength = 2; + + if(iChallengePW.Length() == 0) + challengePWLength = 0; + else + challengePWLength = 4 * ASN1_SHORT_TAG_HDR_LTH + CHALLENGEPW_OID_LTH + iChallengePW.Length(); + + if((iSubjectAltName.Length() == 0) && (iDNSName.Length() == 0)) + emailExtensionLength = 0; + else + // Header part + emailExtensionLength = 8 * ASN1_SHORT_TAG_HDR_LTH + SUBJECTALTNAME_OID_LTH + PKCS10_EXTENSION_OID_LTH; + + if(iSubjectAltName.Length() == 0) + SubAltNLength = 0; + else + SubAltNLength = ASN1_SHORT_TAG_HDR_LTH + iSubjectAltName.Length(); + + emailExtensionLength = emailExtensionLength + SubAltNLength; + + if(iDNSName.Length() == 0) + DNSLength = 0; + else + DNSLength = ASN1_SHORT_TAG_HDR_LTH + iDNSName.Length(); + + emailExtensionLength = emailExtensionLength + DNSLength; + totalLength = challengePWLength + emailExtensionLength; + + if(totalLength > 127) + coverTagLength++; + + totalLength = totalLength + coverTagLength; + + if(totalLength > 0) + { + extendedAttributesBuffer = HBufC8::NewL(totalLength); + extendedAttributesBuffer->Des().SetLength(totalLength); + extendedAttributesPtr = (TUint8*)extendedAttributesBuffer->Ptr(); + + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1EOC | ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, totalLength - coverTagLength); + + if(challengePWLength > 0) + { + // Challenge password + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, challengePWLength - ASN1_SHORT_TAG_HDR_LTH ); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, EASN1ObjectIdentifier); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, CHALLENGEPW_OID_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetOid(extendedAttributesPtr, (TUint8*)KChallengePWOid, CHALLENGEPW_OID_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Set | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, iChallengePW.Length() + ASN1_SHORT_TAG_HDR_LTH ); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1PrintableString)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, iChallengePW.Length()); + extendedAttributesPtr+= CPkcs10Req::ASNCopy(extendedAttributesPtr, CONST_CAST(TUint8 *, iChallengePW.Ptr()), iChallengePW.Length()); + } + + if(emailExtensionLength > 0) + { + // SubjectAltName.Email assumed to be less than 97 bytes + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, emailExtensionLength - ASN1_SHORT_TAG_HDR_LTH ); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, EASN1ObjectIdentifier); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, PKCS10_EXTENSION_OID_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetOid(extendedAttributesPtr, (TUint8*)KPkcs10ExtensionOid, PKCS10_EXTENSION_OID_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Set | ASN1_CONSTRUCTED)); + tempLength = SUBJECTALTNAME_OID_LTH + 5 * ASN1_SHORT_TAG_HDR_LTH + DNSLength + SubAltNLength; + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, tempLength); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, tempLength - ASN1_SHORT_TAG_HDR_LTH ); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, tempLength - 2 * ASN1_SHORT_TAG_HDR_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, EASN1ObjectIdentifier); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, SUBJECTALTNAME_OID_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetOid(extendedAttributesPtr, (TUint8*)KSubjectAltNameOid, SUBJECTALTNAME_OID_LTH); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, EASN1OctetString); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, ASN1_SHORT_TAG_HDR_LTH + DNSLength + SubAltNLength); + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1Sequence | ASN1_CONSTRUCTED)); + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, DNSLength + SubAltNLength); + + if(SubAltNLength > 0) + { + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1EOC | ASN1_CONTEXT_SPECIFIC | 0x1)); // context specific 1 + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, iSubjectAltName.Length()); + extendedAttributesPtr+= CPkcs10Req::ASNCopy(extendedAttributesPtr, CONST_CAST(TUint8 *, iSubjectAltName.Ptr()), iSubjectAltName.Length()); + } + + if(DNSLength > 0) + { + extendedAttributesPtr+= CPkcs10Req::DERSetTag(extendedAttributesPtr, (EASN1EOC | ASN1_CONTEXT_SPECIFIC | 0x2)); // context specific 2 + extendedAttributesPtr+= CPkcs10Req::DERSetLength(extendedAttributesPtr, iDNSName.Length()); + extendedAttributesPtr+= CPkcs10Req::ASNCopy(extendedAttributesPtr, CONST_CAST(TUint8 *, iDNSName.Ptr()), iDNSName.Length()); + } + } + CleanupStack::PushL(extendedAttributesBuffer); + } + + return (extendedAttributesBuffer); +} + +//////////////////////////////////////////////////////////////////////////////////////// +// ASN1 copy +//////////////////////////////////////////////////////////////////////////////////////// +TInt CPkcs10Req::ASNCopy(TUint8 *dataPtr, TUint8 *valuePtr, TInt length) +{ + Mem::Copy(dataPtr, valuePtr, length); + return (length); +}