diff -r e8e63152f320 -r 2a9601315dfc javacommons/security/src/midpauthenticationmoduleimpl.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javacommons/security/src/midpauthenticationmoduleimpl.cpp Mon May 03 12:27:20 2010 +0300 @@ -0,0 +1,613 @@ +/* +* Copyright (c) 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: +* +*/ + + +#include "javajniutils.h" +#include "com_nokia_mj_impl_security_midp_authentication_AuthenticationModule.h" +#include "midpauthenticationmodule.h" +#include "midpauthenticationmoduleimpl.h" +#include "storagehandler.h" +#include "securityutils.h" +#include "javacertstorehandler.h" +#include "securitycommsmessagedefs.h" +#include "javastorage.h" +#include "javastoragenames.h" +#include "logger.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace java::security; +using namespace java::storage; +using namespace java::util; +using namespace std; + +/* The DER encoding of the algorithm ID for the SHA-1 hash function */ +const char SHA_1_ALG_FOOTPRINT[] = + {0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14}; + +/* forward declarations of local/private methods */ +static int verify_callback(int, X509_STORE_CTX *); +int getErrCode(int); +int verifyCertChain(char **, int, const unsigned char *, int, vector CAs, char *, char *, CERT_DETAILS*); + +JNIEXPORT jobjectArray JNICALL Java_com_nokia_mj_impl_security_midp_authentication_AuthenticationModule__1validateChainsAndSignatures +(JNIEnv * env, jobject, jobjectArray authInfos) +{ + // get the roots from JavaCertStore + vector CAs; + JavaCertStoreHandler::retrieveRootsContents(CAs); + if (CAs.size() == 0) + { + SecurityUtils::throw_exception(env, "CONNECTION_TO_CAPTAIN_FAILED"); + return NULL; + } + + // validate each of the chains&signatures and + jint len = env->GetArrayLength(authInfos); + vector all_auth_credentials; + all_auth_credentials.reserve(len); + AUTH_CREDENTIALS * auth_credentials = NULL; + CERT_DETAILS* details = NULL; + AUTH_INFO* authInfo = NULL; + char * jar_hash = NULL; + char * root_hash = NULL; + int validation_result = KDefault; + for (int i=0; icert_chain, authInfo->cert_chain_len, + (const unsigned char *)authInfo->signature, + authInfo->signature_len, CAs, jar_hash, + root_hash, details); + if (chain_verification_result == KCertAndSignatureOk) + { + validation_result = KCertAndSignatureOk; + auth_credentials = new AUTH_CREDENTIALS(); + auth_credentials->jar_hash = new char[2*SHA_1_DIGEST_LEN + 1]; + auth_credentials->root_hash = new char[MD5_DIGEST_LEN + 1]; + memmove(auth_credentials->jar_hash, jar_hash, 2*SHA_1_DIGEST_LEN + 1); + memmove(auth_credentials->root_hash, root_hash, MD5_DIGEST_LEN + 1); + auth_credentials->chain_index = i+1; + auth_credentials->signing_cert = details; + all_auth_credentials.push_back(auth_credentials); + auth_credentials->predefined_domain_category = details->domain_category; + // the domain info is coming shortly + } + else + { + delete details; + details = NULL; + delete[] jar_hash; + jar_hash = NULL; + delete[] root_hash; + root_hash = NULL; + // just record the failure of the chain validation + if (chain_verification_result > validation_result) + { + validation_result = chain_verification_result; + } + } + // release the elements + delete[] authInfo->signature; + authInfo->signature = NULL; + for (int j=0 ; jcert_chain_len; j++) + { + delete[] authInfo->cert_chain[j]; + authInfo->cert_chain[j] = NULL; + } + delete[] authInfo->cert_chain; + authInfo->cert_chain = NULL; + + // free memory + delete authInfo; + authInfo = NULL; + } + // release the CAs + CAs.clear(); + + // analyze the result of authentication + switch (validation_result) + { + case KDefault: + // there were no chains to be validated + return NULL; + case KCertValidationFailure: + SecurityUtils::throw_exception(env, "CERT_VERIFICATION_FAILED"); + return NULL; + case KSignatureVerificationFailure: + SecurityUtils::throw_exception(env, "SIG_VERIFICATION_FAILED"); + return NULL; + case KMissingRoot: + SecurityUtils::throw_exception(env, "MISSING_ROOT"); + return NULL; + case KCertNotYetValidFailure: + SecurityUtils::throw_exception(env, "CERT_NOT_YET_VALID"); + return NULL; + case KCertExpiredFailure: + SecurityUtils::throw_exception(env, "CERT_EXPIRED"); + return NULL; + case KSelfSignedCertInChainFailure: + SecurityUtils::throw_exception(env, "ROOT_CERT_IN_CHAIN"); + return NULL; + case KUnknownExtendedKeyUsage: + SecurityUtils::throw_exception(env, "UNKNOWN_EXT_KEY_USAGE"); + return NULL; + } + + // try to fill in the domain info for each of the existing credentials + int i=0; + int domain_mappings = 0; + while (i < all_auth_credentials.size()) + { + // init the domain name and category + all_auth_credentials[i]->domain_name = NULL; + all_auth_credentials[i]->domain_category = NULL; + std::string protection_domain_name; + std::string protection_domain_category; + JavaCertStoreHandler::retrieveRootProtDomainInfo( + all_auth_credentials[i]->root_hash, + protection_domain_name, + protection_domain_category); + if (strcmp(protection_domain_name.c_str(),"")) + { + // DeveloperCertificates: if domain_category is manufacturer and we have predefined_domain_category use the predefined one + // Get the domain constants from ApplicationInfo&policy + if ((strcmp(protection_domain_category.c_str(),"MFD") == 0 + && all_auth_credentials[i]->predefined_domain_category == DEVCERT_UNKNOWN_DOMAIN) + || (strcmp(protection_domain_category.c_str(),"OPD") == 0 + && all_auth_credentials[i]->predefined_domain_category == DEVCERT_UNKNOWN_DOMAIN)) + { + i++; + continue; + } + if ((strcmp(protection_domain_category.c_str(),"MFD") == 0 + && (all_auth_credentials[i]->predefined_domain_category == DEVCERT_OPERATOR_DOMAIN + || all_auth_credentials[i]->predefined_domain_category == DEVCERT_IDENTIFIEDTHIRDPARTY_DOMAIN)) + || (strcmp(protection_domain_category.c_str(),"OPD") == 0 + && all_auth_credentials[i]->predefined_domain_category == DEVCERT_IDENTIFIEDTHIRDPARTY_DOMAIN)) + { + switch (all_auth_credentials[i]->predefined_domain_category) + { + case DEVCERT_OPERATOR_DOMAIN: + all_auth_credentials[i]->domain_name = new char[strlen("Operator") + 1]; + strcpy(all_auth_credentials[i]->domain_name,"Operator"); + all_auth_credentials[i]->domain_category = new char[strlen("OPD") + 1]; + strcpy(all_auth_credentials[i]->domain_category,"OPD"); + break; + case DEVCERT_IDENTIFIEDTHIRDPARTY_DOMAIN: + all_auth_credentials[i]->domain_name = new char[strlen("IdentifiedThirdParty") + 1]; + strcpy(all_auth_credentials[i]->domain_name,"IdentifiedThirdParty"); + all_auth_credentials[i]->domain_category = new char[strlen("ITPD") + 1]; + strcpy(all_auth_credentials[i]->domain_category,"ITPD"); + break; + } + } + else + { + all_auth_credentials[i]->domain_name = new char[protection_domain_name.size() + 1 /* for the \n */]; + strcpy(all_auth_credentials[i]->domain_name,protection_domain_name.c_str()); + all_auth_credentials[i]->domain_category = new char[protection_domain_category.size() + 1 /* for the \n */]; + strcpy(all_auth_credentials[i]->domain_category,protection_domain_category.c_str()); + } + domain_mappings++; + } + i++; + } + + // if no protection domain was found -> throw corresponding exception + if (domain_mappings == 0) + { + SecurityUtils::throw_exception(env, "MISSING_DOMAIN_MAPPING"); + return NULL; + } + + // send the response + return SecurityUtils::getJNIAuthCredentials(env, all_auth_credentials); +} + +JNIEXPORT jstring JNICALL Java_com_nokia_mj_impl_security_midp_authentication_AuthenticationModule__1computeHash +(JNIEnv * env, jobject, jstring appJARPath) +{ + jboolean isCopy; + const char* app_jar_path(env->GetStringUTFChars(appJARPath, &isCopy)); + char * jar_hash_value = SecurityUtils::computeDigest(app_jar_path); + env->ReleaseStringUTFChars(appJARPath, app_jar_path); + if (jar_hash_value != NULL) + { + jstring hash = env->NewStringUTF(jar_hash_value); + delete[] jar_hash_value; + jar_hash_value = NULL; + return hash; + } + return NULL; +} + +JNIEXPORT jobject JNICALL Java_com_nokia_mj_impl_security_midp_authentication_AuthenticationModule__1parseCertificate +(JNIEnv * env, jobject, jstring rawCertificate) +{ + int len = env->GetStringLength(rawCertificate); + jboolean isCopy; + const char* raw_cert(env->GetStringUTFChars(rawCertificate, &isCopy)); + if (len == 0 || raw_cert == NULL) + { + return NULL; + } + char * encoded_cert = SecurityUtils::encodePEM(raw_cert, len); + if (encoded_cert == NULL) + { + return NULL; + } + env->ReleaseStringUTFChars(rawCertificate, raw_cert); + X509 * x509_cert = SecurityUtils::readCert(encoded_cert, strlen(encoded_cert), PEM); + delete[] encoded_cert; + encoded_cert = NULL; + if (x509_cert != NULL) + { + CERT_DETAILS * details = new CERT_DETAILS(); + SecurityUtils::getCertDetails(*x509_cert, details, false /* don't parse domain info */); + delete x509_cert; + x509_cert = NULL; + jobject cert_details = SecurityUtils::getJNICertDetails(env, *details); + delete[] details->issuer; + details->issuer = NULL; + delete[] details->subject; + details->subject = NULL; + delete[] details->organization; + details->organization = NULL; + delete[] details->notBefore; + details->notBefore = NULL; + delete[] details->notAfter; + details->notAfter = NULL; + delete[] details->serial_number; + details->serial_number = NULL; + delete[] details->fingerprint; + details->fingerprint = NULL; + delete details; + details = NULL; + return cert_details; + } + return NULL; +} + +JNIEXPORT jobject JNICALL Java_com_nokia_mj_impl_security_midp_authentication_AuthenticationModule__1getRootCertificate +(JNIEnv * env, jobject, jstring jRootHash) +{ + // get the certificate content from JavaCertStore + std::wstring rootHash = JniUtils::jstringToWstring(env, jRootHash); + std::string rootContent; + long long len; + JavaCertStoreHandler::retrieveRootContent(rootHash, &len, rootContent); + X509* root = SecurityUtils::readCert((char *)rootContent.c_str(), len , DER); + if (root != NULL) + { + CERT_DETAILS * details = new CERT_DETAILS(); + SecurityUtils::getCertDetails(*root, details, false /* don't parse domain info */); + delete root; + root = NULL; + jobject root_details = SecurityUtils::getJNICertDetails(env, *details); + delete[] details->issuer; + details->issuer = NULL; + delete[] details->subject; + details->subject = NULL; + delete[] details->organization; + details->organization = NULL; + delete[] details->notBefore; + details->notBefore = NULL; + delete[] details->notAfter; + details->notAfter = NULL; + delete[] details->serial_number; + details->serial_number = NULL; + delete[] details->fingerprint; + details->fingerprint = NULL; + delete details; + details = NULL; + return root_details; + } + + // return the prased cert to java + return NULL; +} + +OS_EXPORT void MIDPAuthenticationModuleImpl::getCertChains( + const Uid& aUid, + std::list& aChains) +{ + auto_ptr storageHandler(new StorageHandler()); + list indexes; + storageHandler->readValidCerts(aUid, indexes); + + JavaStorageEntry attr; + JavaStorageApplicationEntry_t entry; + + attr.setEntry(ID, aUid.toString()); + entry.insert(attr); + + auto_ptr js(JavaStorage::createInstance()); + JavaStorageApplicationList_t foundApps; + + try + { + js->open(); + js->search(APPLICATION_PACKAGE_ATTRIBUTES_TABLE, entry, foundApps); + js->close(); + } + catch (JavaStorageException& aJse) + { + ELOG1(EJavaStorage, "CertChains: %s", aJse.toString().c_str()); + } + + entry.clear(); + + list::const_iterator iter; + + for (iter = indexes.begin(); iter != indexes.end(); iter++) + { + + string chain(""); + storageHandler->getChainFromIndex(foundApps, (*iter), chain); + + if (chain.size() > 0) + { + aChains.push_back(chain); + } + } +} + +MIDPAuthenticationModuleImpl::MIDPAuthenticationModuleImpl() +{ +} + +int verifyCertChain(char **cert_chain, int no_certs, + const unsigned char * sig, int sig_len, + vector CAs, char * jar_hash, + char * root_hash, CERT_DETAILS* details) +{ + X509 *end_entity_cert; + X509_STORE_CTX *x509_ctx = NULL; + X509_STORE *x509_store = NULL; + STACK_OF(X509) *validated_certs_st = sk_X509_new_null(); + STACK_OF(X509) *roots_certs_st = sk_X509_new_null(); + RSA * rsakey = NULL; + unsigned char * plainSig = NULL; + int ret_code = KCertAndSignatureOk; + + // Calling OpenSSL_add_all_algorithms() links in all algorithms: as a result a statically linked executable can be quite large. Pick up the supported algorithms only + OpenSSL_add_all_algorithms(); + + // get the end entity certificate + end_entity_cert = SecurityUtils::readCert(cert_chain[0], strlen(cert_chain[0]), PEM); + + while (true) + { + // add certs 1.. into the STACK + for (int i=1; icurrent_issuer)); + // add the '\0' + root_hash[MD5_DIGEST_LEN] = '\0'; + + // 1. get the public key of the signing cert + // 2. decode the provided signature using the signing cert's public key + // 3. parse the digest/decrypted signature + EVP_PKEY *pkey = X509_get_pubkey(end_entity_cert); + rsakey = EVP_PKEY_get1_RSA(pkey); + EVP_PKEY_free(pkey); + plainSig = new unsigned char[RSA_size(rsakey) * 2]; + int digest_len = RSA_public_decrypt(sig_len, sig, plainSig, rsakey, RSA_PKCS1_PADDING); + if (digest_len != (sizeof(SHA_1_ALG_FOOTPRINT) + SHA_1_DIGEST_LEN)) + { + ret_code = KSignatureVerificationFailure; + break; + } + if (memcmp(SHA_1_ALG_FOOTPRINT, + (const char *)plainSig, + sizeof(SHA_1_ALG_FOOTPRINT))) + { + ret_code = KSignatureVerificationFailure; + break; + } + // save the plainSig into jar_hash + char * tmp_jar_hash = NULL; + tmp_jar_hash = jar_hash; + // format it as hex + for (int i=0; i