--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/javacommons/security/src/utils/securityutils.cpp Tue Apr 27 16:30:29 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 <string.h>
+#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,"<init>",
+ "(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<len)
+ {
+ encStr[k] = str[i];
+ k++;
+ if ((i + 1)%64 == 0 && i != (len - 1))
+ {
+ // insert a \n
+ encStr[k] = '\n';
+ k++;
+ }
+ i++;
+ }
+ encStr[k] = '\0';
+ // add the suffix
+ strcat(encStr, PEM_SUFFIX);
+ // add the null terminator
+ encStr[new_len - 1] = '\0';
+ return encStr;
+}
+
+char * SecurityUtils::computeDigest1(const char* jarFileName)
+{
+ FILE *jarFile;
+ jarFile = fopen(jarFileName, "r");
+ if (jarFile == NULL)
+ {
+ return NULL;
+ }
+ // figure out the len of the file
+ unsigned long len;
+ fseek(jarFile, 0L, SEEK_END);
+ len = ftell(jarFile);
+ fseek(jarFile, 0L, SEEK_SET);
+ // if the size of the file is less than the size of the chunks,
+ // then do the hash calculation in on go
+ unsigned char * computed_digest = new unsigned char[SHA_1_DIGEST_LEN];
+ unsigned char * buf = NULL;
+ if (len > 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; i<SHA_1_DIGEST_LEN; i++)
+ {
+ sprintf(tmp, "%02X", computed_digest[i]);
+ tmp = tmp + 2;
+ }
+ delete[] computed_digest;
+ computed_digest = NULL;
+ digest[2*SHA_1_DIGEST_LEN] = '\0';
+ tmp = NULL;
+ return digest;
+}
+
+char * SecurityUtils::computeDigest(const char* jarFileName)
+{
+ char * digest = SecurityUtils::computeDigest1(jarFileName);
+ if (digest == NULL)
+ {
+ // one more try
+ digest = FileUtils::computeDigest(jarFileName);
+ }
+ return digest;
+}
+
+jobject SecurityUtils::getJNICertDetails(JNIEnv * env, const CERT_DETAILS details)
+{
+ jclass cert_class = env->FindClass(
+ "com/nokia/mj/impl/security/common/Certificate");
+ jmethodID cert_class_cid = env->GetMethodID(cert_class,"<init>",
+ "(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; i<n; i++)
+ {
+ sprintf(tmp_fingerprint, "%02X", digest[i]);
+ tmp_fingerprint = tmp_fingerprint + 2;
+ }
+ details->fingerprint[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; i<sk_POLICYINFO_num(policiesInfo); i++)
+ {
+ policyInfo = sk_POLICYINFO_value(policiesInfo, i);
+ OBJ_obj2txt(POLICY_OID, sizeof(POLICY_OID),
+ policyInfo->policyid,
+ 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<AUTH_CREDENTIALS*> all_auth_credentials)
+{
+ jclass auth_credentials_class = env->FindClass(
+ "com/nokia/mj/impl/security/midp/authentication/Credentials");
+ jmethodID cid = env->GetMethodID(auth_credentials_class,"<init>",
+ "(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; i<all_auth_credentials.size(); i++)
+ {
+ jobject signing_cert = SecurityUtils::getJNICertDetails(env, *(all_auth_credentials[i]->signing_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; i<authInfo->cert_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,"<init>", "(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<strlen(IMEI); i++)
+ {
+ if (imei_list[imei_index] == '*'
+ || imei_list[imei_index] == IMEI[i])
+ {
+ found = true;
+ }
+ else
+ {
+ // go to the next IMEI
+ found = false;
+ imei_index = imei_index + strlen(IMEI) - i;
+ break;
+ }
+ imei_index++;
+ }
+ if (found)
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}