diff -r e8e63152f320 -r 2a9601315dfc javacommons/security/src/utils/securityutils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javacommons/security/src/utils/securityutils.cpp Mon May 03 12:27:20 2010 +0300 @@ -0,0 +1,591 @@ +/* +* Copyright (c) 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: +* +*/ +#include +#include "securityutils.h" +#include "telutils.h" +#include "fileutils.h" +#include "com_nokia_mj_impl_security_utils_TelUtils.h" + +using namespace java::security; +using namespace std; + +JNIEXPORT jobject JNICALL Java_com_nokia_mj_impl_security_utils_TelUtils__1getNetworkCodes +(JNIEnv * env, jclass) +{ + std::string mcc; + std::string mnc; + TelUtils * telUtils = TelUtils::createInstance(); + telUtils->getNetworkCodes(mcc, mnc); + delete telUtils; + telUtils = NULL; + jclass network_codes_class = env->FindClass( + "com/nokia/mj/impl/security/utils/TelUtils$NetworkCodes"); + jmethodID network_codes_cid = env->GetMethodID(network_codes_class,"", + "(Ljava/lang/String;Ljava/lang/String;)V"); + jstring jmcc = env->NewStringUTF(mcc.c_str()); + jstring jmnc = env->NewStringUTF(mnc.c_str()); + jobject network_codes = env->NewObject( + network_codes_class, + network_codes_cid, + jmcc,jmnc); + return network_codes; +} + +// Checks if all critical extensions are known. If true and developer +// certificates extensions are found as well, it handles them as following: +// - get the list of allowed IMEIs from signing certificate extension +// - get my IMEI +// - check if my IMEI is among the allowed ones +// - get the protection domain binding from signing certificate extension +// - check that protection domain is valid (manufacturer || operator || trusted third party) +// - return the MIDlet to the protection domain +bool SecurityUtils::areAllCriticalExtsKnown(X509 *cert) +{ + char EXT_OID[80]; + X509_EXTENSION *ext; + int extCount = X509_get_ext_count(cert); + for (int i = 1; i<= extCount; i++) + { + ext = X509_get_ext(cert, i); + if (X509_EXTENSION_get_critical(ext) + && !X509_supported_extension(ext)) + { + // check if the extension is known + OBJ_obj2txt( + EXT_OID, + sizeof(EXT_OID), + X509_EXTENSION_get_object(ext), + 1 /* use numerical form */); + if (!strcmp(EXT_OID, DEVCERT_IMEI_LIST_OID) == 0) + { + return false; + } + // compare my IMEI against the list of IMEI from the certificate extension + std::string imei; + TelUtils * telUtils = TelUtils::createInstance(); + telUtils->getImei(imei); + delete telUtils; + telUtils = NULL; + return checkIMEI(ext, imei.c_str()); + } + } + return true; +} + +X509 * SecurityUtils::readCert(const char * cert, int len, int type) +{ + X509 *x = NULL; + BIO *bio; + bio = BIO_new_mem_buf((void *) cert, len); + if ((bio)) + { + BIO_set_close(bio, BIO_CLOSE); + switch (type) + { + case PEM: + x = PEM_read_bio_X509(bio, 0, 0, 0); + break; + case DER: + x = d2i_X509_bio(bio,NULL); + break; + } + BIO_free(bio); + } + return x; +} + +char * SecurityUtils::encodePEM(const char *str, int len) +{ + const char PEM_PREFIX[] = "-----BEGIN CERTIFICATE-----\n"; + const char PEM_SUFFIX[] = "\n-----END CERTIFICATE-----\n"; + + // wrap the lines at 64 characters and add the header and footer + // lines (required by OpenSSL) + // if the length is multiple of 64 don't add the last \n + int prefixLen = sizeof(PEM_PREFIX); + int suffixLen = sizeof(PEM_SUFFIX); + int new_len = len + prefixLen + + suffixLen + (len/64) /* wrap lines */ + + 1 /* the terminating null separator */; + if (len%64 == 0) + { + new_len = new_len - 1; + } + char * encStr = new char[ new_len ]; + // add the prefix + strncpy(encStr, PEM_PREFIX, prefixLen); + // add the actual string + int i=0; + int k = prefixLen - 1; + while (i 0 && len <= SHA_1_HASH_CHUNK_LEN) + { + // do the hash calculation in one go + buf = new unsigned char[len]; + len = fread(buf, sizeof(unsigned char), len, jarFile); + if (ferror(jarFile)) + { + // stop right here + fclose(jarFile); + delete[] buf; + buf = NULL; + delete[] computed_digest; + computed_digest = NULL; + return NULL; + } + fclose(jarFile); + SHA1(buf, len, computed_digest); + delete[] buf; + buf = NULL; + } + else + { + // do the hash calculation in chunks + SHA_CTX c; + SHA1_Init(&c); + buf = new unsigned char[SHA_1_HASH_CHUNK_LEN]; + while ((len = fread(buf, sizeof(unsigned char), SHA_1_HASH_CHUNK_LEN, jarFile)) > 0 + && !feof(jarFile) && !ferror(jarFile)) + { + SHA1_Update(&c, buf, len); + len = 0; + } + if (ferror(jarFile)) + { + // stop right here + delete[] buf; + buf = NULL; + delete[] computed_digest; + computed_digest = NULL; + fclose(jarFile); + return NULL; + } + fclose(jarFile); + // the end of file was reached + if (len > 0) + { + // write the leftovers + SHA1_Update(&c, buf, len); + } + SHA1_Final(computed_digest, &c); + delete[] buf; + buf = NULL; + } + // format it as hex + char * digest = new char[2*SHA_1_DIGEST_LEN + 1]; + char * tmp = digest; + for (int i=0; iFindClass( + "com/nokia/mj/impl/security/common/Certificate"); + jmethodID cert_class_cid = env->GetMethodID(cert_class,"", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); + jstring j_issuer = NULL; + if (strlen(details.issuer) > 0) + { + j_issuer = env->NewStringUTF(details.issuer); + } + jstring j_subject = NULL; + if (strlen(details.subject) > 0) + { + j_subject = env->NewStringUTF(details.subject); + } + jstring j_organization = NULL; + if (strlen(details.organization) > 0) + { + j_organization = env->NewStringUTF(details.organization); + } + jstring j_notAfter = + env->NewStringUTF(details.notAfter); + jstring j_notBefore = + env->NewStringUTF(details.notBefore); + jstring j_serial_number = + env->NewStringUTF(details.serial_number); + jstring j_fingerprint = + env->NewStringUTF(details.fingerprint); + jobject cert_details = env->NewObject( + cert_class, + cert_class_cid, + j_issuer,j_subject,j_organization, j_notBefore,j_notAfter,j_serial_number,j_fingerprint); + return cert_details; +} + +void SecurityUtils::getCertDetails(X509 cert, CERT_DETAILS* details, bool parse_domain_info) +{ + X509_NAME * subject_name = X509_get_subject_name(&cert); + details->issuer = new char[512]; + X509_NAME_oneline(X509_get_issuer_name(&cert),details->issuer,512); + details->subject = new char[512]; + X509_NAME_oneline(subject_name,details->subject,512); + details->organization = new char[512]; + int ret = X509_NAME_get_text_by_NID(subject_name, NID_organizationName, details->organization, 512); + if (ret == -1) + { + details->organization[0] = '\0'; + } + ASN1_GENERALIZEDTIME * gTimeBefore = ASN1_TIME_to_generalizedtime(X509_get_notBefore(&cert), NULL); + details->notBefore = new char[15]; + strncpy(details->notBefore,(const char*)gTimeBefore->data, 14 /* the format is YYYYMMDDHHMMSSZ and we leave the Z out */); + details->notBefore[14] = '\0'; + ASN1_TIME_free(gTimeBefore); + ASN1_GENERALIZEDTIME * gTimeAfter = ASN1_TIME_to_generalizedtime(X509_get_notAfter(&cert), NULL); + details->notAfter = new char[15]; + strncpy(details->notAfter,(const char*)gTimeAfter->data, 14 /* the format is YYMMDDHHMMSSZ and we and we leave the Z out */); + details->notAfter[14] = '\0'; + ASN1_TIME_free(gTimeAfter); + // serial number + ASN1_INTEGER* serial_number = X509_get_serialNumber(&cert); + details->serial_number = new char[10]; + unsigned char * int_serial_number = new unsigned char[20]; + unsigned char * tmp_serial_number = int_serial_number; + int len = i2c_ASN1_INTEGER(serial_number, &tmp_serial_number); + if (len > 0) + { + // format it as hex + sprintf(details->serial_number,"%08lX",int_serial_number); + details->serial_number[8] = '\0'; + } + delete[] int_serial_number; + int_serial_number = NULL; + tmp_serial_number = NULL; + // fingerprint + const EVP_MD * SHA1=EVP_sha1(); + unsigned int n; + unsigned char * digest = new unsigned char[SHA_1_DIGEST_LEN]; + if (X509_digest(&cert,SHA1,digest,&n)) + { + details->fingerprint = new char[50]; + char * tmp_fingerprint = NULL; + tmp_fingerprint = details->fingerprint; + // format it as hex + for (int i=0; ifingerprint[2*n] = '\0'; + tmp_fingerprint = NULL; + } + delete[] digest; + digest = NULL; + // domain info + // initialize the domain info to unknown value + details->domain_category = DEVCERT_ANY_DOMAIN; + if (parse_domain_info) + { + ASN1_OBJECT *imei_list_ext_obj = OBJ_txt2obj(DEVCERT_IMEI_LIST_OID, 1); + if (X509_get_ext_by_OBJ(&cert, imei_list_ext_obj, -1) != -1) + { + details->domain_category = DEVCERT_UNKNOWN_DOMAIN; + } + // the extension carrying the domain info + STACK_OF(POLICYINFO) *policiesInfo; + POLICYINFO *policyInfo; + char POLICY_OID[80]; + if ((policiesInfo = (STACK_OF(POLICYINFO)*)X509_get_ext_d2i(&cert, NID_certificate_policies, NULL, NULL)) != NULL) + { + for (int i=0; ipolicyid, + 1 /* use numerical form */); + if (strcmp(POLICY_OID, DEVCERT_MANUFACTURER_DOMAIN_OID) == 0) + { + details->domain_category = DEVCERT_MANUFACTURER_DOMAIN; + break; + } + else if (strcmp(POLICY_OID, DEVCERT_OPERATOR_DOMAIN_OID) == 0) + { + details->domain_category = DEVCERT_OPERATOR_DOMAIN; + break; + } + else if (strcmp(POLICY_OID, DEVCERT_IDENTIFIEDTHIRDPARTY_DOMAIN_OID) == 0) + { + details->domain_category = DEVCERT_IDENTIFIEDTHIRDPARTY_DOMAIN; + break; + } + } + // cleanup + for (;;) + { + POLICYINFO* policyInfo = sk_POLICYINFO_pop(policiesInfo); + if (!policyInfo) break; + POLICYINFO_free(policyInfo); + } + sk_POLICYINFO_free(policiesInfo); + } + ASN1_OBJECT_free(imei_list_ext_obj); + } +} + +jobjectArray SecurityUtils::getJNIAuthCredentials(JNIEnv * env, vector all_auth_credentials) +{ + jclass auth_credentials_class = env->FindClass( + "com/nokia/mj/impl/security/midp/authentication/Credentials"); + jmethodID cid = env->GetMethodID(auth_credentials_class,"", + "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILcom/nokia/mj/impl/security/common/Certificate;)V"); + + jobjectArray result = env->NewObjectArray(all_auth_credentials.size(), auth_credentials_class, NULL); + if (result != NULL) + { + for (int i=0; isigning_cert)); + delete[] all_auth_credentials[i]->signing_cert->issuer; + all_auth_credentials[i]->signing_cert->issuer = NULL; + delete[] all_auth_credentials[i]->signing_cert->subject; + all_auth_credentials[i]->signing_cert->subject = NULL; + delete[] all_auth_credentials[i]->signing_cert->organization; + all_auth_credentials[i]->signing_cert->organization = NULL; + delete[] all_auth_credentials[i]->signing_cert->notBefore; + all_auth_credentials[i]->signing_cert->notBefore = NULL; + delete[] all_auth_credentials[i]->signing_cert->notAfter; + all_auth_credentials[i]->signing_cert->notAfter = NULL; + delete[] all_auth_credentials[i]->signing_cert->serial_number; + all_auth_credentials[i]->signing_cert->serial_number = NULL; + delete[] all_auth_credentials[i]->signing_cert->fingerprint; + all_auth_credentials[i]->signing_cert->fingerprint = NULL; + delete all_auth_credentials[i]->signing_cert; + all_auth_credentials[i]->signing_cert = NULL; + jstring j_jar_hash_value = + env->NewStringUTF(all_auth_credentials[i]->jar_hash); + jstring j_root_hash_value = + env->NewStringUTF(all_auth_credentials[i]->root_hash); + jstring j_protection_domain_name = NULL; + if (all_auth_credentials[i]->domain_name != NULL) + { + j_protection_domain_name = + env->NewStringUTF(all_auth_credentials[i]->domain_name); + + } + jstring j_protection_domain_category = NULL; + if (all_auth_credentials[i]->domain_category != NULL) + { + j_protection_domain_category = + env->NewStringUTF(all_auth_credentials[i]->domain_category); + } + jobject auth_credentials = env->NewObject( + auth_credentials_class, + cid, + j_protection_domain_name, + j_protection_domain_category, + j_jar_hash_value, + j_root_hash_value, + all_auth_credentials[i]->chain_index, + signing_cert); + delete[] all_auth_credentials[i]->domain_name; + all_auth_credentials[i]->domain_name = NULL; + delete[] all_auth_credentials[i]->domain_category; + all_auth_credentials[i]->domain_category = NULL; + delete[] all_auth_credentials[i]->jar_hash; + all_auth_credentials[i]->jar_hash = NULL; + delete[] all_auth_credentials[i]->root_hash; + all_auth_credentials[i]->root_hash = NULL; + delete all_auth_credentials[i]; + all_auth_credentials[i] = NULL; + env->SetObjectArrayElement(result, i, auth_credentials); + } + } + all_auth_credentials.clear(); + return result; +} + +void SecurityUtils::getAuthInfo(JNIEnv* env, jobjectArray authInfos, int authInfoIndex, AUTH_INFO * authInfo) +{ + jboolean isCopy; + jobject jAuthInfo = env->GetObjectArrayElement(authInfos, authInfoIndex); + jclass authInfoClass = env->GetObjectClass(jAuthInfo); + jmethodID certChainMethod = env->GetMethodID( + authInfoClass,"getCertChain", "()[Ljava/lang/String;"); + jobjectArray certChain = (jobjectArray)env->CallObjectMethod( + jAuthInfo, certChainMethod); + jmethodID signatureMethod = env->GetMethodID( + authInfoClass,"getSignature", "()Ljava/lang/String;"); + jstring signature = (jstring)env->CallObjectMethod( + jAuthInfo, signatureMethod); + authInfo->cert_chain_len = env->GetArrayLength(certChain); + authInfo->cert_chain = new char* [authInfo->cert_chain_len]; + const char * sig(env->GetStringUTFChars(signature, &isCopy)); + int sig_len = env->GetStringLength(signature); + jstring *jcert = new jstring[authInfo->cert_chain_len]; + for (int i=0; icert_chain_len; i++) + { + jcert[i] = (jstring)env->GetObjectArrayElement(certChain, i); + const char *str(env->GetStringUTFChars(jcert[i], &isCopy)); + int len = env->GetStringLength(jcert[i]); + authInfo->cert_chain[i] = SecurityUtils::encodePEM(str, len); + env->ReleaseStringUTFChars(jcert[i],str); + } + // do the base64 decoding + authInfo->signature = new char[sig_len]; + BIO * b64 = BIO_new(BIO_f_base64()); + BIO * mem = BIO_new_mem_buf((char *)sig, sig_len); + if ((NULL == b64) || (NULL == mem)) + { + env->ReleaseStringUTFChars(signature,sig); + delete[] jcert; + jcert = NULL; + } + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + BIO_set_close(b64, BIO_CLOSE); + BIO_set_close(mem, BIO_CLOSE); + mem = BIO_push(b64, mem); + authInfo->signature_len = BIO_read(mem, authInfo->signature, sig_len); + BIO_free_all(mem); + env->ReleaseStringUTFChars(signature,sig); + delete[] jcert; + jcert = NULL; +} + + +void SecurityUtils::throw_exception(JNIEnv* env, const char * name) +{ + jclass excClass = env->FindClass("com/nokia/mj/impl/security/midp/authentication/AuthenticationException"); + jmethodID excCid = env->GetMethodID(excClass,"", "(I)V"); + jfieldID errCodeID = env->GetStaticFieldID(excClass, name, "I"); + jthrowable exc = (jthrowable)env->NewObject(excClass, excCid, env->GetStaticIntField(excClass, errCodeID)); + env->Throw(exc); + env->DeleteLocalRef(excClass); +} + + +// The list of IMEIs is encoded according to Distinguished Encoding Rule (DER) +// of Abstract Syntax Notation One (ASN.1) syntax encapsulating a sequence of +// UTF-8 strings. The exact syntax is: +// sequence_tag length_tag total_imei_length list_of_imeis +// where: +// sequence_tag = 0x30 +// length_tag = 0x82 (saying that the length is represented on 2 bytes) +// total_imei_length = hexavalue of (number of imeis multiplied by 17) represented on 2 bytes +// list_of_imei = zero or more if these elements (utf8string_tag imei_length imei_hexvalue) +// utf8string_tag = 0x0C +// imei_length = hexavalue of length of imei represented on 1 byte (0x0F) +// imei_hexavalue = array of UTF-8 representation of each of the IMEI digit +bool SecurityUtils::checkIMEI(const X509_EXTENSION * ext, const char * IMEI) +{ + int SEQUENCE_TAG = 0x30; + int LENGTH_TAG = 0x82; + int UTF8STRING_TAG = 0x0C; + int IMEI_LENGTH = 0x0F; + if (ext == NULL + || ext->value == NULL + || ext->value->data == NULL + || ext->value->length <= 4 /* at least sequence_tag length_tag totam_imei_length */) + { + return false; + } + unsigned char *imei_list = ext->value->data; + int len = ext->value->length; + if (imei_list[0] == SEQUENCE_TAG + && imei_list[1] == LENGTH_TAG) + { + long total_imei_length = (((long)imei_list[2] << 8) | ((long)imei_list[3])); + // sanity check + if (total_imei_length == len - 4) + { + int imei_index = 4; + while (imei_index + 17 <= len + && imei_list[imei_index] == UTF8STRING_TAG + && imei_list[++imei_index] == IMEI_LENGTH) + { + imei_index++; + bool found = false; + for (int i=0; i