| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.internal.telephony; |
| |
| import static android.preference.PreferenceManager.getDefaultSharedPreferences; |
| import static android.telephony.CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL; |
| |
| import static java.nio.charset.StandardCharsets.UTF_8; |
| |
| import android.app.AlarmManager; |
| import android.app.DownloadManager; |
| import android.app.PendingIntent; |
| import android.content.BroadcastReceiver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.IntentFilter; |
| import android.content.SharedPreferences; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.PersistableBundle; |
| import android.telephony.CarrierConfigManager; |
| import android.telephony.ImsiEncryptionInfo; |
| import android.telephony.SubscriptionManager; |
| import android.telephony.TelephonyManager; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import org.json.JSONArray; |
| import org.json.JSONException; |
| import org.json.JSONObject; |
| |
| import java.io.BufferedReader; |
| import java.io.ByteArrayInputStream; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.security.PublicKey; |
| import java.security.cert.CertificateFactory; |
| import java.security.cert.X509Certificate; |
| import java.util.Date; |
| import java.util.Random; |
| import java.util.zip.GZIPInputStream; |
| |
| /** |
| * This class contains logic to get Certificates and keep them current. |
| * The class will be instantiated by various Phone implementations. |
| */ |
| public class CarrierKeyDownloadManager extends Handler { |
| private static final String LOG_TAG = "CarrierKeyDownloadManager"; |
| |
| private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC"; |
| |
| private static final String CERT_BEGIN_STRING = "-----BEGIN CERTIFICATE-----"; |
| |
| private static final String CERT_END_STRING = "-----END CERTIFICATE-----"; |
| |
| private static final int DAY_IN_MILLIS = 24 * 3600 * 1000; |
| |
| // Create a window prior to the key expiration, during which the cert will be |
| // downloaded. Defines the start date of that window. So if the key expires on |
| // Dec 21st, the start of the renewal window will be Dec 1st. |
| private static final int START_RENEWAL_WINDOW_DAYS = 21; |
| |
| // This will define the end date of the window. |
| private static final int END_RENEWAL_WINDOW_DAYS = 7; |
| |
| |
| |
| /* Intent for downloading the public key */ |
| private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX = |
| "com.android.internal.telephony.carrier_key_download_alarm"; |
| |
| @VisibleForTesting |
| public int mKeyAvailability = 0; |
| |
| public static final String MNC = "MNC"; |
| public static final String MCC = "MCC"; |
| private static final String SEPARATOR = ":"; |
| |
| private static final String JSON_CERTIFICATE = "certificate"; |
| private static final String JSON_CERTIFICATE_ALTERNATE = "public-key"; |
| private static final String JSON_TYPE = "key-type"; |
| private static final String JSON_IDENTIFIER = "key-identifier"; |
| private static final String JSON_CARRIER_KEYS = "carrier-keys"; |
| private static final String JSON_TYPE_VALUE_WLAN = "WLAN"; |
| private static final String JSON_TYPE_VALUE_EPDG = "EPDG"; |
| |
| private static final int EVENT_ALARM_OR_CONFIG_CHANGE = 0; |
| private static final int EVENT_DOWNLOAD_COMPLETE = 1; |
| |
| |
| private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG, |
| TelephonyManager.KEY_TYPE_WLAN}; |
| |
| private final Phone mPhone; |
| private final Context mContext; |
| public final DownloadManager mDownloadManager; |
| private String mURL; |
| private boolean mAllowedOverMeteredNetwork = false; |
| |
| public CarrierKeyDownloadManager(Phone phone) { |
| mPhone = phone; |
| mContext = phone.getContext(); |
| IntentFilter filter = new IntentFilter(); |
| filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); |
| filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE); |
| filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId()); |
| filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); |
| mContext.registerReceiver(mBroadcastReceiver, filter, null, phone); |
| mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); |
| } |
| |
| private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { |
| @Override |
| public void onReceive(Context context, Intent intent) { |
| String action = intent.getAction(); |
| int slotId = mPhone.getPhoneId(); |
| if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) { |
| Log.d(LOG_TAG, "Handling key renewal alarm: " + action); |
| sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); |
| } else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) { |
| if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY, |
| SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { |
| Log.d(LOG_TAG, "Handling reset intent: " + action); |
| sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); |
| } |
| } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { |
| if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY, |
| SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { |
| Log.d(LOG_TAG, "Carrier Config changed: " + action); |
| sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); |
| } |
| } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { |
| Log.d(LOG_TAG, "Download Complete"); |
| sendMessage(obtainMessage(EVENT_DOWNLOAD_COMPLETE, |
| intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0))); |
| } |
| } |
| }; |
| |
| @Override |
| public void handleMessage (Message msg) { |
| switch (msg.what) { |
| case EVENT_ALARM_OR_CONFIG_CHANGE: |
| handleAlarmOrConfigChange(); |
| break; |
| case EVENT_DOWNLOAD_COMPLETE: |
| long carrierKeyDownloadIdentifier = (long) msg.obj; |
| String mccMnc = getMccMncSetFromPref(); |
| if (isValidDownload(mccMnc)) { |
| onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc); |
| onPostDownloadProcessing(carrierKeyDownloadIdentifier); |
| } |
| break; |
| } |
| } |
| |
| private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) { |
| resetRenewalAlarm(); |
| cleanupDownloadPreferences(carrierKeyDownloadIdentifier); |
| } |
| |
| private void handleAlarmOrConfigChange() { |
| if (carrierUsesKeys()) { |
| if (areCarrierKeysAbsentOrExpiring()) { |
| boolean downloadStartedSuccessfully = downloadKey(); |
| // if the download was attemped, but not started successfully, and if carriers uses |
| // keys, we'll still want to renew the alarms, and try downloading the key a day |
| // later. |
| if (!downloadStartedSuccessfully) { |
| resetRenewalAlarm(); |
| } |
| } else { |
| return; |
| } |
| } else { |
| // delete any existing alarms. |
| cleanupRenewalAlarms(); |
| } |
| } |
| |
| private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) { |
| Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier); |
| SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); |
| editor.remove(String.valueOf(carrierKeyDownloadIdentifier)); |
| editor.commit(); |
| } |
| |
| private void cleanupRenewalAlarms() { |
| Log.d(LOG_TAG, "Cleaning up existing renewal alarms"); |
| int slotId = mPhone.getPhoneId(); |
| Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); |
| PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, |
| PendingIntent.FLAG_UPDATE_CURRENT); |
| AlarmManager alarmManager = |
| (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE); |
| alarmManager.cancel(carrierKeyDownloadIntent); |
| } |
| |
| /** |
| * this method returns the date to be used to decide on when to start downloading the key. |
| * from the carrier. |
| **/ |
| @VisibleForTesting |
| public long getExpirationDate() { |
| long minExpirationDate = Long.MAX_VALUE; |
| for (int key_type : CARRIER_KEY_TYPES) { |
| if (!isKeyEnabled(key_type)) { |
| continue; |
| } |
| ImsiEncryptionInfo imsiEncryptionInfo = |
| mPhone.getCarrierInfoForImsiEncryption(key_type); |
| if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) { |
| if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) { |
| minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime(); |
| } |
| } |
| } |
| |
| // if there are no keys, or expiration date is in the past, or within 7 days, then we |
| // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to |
| // expiration. |
| if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate |
| < System.currentTimeMillis() + END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) { |
| minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS; |
| } else { |
| // We don't want all the phones to download the certs simultaneously, so |
| // we pick a random time during the download window to avoid this situation. |
| Random random = new Random(); |
| int max = START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; |
| int min = END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; |
| int randomTime = random.nextInt(max - min) + min; |
| minExpirationDate = minExpirationDate - randomTime; |
| } |
| return minExpirationDate; |
| } |
| |
| /** |
| * this method resets the alarm. Starts by cleaning up the existing alarms. |
| * We look at the earliest expiration date, and setup an alarms X days prior. |
| * If the expiration date is in the past, we'll setup an alarm to run the next day. This |
| * could happen if the download has failed. |
| **/ |
| @VisibleForTesting |
| public void resetRenewalAlarm() { |
| cleanupRenewalAlarms(); |
| int slotId = mPhone.getPhoneId(); |
| long minExpirationDate = getExpirationDate(); |
| Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate)); |
| final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( |
| Context.ALARM_SERVICE); |
| Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); |
| PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, |
| PendingIntent.FLAG_UPDATE_CURRENT); |
| alarmManager.set(AlarmManager.RTC_WAKEUP, minExpirationDate, carrierKeyDownloadIntent); |
| Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time=" |
| + new Date(minExpirationDate)); |
| } |
| |
| private String getMccMncSetFromPref() { |
| // check if this is a download that we had created. We do this by checking if the |
| // downloadId is stored in the shared prefs. |
| int slotId = mPhone.getPhoneId(); |
| SharedPreferences preferences = getDefaultSharedPreferences(mContext); |
| return preferences.getString(MCC_MNC_PREF_TAG + slotId, null); |
| } |
| |
| /** |
| * Returns the sim operator. |
| **/ |
| @VisibleForTesting |
| public String getSimOperator() { |
| final TelephonyManager telephonyManager = |
| (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); |
| return telephonyManager.getSimOperator(mPhone.getSubId()); |
| } |
| |
| /** |
| * checks if the download was sent by this particular instance. We do this by including the |
| * slot id in the key. If no value is found, we know that the download was not for this |
| * instance of the phone. |
| **/ |
| @VisibleForTesting |
| public boolean isValidDownload(String mccMnc) { |
| String mccCurrent = ""; |
| String mncCurrent = ""; |
| String mccSource = ""; |
| String mncSource = ""; |
| |
| String simOperator = getSimOperator(); |
| if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) { |
| Log.e(LOG_TAG, "simOperator or mcc/mnc is empty"); |
| return false; |
| } |
| |
| String[] splitValue = mccMnc.split(SEPARATOR); |
| mccSource = splitValue[0]; |
| mncSource = splitValue[1]; |
| Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource); |
| |
| mccCurrent = simOperator.substring(0, 3); |
| mncCurrent = simOperator.substring(3); |
| Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent); |
| |
| if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * This method will try to parse the downloaded information, and persist it in the database. |
| **/ |
| private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc) { |
| Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier); |
| String jsonStr; |
| DownloadManager.Query query = new DownloadManager.Query(); |
| query.setFilterById(carrierKeyDownloadIdentifier); |
| Cursor cursor = mDownloadManager.query(query); |
| InputStream source = null; |
| |
| if (cursor == null) { |
| return; |
| } |
| if (cursor.moveToFirst()) { |
| int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); |
| if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) { |
| try { |
| source = new FileInputStream( |
| mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier) |
| .getFileDescriptor()); |
| jsonStr = convertToString(source); |
| parseJsonAndPersistKey(jsonStr, mccMnc); |
| } catch (Exception e) { |
| Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier |
| + ". " + e); |
| } finally { |
| mDownloadManager.remove(carrierKeyDownloadIdentifier); |
| try { |
| source.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
| Log.d(LOG_TAG, "Completed downloading keys"); |
| } |
| cursor.close(); |
| return; |
| } |
| |
| /** |
| * This method checks if the carrier requires key. We'll read the carrier config to make that |
| * determination. |
| * @return boolean returns true if carrier requires keys, else false. |
| **/ |
| private boolean carrierUsesKeys() { |
| CarrierConfigManager carrierConfigManager = (CarrierConfigManager) |
| mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); |
| if (carrierConfigManager == null) { |
| return false; |
| } |
| int subId = mPhone.getSubId(); |
| PersistableBundle b = carrierConfigManager.getConfigForSubId(subId); |
| if (b == null) { |
| return false; |
| } |
| mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); |
| mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING); |
| mAllowedOverMeteredNetwork = b.getBoolean( |
| KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); |
| if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) { |
| Log.d(LOG_TAG, "Carrier not enabled or invalid values"); |
| return false; |
| } |
| for (int key_type : CARRIER_KEY_TYPES) { |
| if (isKeyEnabled(key_type)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static String convertToString(InputStream is) { |
| try { |
| // The current implementation at certain Carriers has the data gzipped, which requires |
| // us to unzip the contents. Longer term, we want to add a flag in carrier config which |
| // determines if the data needs to be zipped or not. |
| GZIPInputStream gunzip = new GZIPInputStream(is); |
| BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8)); |
| StringBuilder sb = new StringBuilder(); |
| |
| String line; |
| while ((line = reader.readLine()) != null) { |
| sb.append(line).append('\n'); |
| } |
| return sb.toString(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| return null; |
| } |
| |
| /** |
| * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes, |
| * including the Carrier public key, the key type and the key identifier. Once the nodes have |
| * been extracted, they get persisted to the database. Sample: |
| * "carrier-keys": [ { "certificate": "", |
| * "key-type": "WLAN", |
| * "key-identifier": "" |
| * } ] |
| * @param jsonStr the json string. |
| * @param mccMnc contains the mcc, mnc. |
| */ |
| @VisibleForTesting |
| public void parseJsonAndPersistKey(String jsonStr, String mccMnc) { |
| if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) { |
| Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty"); |
| return; |
| } |
| try { |
| String mcc = ""; |
| String mnc = ""; |
| String[] splitValue = mccMnc.split(SEPARATOR); |
| mcc = splitValue[0]; |
| mnc = splitValue[1]; |
| JSONObject jsonObj = new JSONObject(jsonStr); |
| JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS); |
| for (int i = 0; i < keys.length(); i++) { |
| JSONObject key = keys.getJSONObject(i); |
| // Support both "public-key" and "certificate" String property. |
| // "certificate" is a more accurate description, however, the 3GPP draft spec |
| // S3-170116, "Privacy Protection for EAP-AKA" section 4.3 mandates the use of |
| // "public-key". |
| String cert = null; |
| if (key.has(JSON_CERTIFICATE)) { |
| cert = key.getString(JSON_CERTIFICATE); |
| } else { |
| cert = key.getString(JSON_CERTIFICATE_ALTERNATE); |
| } |
| // The 3GPP draft spec 3GPP draft spec S3-170116, "Privacy Protection for EAP-AKA" |
| // section 4.3, does not specify any key-type property. To be compatible with these |
| // networks, the logic defaults to WLAN type if not specified. |
| int type = TelephonyManager.KEY_TYPE_WLAN; |
| if (key.has(JSON_TYPE)) { |
| String typeString = key.getString(JSON_TYPE); |
| if (typeString.equals(JSON_TYPE_VALUE_EPDG)) { |
| type = TelephonyManager.KEY_TYPE_EPDG; |
| } else if (!typeString.equals(JSON_TYPE_VALUE_WLAN)) { |
| Log.e(LOG_TAG, "Invalid key-type specified: " + typeString); |
| } |
| } |
| String identifier = key.getString(JSON_IDENTIFIER); |
| Pair<PublicKey, Long> keyInfo = |
| getKeyInformation(cleanCertString(cert).getBytes()); |
| savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc); |
| } |
| } catch (final JSONException e) { |
| Log.e(LOG_TAG, "Json parsing error: " + e.getMessage()); |
| } catch (final Exception e) { |
| Log.e(LOG_TAG, "Exception getting certificate: " + e); |
| } |
| } |
| |
| /** |
| * introspects the mKeyAvailability bitmask |
| * @return true if the digit at position k is 1, else false. |
| */ |
| @VisibleForTesting |
| public boolean isKeyEnabled(int keyType) { |
| //since keytype has values of 1, 2.... we need to subtract 1 from the keytype. |
| int returnValue = (mKeyAvailability >> (keyType - 1)) & 1; |
| return (returnValue == 1) ? true : false; |
| } |
| |
| /** |
| * Checks whether is the keys are absent or close to expiration. Returns true, if either of |
| * those conditions are true. |
| * @return boolean returns true when keys are absent or close to expiration, else false. |
| */ |
| @VisibleForTesting |
| public boolean areCarrierKeysAbsentOrExpiring() { |
| for (int key_type : CARRIER_KEY_TYPES) { |
| if (!isKeyEnabled(key_type)) { |
| continue; |
| } |
| ImsiEncryptionInfo imsiEncryptionInfo = |
| mPhone.getCarrierInfoForImsiEncryption(key_type); |
| if (imsiEncryptionInfo == null) { |
| Log.d(LOG_TAG, "Key not found for: " + key_type); |
| return true; |
| } |
| Date imsiDate = imsiEncryptionInfo.getExpirationTime(); |
| long timeToExpire = imsiDate.getTime() - System.currentTimeMillis(); |
| return (timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false; |
| } |
| return false; |
| } |
| |
| private boolean downloadKey() { |
| Log.d(LOG_TAG, "starting download from: " + mURL); |
| String mcc = ""; |
| String mnc = ""; |
| String simOperator = getSimOperator(); |
| |
| if (!TextUtils.isEmpty(simOperator)) { |
| mcc = simOperator.substring(0, 3); |
| mnc = simOperator.substring(3); |
| Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc); |
| } else { |
| Log.e(LOG_TAG, "mcc, mnc: is empty"); |
| return false; |
| } |
| try { |
| DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL)); |
| |
| // TODO(b/128550341): Implement the logic to minimize using metered network such as |
| // LTE for downloading a certificate. |
| request.setAllowedOverMetered(mAllowedOverMeteredNetwork); |
| request.setVisibleInDownloadsUi(false); |
| request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); |
| Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request); |
| SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); |
| |
| String mccMnc = mcc + SEPARATOR + mnc; |
| int slotId = mPhone.getPhoneId(); |
| Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc |
| + "," + carrierKeyDownloadRequestId); |
| editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc); |
| editor.commit(); |
| } catch (Exception e) { |
| Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Save the public key |
| * @param certificate certificate that contains the public key. |
| * @return Pair containing the Public Key and the expiration date. |
| **/ |
| @VisibleForTesting |
| public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception { |
| InputStream inStream = new ByteArrayInputStream(certificate); |
| CertificateFactory cf = CertificateFactory.getInstance("X.509"); |
| X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); |
| Pair<PublicKey, Long> keyInformation = |
| new Pair(cert.getPublicKey(), cert.getNotAfter().getTime()); |
| return keyInformation; |
| } |
| |
| /** |
| * Save the public key |
| * @param publicKey public key. |
| * @param type key-type. |
| * @param identifier which is an opaque string. |
| * @param expirationDate expiration date of the key. |
| * @param mcc |
| * @param mnc |
| **/ |
| @VisibleForTesting |
| public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate, |
| String mcc, String mnc) { |
| ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier, |
| publicKey, new Date(expirationDate)); |
| mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); |
| } |
| |
| /** |
| * Remove potential extraneous text in a certificate string |
| * @param cert certificate string |
| * @return Cleaned up version of the certificate string |
| */ |
| @VisibleForTesting |
| public static String cleanCertString(String cert) { |
| return cert.substring( |
| cert.indexOf(CERT_BEGIN_STRING), |
| cert.indexOf(CERT_END_STRING) + CERT_END_STRING.length()); |
| } |
| } |