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