diff -r 000000000000 -r 33413c0669b9 vpnengine/ikecert/src/ikepkiutils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikecert/src/ikepkiutils.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,587 @@ +/* +* Copyright (c) 2008-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: static method for certficate handling +* +*/ + +#include +#include +#include + +#include "ikepkiutils.h" +#include "ikev1pkiservice.h" +#include "ikepublickey.h" +#include "utlcrypto.h" +#include "ikecert.h" +#include "ikecalist.h" +#include "ikecaelem.h" +#include "ikev2const.h" +#include "ikev2payloads.h" +#include "ikecertconst.h" + +EXPORT_C TBool IkePkiUtils::CertifyIdentityL(const CX509Certificate* aCert, + TDesC8& aId, TInt aIdType) + + { + TBool status = EFalse; + if ( aCert ) + { + // + // Check that specified identity exist in current certificate + // data. If aIdType type is ID_DER_ASN1_DN, identity must match + // with Certificate Subject name. + // All other aIdTypes ara checked against SubjectAltNames data, + // if present + // + if ( aIdType == ID_DER_ASN1_DN ) + { + // + // Binary DER encoding of an ASN.1 X.500 Distinguished Name identity + // is certified by comparing it to peer certificate subject name + // + + CX500DistinguishedName* asn1DnName = CX500DistinguishedName::NewLC(aId); + status = asn1DnName->ExactMatchL(aCert->SubjectName()); + CleanupStack::PopAndDestroy(asn1DnName); + + } + else + { + // + // Other identity types are certified by comparing it to the SubjectAltName field + // + HBufC8* idBuf = HBufC8::NewLC(aId.Length() + 2); + TUint8* idHdr = (TUint8*)idBuf->Des().Ptr(); + idHdr[1] = (TUint8)aId.Length(); + + switch ( aIdType ) + { + case ID_IPV4_ADDR: + idHdr[0] = 0x87; + break; + case ID_FQDN: + idHdr[0] = 0x82; + break; + case ID_RFC822_ADDR: + idHdr[0] = 0x81; + break; + case ID_IPV6_ADDR: + idHdr[0] = 0x87; + break; + default: + idHdr = NULL; + break; + } + + if ( idHdr ) + { + Mem::Copy(&idHdr[2], aId.Ptr(), aId.Length()); + TPtrC8 ptrId(idHdr, (aId.Length() + 2)); + status = IkeCert::AltNameExistsL(aCert, ptrId); + } + CleanupStack::PopAndDestroy(); //IdBuf + } + } + + return status; + } + + +EXPORT_C TBool IkePkiUtils::VerifyIkev2SignatureL(const TDesC8& aSignature, + const TDesC8& aAuthData, + const CX509Certificate& aCert) + { + // + // Calculate SHA1 hash over aAuthdata to build reference hash and + // verify IKEv2 signature with it + // + CUtlMessageDigest* Digest = TUtlCrypto::MakeMessageDigesterL(TUtlCrypto::EUtlMessageDigestSha1); + CleanupStack::PushL(Digest); + TPtrC8 RefHash = Digest->Final(aAuthData); + TBool Status = VerifySignatureL(MAJORV2, aSignature, RefHash, aCert); + CleanupStack::PopAndDestroy(Digest); + + return Status; + } + + +EXPORT_C TBool IkePkiUtils::VerifyIkev1SignatureL(const TDesC8& aSignature, + const TDesC8& aRefHash, + const CX509Certificate& aCert) + { + // + // Verify IKEv1 signature. + // + return VerifySignatureL(MAJORV1, aSignature, aRefHash, aCert); + } + + +EXPORT_C HBufC8* IkePkiUtils::GetIdentityFromCertL(TUint8 aIdType, const TDesC8& aCertData) + { + // + // Get IKE Identity data from specified Certificate data. The Id + // type parameter specifies the field of Certificate from where the + // Identity data is taken + // + HBufC8* identity; + CX509Certificate* cert = CX509Certificate::NewL(aCertData); + CleanupStack::PushL(cert); + if ( (aIdType == ID_DER_ASN1_DN) || (aIdType == ID_NOT_DEFINED) || (aIdType > ID_IPV6_ADDR) ) + { + // + // DER encoded ASN.1 X.500 Distinguished Name as IKE identity + // Get Id data from certificate subject name + // + identity = IkeCert::GetCertificateFieldDERL(cert, KSubjectName); + } + else + { + // + // Get IKE Identity from own certifate subject alt name extension (according to IdType value) + // + identity = IkeCert::GetSubjectAltNameDataL(cert, aIdType); + } + CleanupStack::PopAndDestroy(cert); + + return identity; + } + + +EXPORT_C TBool IkePkiUtils::GetCertSubjectNameDERL(const CX509Certificate* aCert, + TDes8& aSubjectName) + { + TBool status = ETrue; + HBufC8* nameBfr = IkeCert::GetCertificateFieldDERL(aCert, KSubjectName); + if ( nameBfr && ( nameBfr->Des().Length() <= aSubjectName.MaxLength() ) ) + { + aSubjectName.Copy(nameBfr->Des()); + delete nameBfr; + } + else + { + aSubjectName.SetLength(0); + status = EFalse; + } + + return status; + } + + +EXPORT_C CX509Certificate* IkePkiUtils::VerifyCertificateL(const CArrayFixFlat& aCerts, + const CIkeCaList& aTrustedCAList) + { + const CArrayFixFlat* certificateArray = IkePkiUtils::CastCertArray(&aCerts); + return VerifyCertificateL(*certificateArray, aTrustedCAList); + + } + + +EXPORT_C CX509Certificate* IkePkiUtils::VerifyCertificateL(const CArrayFixFlat& aCerts, + const CIkeCaList& aTrustedCAList) + { + // + // Find a requested certificate or chain of certificates from IKE certificate payload array + // using trusted CA list (iTrustedCAList). + // When requested certificate found verify this certificate. + // + CX509Certificate* certOk = NULL; + CX509Certificate* caCert = NULL; + CX509Certificate* currCert=NULL; + const TCertPayloadIkev2* firstCertPayload=NULL; + const TCertPayloadIkev2* certPayload = aCerts.At(0); + __ASSERT_DEBUG(certPayload != NULL, User::Invariant()); + CArrayFixFlat* caArray=new (ELeave) CArrayFixFlat (aCerts.Count()); + CleanupStack::PushL(caArray); + for ( TInt i = 0; i < aCerts.Count(); i++ ) + { + const TCertPayloadIkev2* currCertPayload=aCerts.At(i); + __ASSERT_DEBUG(currCertPayload != NULL, User::Invariant()); + if ( currCertPayload->GetEncoding() == X509_CERTIFICATE_SIGN ) + { + firstCertPayload=currCertPayload; + const TPtrC8 CertStream(currCertPayload->Certificate(), + (TPayloadIkev2::Cast(currCertPayload)->GetLength() - + TCertPayloadIkev2::Size())); + currCert = CX509Certificate::NewLC(CertStream); + caArray->AppendL(currCert); + } + } + if ( caArray->Count() == 1) + { + const TPtrC8 userCertStream(firstCertPayload->Certificate(), + (TPayloadIkev2::Cast(firstCertPayload)->GetLength() - + TCertPayloadIkev2::Size())); + firstCertPayload=NULL; + CX509Certificate* clientCert = CX509Certificate::NewLC(userCertStream); + caCert = IkePkiUtils::FindCaCertificateL(*clientCert, aTrustedCAList); + if ( caCert ) + { + certOk = IkePkiUtils::VerifyX509CertificateL(*caCert, *clientCert); + if ( certOk ) // CertOk = clientCert + { + CleanupStack::Pop(clientCert); // CertOk = Cert + clientCert=NULL; + + CleanupStack::PopAndDestroy(currCert); + currCert=NULL; + + CleanupStack::PopAndDestroy(caArray); + caArray=NULL; + + return certOk; + } + } + CleanupStack::PopAndDestroy(clientCert); // Cert + clientCert=NULL; + + CleanupStack::PopAndDestroy(currCert); + currCert=NULL; + + CleanupStack::PopAndDestroy(caArray); + caArray=NULL; + return NULL; + } + if ( caArray->Count()>1 ) //if certificate chain is received + { + CX509Certificate* userCert=NULL; + CX509Certificate* certChainRoot = IkePkiUtils::VerifyCertChainL(*caArray, userCert, aTrustedCAList); + + CX509Certificate* realUserCert=userCert; //Real user certificate found from chain as a parameter by reference + + //cleaning + TInt certCount=caArray->Count(); + for ( TInt i=0; iAt(i); + if ( realUserCert != itemPtr && certChainRoot != itemPtr ) + delete itemPtr; + } + for ( TInt i=0; iDelete(0); + delete caArray; + + if ( certChainRoot ) + caCert = IkePkiUtils::FindCaCertificateL(*certChainRoot, aTrustedCAList); + else + caCert=NULL; + if ( caCert ) + certOk = IkePkiUtils::VerifyX509CertificateL(*caCert, *certChainRoot); + if ( certChainRoot != userCert ) + delete certChainRoot; + if ( certOk ) + return realUserCert; + + else + return NULL; + } + return NULL; + } + + +EXPORT_C const CArrayFixFlat* IkePkiUtils::CastCertArray(const CArrayFixFlat* aCerts) + { + return reinterpret_cast*>(aCerts); + } + + +CX509Certificate* IkePkiUtils::VerifyX509CertificateL(const CX509Certificate& aCaCert, + CX509Certificate& aCert) + { + TPKIKeyAlgorithm reqKeyType = EPKIInvalidAlgorithm; + + switch (aCert.SigningAlgorithm().AsymmetricAlgorithm().Algorithm()) + { + case ERSA: + reqKeyType = EPKIRSA; + break; + case EDSA: + reqKeyType = EPKIDSA; + break; + default: + break; + } + + if ( reqKeyType == EPKIInvalidAlgorithm ) + return NULL; + + CIkePublicKey* pubKey = CIkePublicKey::NewL(aCaCert); + if ( !pubKey ) + return NULL; + + if ( reqKeyType != pubKey->Algorithm() ) + { + delete pubKey; + return NULL; + } + CleanupStack::PushL(pubKey); + TInt ret = KErrNotSupported; + TBool valid = EFalse; + + switch (pubKey->Algorithm()) + { + case EPKIRSA: + TRAP(ret, valid = aCert.VerifySignatureL(pubKey->KeyData())); + break; + + case EPKIDSA: + { + TX509KeyFactory keyFactory; + CDSAParameters* params = keyFactory.DSAParametersL(pubKey->KeyParams()); + CleanupStack::PushL(params); + + CSigningKeyParameters*sgkp = CSigningKeyParameters::NewLC(); + sgkp->SetDSAParamsL(*params); + aCert.SetParametersL(*sgkp); + TRAP(ret, valid = aCert.VerifySignatureL(pubKey->KeyData())); + CleanupStack::PopAndDestroy(sgkp); + CleanupStack::PopAndDestroy(params); + break; + } + + case EPKIInvalidAlgorithm: + break; + } + + CX509Certificate* ValidCert = NULL; + if ( valid && (ret == KErrNone) ) + { + // + // One hour error margin, no warning margin + // + ret = IkeCert::CheckValidityPeriod(aCert, 0, KDefaultErrorMargin); + if ( ret == KErrNone ) + { + // + // Verify certificate extensions + // + ret = IkeCert::VerifyCertExtensionsL(aCert); + if ( ret == KErrNone || ret == KCertVerifyCACertificate ) + ValidCert = &aCert; + } + } + + CleanupStack::PopAndDestroy(pubKey); + return ValidCert; +} + + +CX509Certificate* IkePkiUtils::FindCaCertificateL(const CX509Certificate& aUserCert, + const CIkeCaList& aTrustedCAList) + { + // + // Find a CA certificate from Trusted CA list (iTrustedCAList) for + // specified user certificate + // + const CX500DistinguishedName& issuerName = aUserCert.IssuerName(); + CX509Certificate* caCert = NULL; + + for (TInt i = 0; i < aTrustedCAList.Count(); i++) + { + // + // Find CA certificate for current user certificate by + // comparing certificate issuer name to the subject name of a + // CA certificate + // + caCert = aTrustedCAList.At(i)->Certificate(); + if ( issuerName.ExactMatchL(caCert->SubjectName()) ) + { + break; + } + caCert = NULL; + } + + return caCert; + } + + +TBool IkePkiUtils::VerifySignatureL(TInt aIkeVersion, + const TDesC8& aSignature, + const TDesC8& aRefHash, + const CX509Certificate& aCert) + { + // + // Verify IKE signature. + // + TBool status = EFalse; + + if ( aSignature.Length() > 0 ) + { + CIkePublicKey* publicKey = CIkePublicKey::NewL(aCert); + if ( !publicKey ) + { + return EFalse; + } + + CleanupStack::PushL(publicKey); + + switch (publicKey->Algorithm()) + { + case EPKIRSA: + { + HBufC8 *resBuf; + TUtlCrypto::RsaPublicKeyDecryptL(publicKey->KeyData(), aSignature, resBuf); + CleanupStack::PushL(resBuf); + + if ( aIkeVersion == MAJORV1 ) + { + // + // Because IKEv1 signature is not a "real" PKCS1 + // encoded signature but pure private encrypted has + // signature is verified by using RSA public key + // decrypt and result comparison to reference hash + // + status = (aRefHash.Compare(*resBuf) == 0); //Compare the result with the hash to see if they match + } + else + { + // + // IKEv2(n) signature is encoded as PKCS1v1_5 + // signature (EMSA-PKCS1-v1_5) + // ASN1 encoding of signature is the following: + // DigestInfo::=SEQUENCE{ + // digestAlgorithm AlgorithmIdentifier, + // digest OCTET STRING } + // + CArrayPtrFlat* seq = NULL; + TInt position = 0; + + TRAPD(err, seq = DecodeDERL(*resBuf, position)); + if ( err == KErrNone ) + { + TCleanupItem CleanupSeq(IkeCert::CleanupSequence, seq); + CleanupStack::PushL(CleanupSeq); + if (seq->Count() == 2) + { + // + // Currently the digestAlgorithm is not + // verified, but only digest value itself is + // compared with reference hash. + // ( see CPKCS1SignatureResult::DoVerifyL() in + // x509cert.cpp) + // + const TASN1DecGeneric* gen2 = seq->At(1); + TPtrC8 digest(gen2->GetContentDER()); + status = (aRefHash.Compare(digest) == 0); + } + CleanupStack::PopAndDestroy(); //CleanupSeq + } + else + { + // + // Verify signature as pure encrypted (SHA1) + // hash as old IKEv1 style "signature" + // + //DEB(iService.PrintText(_L("Old IKEv1 style signature used by IKEv2 peer !\n"));) + status = (aRefHash.Compare(*resBuf) == 0); //Compare the result with the hash to see if they match + } + } + CleanupStack::PopAndDestroy(resBuf); + break; + } + case EPKIDSA: + { + const TPtrC8 sigR = aSignature.Left(aSignature.Length() / 2); + const TPtrC8 sigS = aSignature.Right(aSignature.Length() / 2); + + status = TUtlCrypto::DsaVerifySignatureL(publicKey->KeyData(), + publicKey->KeyParams(), + sigR, sigS, aRefHash); + break; + } + default: //Only RSA and DSA are valid + User::Invariant(); + break; + } + + CleanupStack::PopAndDestroy(publicKey); + } + return status; + } + + +CArrayPtrFlat* IkePkiUtils::DecodeDERL(const TDesC8& aPtr, TInt& aPosition) + { + TASN1DecSequence decSeq; + CArrayPtrFlat* seq = decSeq.DecodeDERLC(aPtr, aPosition); + CleanupStack::Pop(seq); + return seq; + } + +EXPORT_C CX509Certificate* IkePkiUtils::VerifyCertChainL(const CArrayFixFlat& aCerts, CX509Certificate*& realUserCert, const CIkeCaList& aTrustedCAList) + { + CX509Certificate* currCaCert=NULL; + CX509Certificate* certOk=NULL; + CArrayFixFlat* issuerArray=new (ELeave) CArrayFixFlat (aCerts.Count()); + CArrayFixFlat* subjectArray=new (ELeave) CArrayFixFlat (aCerts.Count()); + CleanupStack::PushL(issuerArray); + CleanupStack::PushL(subjectArray); + for ( TInt i = 0; i < aCerts.Count(); i++ ) + { + issuerArray->AppendL(&aCerts.At(i)->IssuerName()); + subjectArray->AppendL(&aCerts.At(i)->SubjectName()); + } + TInt userCertIndex=0; + TInt caCertIndex=0; + + //find UserCert from aCerts array if not in first certificate payload + for ( TInt i = 0; i < (aCerts.Count()); i++ ) + { + for ( TInt j = 0; j < (aCerts.Count()); j++ ) + { + caCertIndex=j; + if ( i!=caCertIndex ) + { + const CX500DistinguishedName& issuerNameStr=*issuerArray->At(j); + if ( issuerNameStr.ExactMatchL(*subjectArray->At(i)) ) + userCertIndex=i+1; + } + } + if ( userCertIndex == i && i!=caCertIndex ) + break; + userCertIndex=i; + } + CleanupStack::PopAndDestroy(subjectArray); + CleanupStack::PopAndDestroy(issuerArray); + CX509Certificate* currCert = aCerts.At(userCertIndex); + TInt currCertIndex=0; + realUserCert=currCert; + //Verify chain and return highest CA + while ( currCertIndex < aCerts.Count()) + { + CX509Certificate* trustedCaCert = IkePkiUtils::FindCaCertificateL(*currCert, aTrustedCAList); + if ( trustedCaCert ) + return currCert; + else + { + for ( TInt j = 0; j < aCerts.Count(); j++ ) + { + currCaCert = aCerts.At(j); + if ( currCert->IssuerName().ExactMatchL(currCaCert->SubjectName())) + { + certOk = IkePkiUtils::VerifyX509CertificateL(*currCaCert, *currCert); + if ( certOk ) + break; + } + } + if ( !certOk ) //if chain is malicious break and return NULL + break; + currCertIndex++; + currCert=currCaCert; + } + } + return NULL; + }