1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony; 18 19 import static android.preference.PreferenceManager.getDefaultSharedPreferences; 20 import static android.telephony.CarrierConfigManager.KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL; 21 22 import static java.nio.charset.StandardCharsets.UTF_8; 23 24 import android.app.AlarmManager; 25 import android.app.DownloadManager; 26 import android.app.PendingIntent; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.SharedPreferences; 32 import android.database.Cursor; 33 import android.net.Uri; 34 import android.os.Handler; 35 import android.os.Message; 36 import android.os.PersistableBundle; 37 import android.telephony.CarrierConfigManager; 38 import android.telephony.ImsiEncryptionInfo; 39 import android.telephony.SubscriptionManager; 40 import android.telephony.TelephonyManager; 41 import android.text.TextUtils; 42 import android.util.Log; 43 import android.util.Pair; 44 45 import com.android.internal.annotations.VisibleForTesting; 46 47 import org.json.JSONArray; 48 import org.json.JSONException; 49 import org.json.JSONObject; 50 51 import java.io.BufferedReader; 52 import java.io.ByteArrayInputStream; 53 import java.io.FileInputStream; 54 import java.io.IOException; 55 import java.io.InputStream; 56 import java.io.InputStreamReader; 57 import java.security.PublicKey; 58 import java.security.cert.CertificateFactory; 59 import java.security.cert.X509Certificate; 60 import java.util.Date; 61 import java.util.Random; 62 import java.util.zip.GZIPInputStream; 63 64 /** 65 * This class contains logic to get Certificates and keep them current. 66 * The class will be instantiated by various Phone implementations. 67 */ 68 public class CarrierKeyDownloadManager extends Handler { 69 private static final String LOG_TAG = "CarrierKeyDownloadManager"; 70 71 private static final String MCC_MNC_PREF_TAG = "CARRIER_KEY_DM_MCC_MNC"; 72 73 private static final String CERT_BEGIN_STRING = "-----BEGIN CERTIFICATE-----"; 74 75 private static final String CERT_END_STRING = "-----END CERTIFICATE-----"; 76 77 private static final int DAY_IN_MILLIS = 24 * 3600 * 1000; 78 79 // Create a window prior to the key expiration, during which the cert will be 80 // downloaded. Defines the start date of that window. So if the key expires on 81 // Dec 21st, the start of the renewal window will be Dec 1st. 82 private static final int START_RENEWAL_WINDOW_DAYS = 21; 83 84 // This will define the end date of the window. 85 private static final int END_RENEWAL_WINDOW_DAYS = 7; 86 87 88 89 /* Intent for downloading the public key */ 90 private static final String INTENT_KEY_RENEWAL_ALARM_PREFIX = 91 "com.android.internal.telephony.carrier_key_download_alarm"; 92 93 @VisibleForTesting 94 public int mKeyAvailability = 0; 95 96 public static final String MNC = "MNC"; 97 public static final String MCC = "MCC"; 98 private static final String SEPARATOR = ":"; 99 100 private static final String JSON_CERTIFICATE = "certificate"; 101 private static final String JSON_CERTIFICATE_ALTERNATE = "public-key"; 102 private static final String JSON_TYPE = "key-type"; 103 private static final String JSON_IDENTIFIER = "key-identifier"; 104 private static final String JSON_CARRIER_KEYS = "carrier-keys"; 105 private static final String JSON_TYPE_VALUE_WLAN = "WLAN"; 106 private static final String JSON_TYPE_VALUE_EPDG = "EPDG"; 107 108 private static final int EVENT_ALARM_OR_CONFIG_CHANGE = 0; 109 private static final int EVENT_DOWNLOAD_COMPLETE = 1; 110 111 112 private static final int[] CARRIER_KEY_TYPES = {TelephonyManager.KEY_TYPE_EPDG, 113 TelephonyManager.KEY_TYPE_WLAN}; 114 115 private final Phone mPhone; 116 private final Context mContext; 117 public final DownloadManager mDownloadManager; 118 private String mURL; 119 private boolean mAllowedOverMeteredNetwork = false; 120 CarrierKeyDownloadManager(Phone phone)121 public CarrierKeyDownloadManager(Phone phone) { 122 mPhone = phone; 123 mContext = phone.getContext(); 124 IntentFilter filter = new IntentFilter(); 125 filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 126 filter.addAction(DownloadManager.ACTION_DOWNLOAD_COMPLETE); 127 filter.addAction(INTENT_KEY_RENEWAL_ALARM_PREFIX + mPhone.getPhoneId()); 128 filter.addAction(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); 129 mContext.registerReceiver(mBroadcastReceiver, filter, null, phone); 130 mDownloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE); 131 } 132 133 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 134 @Override 135 public void onReceive(Context context, Intent intent) { 136 String action = intent.getAction(); 137 int slotId = mPhone.getPhoneId(); 138 if (action.equals(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId)) { 139 Log.d(LOG_TAG, "Handling key renewal alarm: " + action); 140 sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); 141 } else if (action.equals(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD)) { 142 if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY, 143 SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { 144 Log.d(LOG_TAG, "Handling reset intent: " + action); 145 sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); 146 } 147 } else if (action.equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 148 if (slotId == intent.getIntExtra(PhoneConstants.PHONE_KEY, 149 SubscriptionManager.INVALID_SIM_SLOT_INDEX)) { 150 Log.d(LOG_TAG, "Carrier Config changed: " + action); 151 sendEmptyMessage(EVENT_ALARM_OR_CONFIG_CHANGE); 152 } 153 } else if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) { 154 Log.d(LOG_TAG, "Download Complete"); 155 sendMessage(obtainMessage(EVENT_DOWNLOAD_COMPLETE, 156 intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, 0))); 157 } 158 } 159 }; 160 161 @Override handleMessage(Message msg)162 public void handleMessage (Message msg) { 163 switch (msg.what) { 164 case EVENT_ALARM_OR_CONFIG_CHANGE: 165 handleAlarmOrConfigChange(); 166 break; 167 case EVENT_DOWNLOAD_COMPLETE: 168 long carrierKeyDownloadIdentifier = (long) msg.obj; 169 String mccMnc = getMccMncSetFromPref(); 170 if (isValidDownload(mccMnc)) { 171 onDownloadComplete(carrierKeyDownloadIdentifier, mccMnc); 172 onPostDownloadProcessing(carrierKeyDownloadIdentifier); 173 } 174 break; 175 } 176 } 177 onPostDownloadProcessing(long carrierKeyDownloadIdentifier)178 private void onPostDownloadProcessing(long carrierKeyDownloadIdentifier) { 179 resetRenewalAlarm(); 180 cleanupDownloadPreferences(carrierKeyDownloadIdentifier); 181 } 182 handleAlarmOrConfigChange()183 private void handleAlarmOrConfigChange() { 184 if (carrierUsesKeys()) { 185 if (areCarrierKeysAbsentOrExpiring()) { 186 boolean downloadStartedSuccessfully = downloadKey(); 187 // if the download was attemped, but not started successfully, and if carriers uses 188 // keys, we'll still want to renew the alarms, and try downloading the key a day 189 // later. 190 if (!downloadStartedSuccessfully) { 191 resetRenewalAlarm(); 192 } 193 } else { 194 return; 195 } 196 } else { 197 // delete any existing alarms. 198 cleanupRenewalAlarms(); 199 } 200 } 201 cleanupDownloadPreferences(long carrierKeyDownloadIdentifier)202 private void cleanupDownloadPreferences(long carrierKeyDownloadIdentifier) { 203 Log.d(LOG_TAG, "Cleaning up download preferences: " + carrierKeyDownloadIdentifier); 204 SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); 205 editor.remove(String.valueOf(carrierKeyDownloadIdentifier)); 206 editor.commit(); 207 } 208 cleanupRenewalAlarms()209 private void cleanupRenewalAlarms() { 210 Log.d(LOG_TAG, "Cleaning up existing renewal alarms"); 211 int slotId = mPhone.getPhoneId(); 212 Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); 213 PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, 214 PendingIntent.FLAG_UPDATE_CURRENT); 215 AlarmManager alarmManager = 216 (AlarmManager) mContext.getSystemService(mContext.ALARM_SERVICE); 217 alarmManager.cancel(carrierKeyDownloadIntent); 218 } 219 220 /** 221 * this method returns the date to be used to decide on when to start downloading the key. 222 * from the carrier. 223 **/ 224 @VisibleForTesting getExpirationDate()225 public long getExpirationDate() { 226 long minExpirationDate = Long.MAX_VALUE; 227 for (int key_type : CARRIER_KEY_TYPES) { 228 if (!isKeyEnabled(key_type)) { 229 continue; 230 } 231 ImsiEncryptionInfo imsiEncryptionInfo = 232 mPhone.getCarrierInfoForImsiEncryption(key_type); 233 if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) { 234 if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) { 235 minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime(); 236 } 237 } 238 } 239 240 // if there are no keys, or expiration date is in the past, or within 7 days, then we 241 // set the alarm to run in a day. Else, we'll set the alarm to run 7 days prior to 242 // expiration. 243 if (minExpirationDate == Long.MAX_VALUE || (minExpirationDate 244 < System.currentTimeMillis() + END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS)) { 245 minExpirationDate = System.currentTimeMillis() + DAY_IN_MILLIS; 246 } else { 247 // We don't want all the phones to download the certs simultaneously, so 248 // we pick a random time during the download window to avoid this situation. 249 Random random = new Random(); 250 int max = START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; 251 int min = END_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS; 252 int randomTime = random.nextInt(max - min) + min; 253 minExpirationDate = minExpirationDate - randomTime; 254 } 255 return minExpirationDate; 256 } 257 258 /** 259 * this method resets the alarm. Starts by cleaning up the existing alarms. 260 * We look at the earliest expiration date, and setup an alarms X days prior. 261 * If the expiration date is in the past, we'll setup an alarm to run the next day. This 262 * could happen if the download has failed. 263 **/ 264 @VisibleForTesting resetRenewalAlarm()265 public void resetRenewalAlarm() { 266 cleanupRenewalAlarms(); 267 int slotId = mPhone.getPhoneId(); 268 long minExpirationDate = getExpirationDate(); 269 Log.d(LOG_TAG, "minExpirationDate: " + new Date(minExpirationDate)); 270 final AlarmManager alarmManager = (AlarmManager) mContext.getSystemService( 271 Context.ALARM_SERVICE); 272 Intent intent = new Intent(INTENT_KEY_RENEWAL_ALARM_PREFIX + slotId); 273 PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent, 274 PendingIntent.FLAG_UPDATE_CURRENT); 275 alarmManager.set(AlarmManager.RTC_WAKEUP, minExpirationDate, carrierKeyDownloadIntent); 276 Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time=" 277 + new Date(minExpirationDate)); 278 } 279 getMccMncSetFromPref()280 private String getMccMncSetFromPref() { 281 // check if this is a download that we had created. We do this by checking if the 282 // downloadId is stored in the shared prefs. 283 int slotId = mPhone.getPhoneId(); 284 SharedPreferences preferences = getDefaultSharedPreferences(mContext); 285 return preferences.getString(MCC_MNC_PREF_TAG + slotId, null); 286 } 287 288 /** 289 * Returns the sim operator. 290 **/ 291 @VisibleForTesting getSimOperator()292 public String getSimOperator() { 293 final TelephonyManager telephonyManager = 294 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 295 return telephonyManager.getSimOperator(mPhone.getSubId()); 296 } 297 298 /** 299 * checks if the download was sent by this particular instance. We do this by including the 300 * slot id in the key. If no value is found, we know that the download was not for this 301 * instance of the phone. 302 **/ 303 @VisibleForTesting isValidDownload(String mccMnc)304 public boolean isValidDownload(String mccMnc) { 305 String mccCurrent = ""; 306 String mncCurrent = ""; 307 String mccSource = ""; 308 String mncSource = ""; 309 310 String simOperator = getSimOperator(); 311 if (TextUtils.isEmpty(simOperator) || TextUtils.isEmpty(mccMnc)) { 312 Log.e(LOG_TAG, "simOperator or mcc/mnc is empty"); 313 return false; 314 } 315 316 String[] splitValue = mccMnc.split(SEPARATOR); 317 mccSource = splitValue[0]; 318 mncSource = splitValue[1]; 319 Log.d(LOG_TAG, "values from sharedPrefs mcc, mnc: " + mccSource + "," + mncSource); 320 321 mccCurrent = simOperator.substring(0, 3); 322 mncCurrent = simOperator.substring(3); 323 Log.d(LOG_TAG, "using values for mcc, mnc: " + mccCurrent + "," + mncCurrent); 324 325 if (TextUtils.equals(mncSource, mncCurrent) && TextUtils.equals(mccSource, mccCurrent)) { 326 return true; 327 } 328 return false; 329 } 330 331 /** 332 * This method will try to parse the downloaded information, and persist it in the database. 333 **/ onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc)334 private void onDownloadComplete(long carrierKeyDownloadIdentifier, String mccMnc) { 335 Log.d(LOG_TAG, "onDownloadComplete: " + carrierKeyDownloadIdentifier); 336 String jsonStr; 337 DownloadManager.Query query = new DownloadManager.Query(); 338 query.setFilterById(carrierKeyDownloadIdentifier); 339 Cursor cursor = mDownloadManager.query(query); 340 InputStream source = null; 341 342 if (cursor == null) { 343 return; 344 } 345 if (cursor.moveToFirst()) { 346 int columnIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS); 347 if (DownloadManager.STATUS_SUCCESSFUL == cursor.getInt(columnIndex)) { 348 try { 349 source = new FileInputStream( 350 mDownloadManager.openDownloadedFile(carrierKeyDownloadIdentifier) 351 .getFileDescriptor()); 352 jsonStr = convertToString(source); 353 parseJsonAndPersistKey(jsonStr, mccMnc); 354 } catch (Exception e) { 355 Log.e(LOG_TAG, "Error in download:" + carrierKeyDownloadIdentifier 356 + ". " + e); 357 } finally { 358 mDownloadManager.remove(carrierKeyDownloadIdentifier); 359 try { 360 source.close(); 361 } catch (IOException e) { 362 e.printStackTrace(); 363 } 364 } 365 } 366 Log.d(LOG_TAG, "Completed downloading keys"); 367 } 368 cursor.close(); 369 return; 370 } 371 372 /** 373 * This method checks if the carrier requires key. We'll read the carrier config to make that 374 * determination. 375 * @return boolean returns true if carrier requires keys, else false. 376 **/ carrierUsesKeys()377 private boolean carrierUsesKeys() { 378 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 379 mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE); 380 if (carrierConfigManager == null) { 381 return false; 382 } 383 int subId = mPhone.getSubId(); 384 PersistableBundle b = carrierConfigManager.getConfigForSubId(subId); 385 if (b == null) { 386 return false; 387 } 388 mKeyAvailability = b.getInt(CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT); 389 mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING); 390 mAllowedOverMeteredNetwork = b.getBoolean( 391 KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL); 392 if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) { 393 Log.d(LOG_TAG, "Carrier not enabled or invalid values"); 394 return false; 395 } 396 for (int key_type : CARRIER_KEY_TYPES) { 397 if (isKeyEnabled(key_type)) { 398 return true; 399 } 400 } 401 return false; 402 } 403 convertToString(InputStream is)404 private static String convertToString(InputStream is) { 405 try { 406 // The current implementation at certain Carriers has the data gzipped, which requires 407 // us to unzip the contents. Longer term, we want to add a flag in carrier config which 408 // determines if the data needs to be zipped or not. 409 GZIPInputStream gunzip = new GZIPInputStream(is); 410 BufferedReader reader = new BufferedReader(new InputStreamReader(gunzip, UTF_8)); 411 StringBuilder sb = new StringBuilder(); 412 413 String line; 414 while ((line = reader.readLine()) != null) { 415 sb.append(line).append('\n'); 416 } 417 return sb.toString(); 418 } catch (IOException e) { 419 e.printStackTrace(); 420 } 421 return null; 422 } 423 424 /** 425 * Converts the string into a json object to retreive the nodes. The Json should have 3 nodes, 426 * including the Carrier public key, the key type and the key identifier. Once the nodes have 427 * been extracted, they get persisted to the database. Sample: 428 * "carrier-keys": [ { "certificate": "", 429 * "key-type": "WLAN", 430 * "key-identifier": "" 431 * } ] 432 * @param jsonStr the json string. 433 * @param mccMnc contains the mcc, mnc. 434 */ 435 @VisibleForTesting parseJsonAndPersistKey(String jsonStr, String mccMnc)436 public void parseJsonAndPersistKey(String jsonStr, String mccMnc) { 437 if (TextUtils.isEmpty(jsonStr) || TextUtils.isEmpty(mccMnc)) { 438 Log.e(LOG_TAG, "jsonStr or mcc, mnc: is empty"); 439 return; 440 } 441 try { 442 String mcc = ""; 443 String mnc = ""; 444 String[] splitValue = mccMnc.split(SEPARATOR); 445 mcc = splitValue[0]; 446 mnc = splitValue[1]; 447 JSONObject jsonObj = new JSONObject(jsonStr); 448 JSONArray keys = jsonObj.getJSONArray(JSON_CARRIER_KEYS); 449 for (int i = 0; i < keys.length(); i++) { 450 JSONObject key = keys.getJSONObject(i); 451 // Support both "public-key" and "certificate" String property. 452 String cert = null; 453 if (key.has(JSON_CERTIFICATE)) { 454 cert = key.getString(JSON_CERTIFICATE); 455 } else { 456 cert = key.getString(JSON_CERTIFICATE_ALTERNATE); 457 } 458 // The key-type property is optional, therefore, the default value is WLAN type if 459 // not specified. 460 int type = TelephonyManager.KEY_TYPE_WLAN; 461 if (key.has(JSON_TYPE)) { 462 String typeString = key.getString(JSON_TYPE); 463 if (typeString.equals(JSON_TYPE_VALUE_EPDG)) { 464 type = TelephonyManager.KEY_TYPE_EPDG; 465 } else if (!typeString.equals(JSON_TYPE_VALUE_WLAN)) { 466 Log.e(LOG_TAG, "Invalid key-type specified: " + typeString); 467 } 468 } 469 String identifier = key.getString(JSON_IDENTIFIER); 470 Pair<PublicKey, Long> keyInfo = 471 getKeyInformation(cleanCertString(cert).getBytes()); 472 savePublicKey(keyInfo.first, type, identifier, keyInfo.second, mcc, mnc); 473 } 474 } catch (final JSONException e) { 475 Log.e(LOG_TAG, "Json parsing error: " + e.getMessage()); 476 } catch (final Exception e) { 477 Log.e(LOG_TAG, "Exception getting certificate: " + e); 478 } 479 } 480 481 /** 482 * introspects the mKeyAvailability bitmask 483 * @return true if the digit at position k is 1, else false. 484 */ 485 @VisibleForTesting isKeyEnabled(int keyType)486 public boolean isKeyEnabled(int keyType) { 487 //since keytype has values of 1, 2.... we need to subtract 1 from the keytype. 488 int returnValue = (mKeyAvailability >> (keyType - 1)) & 1; 489 return (returnValue == 1) ? true : false; 490 } 491 492 /** 493 * Checks whether is the keys are absent or close to expiration. Returns true, if either of 494 * those conditions are true. 495 * @return boolean returns true when keys are absent or close to expiration, else false. 496 */ 497 @VisibleForTesting areCarrierKeysAbsentOrExpiring()498 public boolean areCarrierKeysAbsentOrExpiring() { 499 for (int key_type : CARRIER_KEY_TYPES) { 500 if (!isKeyEnabled(key_type)) { 501 continue; 502 } 503 ImsiEncryptionInfo imsiEncryptionInfo = 504 mPhone.getCarrierInfoForImsiEncryption(key_type); 505 if (imsiEncryptionInfo == null) { 506 Log.d(LOG_TAG, "Key not found for: " + key_type); 507 return true; 508 } 509 Date imsiDate = imsiEncryptionInfo.getExpirationTime(); 510 long timeToExpire = imsiDate.getTime() - System.currentTimeMillis(); 511 return (timeToExpire < START_RENEWAL_WINDOW_DAYS * DAY_IN_MILLIS) ? true : false; 512 } 513 return false; 514 } 515 downloadKey()516 private boolean downloadKey() { 517 Log.d(LOG_TAG, "starting download from: " + mURL); 518 String mcc = ""; 519 String mnc = ""; 520 String simOperator = getSimOperator(); 521 522 if (!TextUtils.isEmpty(simOperator)) { 523 mcc = simOperator.substring(0, 3); 524 mnc = simOperator.substring(3); 525 Log.d(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc); 526 } else { 527 Log.e(LOG_TAG, "mcc, mnc: is empty"); 528 return false; 529 } 530 try { 531 DownloadManager.Request request = new DownloadManager.Request(Uri.parse(mURL)); 532 533 // TODO(b/128550341): Implement the logic to minimize using metered network such as 534 // LTE for downloading a certificate. 535 request.setAllowedOverMetered(mAllowedOverMeteredNetwork); 536 request.setVisibleInDownloadsUi(false); 537 request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN); 538 request.addRequestHeader("Accept-Encoding", "gzip"); 539 Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request); 540 SharedPreferences.Editor editor = getDefaultSharedPreferences(mContext).edit(); 541 542 String mccMnc = mcc + SEPARATOR + mnc; 543 int slotId = mPhone.getPhoneId(); 544 Log.d(LOG_TAG, "storing values in sharedpref mcc, mnc, days: " + mcc + "," + mnc 545 + "," + carrierKeyDownloadRequestId); 546 editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc); 547 editor.commit(); 548 } catch (Exception e) { 549 Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL); 550 return false; 551 } 552 return true; 553 } 554 555 /** 556 * Save the public key 557 * @param certificate certificate that contains the public key. 558 * @return Pair containing the Public Key and the expiration date. 559 **/ 560 @VisibleForTesting getKeyInformation(byte[] certificate)561 public static Pair<PublicKey, Long> getKeyInformation(byte[] certificate) throws Exception { 562 InputStream inStream = new ByteArrayInputStream(certificate); 563 CertificateFactory cf = CertificateFactory.getInstance("X.509"); 564 X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream); 565 Pair<PublicKey, Long> keyInformation = 566 new Pair(cert.getPublicKey(), cert.getNotAfter().getTime()); 567 return keyInformation; 568 } 569 570 /** 571 * Save the public key 572 * @param publicKey public key. 573 * @param type key-type. 574 * @param identifier which is an opaque string. 575 * @param expirationDate expiration date of the key. 576 * @param mcc 577 * @param mnc 578 **/ 579 @VisibleForTesting savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate, String mcc, String mnc)580 public void savePublicKey(PublicKey publicKey, int type, String identifier, long expirationDate, 581 String mcc, String mnc) { 582 ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc, type, identifier, 583 publicKey, new Date(expirationDate)); 584 mPhone.setCarrierInfoForImsiEncryption(imsiEncryptionInfo); 585 } 586 587 /** 588 * Remove potential extraneous text in a certificate string 589 * @param cert certificate string 590 * @return Cleaned up version of the certificate string 591 */ 592 @VisibleForTesting cleanCertString(String cert)593 public static String cleanCertString(String cert) { 594 return cert.substring( 595 cert.indexOf(CERT_BEGIN_STRING), 596 cert.indexOf(CERT_END_STRING) + CERT_END_STRING.length()); 597 } 598 } 599