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