Revision 9e69afe0
res/layout/activity_main.xml | ||
---|---|---|
1 |
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2 | 2 |
xmlns:tools="http://schemas.android.com/tools" |
3 |
android:layout_width="match_parent" |
|
4 |
android:layout_height="match_parent" > |
|
3 |
android:layout_width="fill_parent" |
|
4 |
android:layout_height="fill_parent" |
|
5 |
android:orientation="vertical" > |
|
6 |
|
|
7 |
<TableLayout |
|
8 |
android:layout_width="match_parent" |
|
9 |
android:layout_height="wrap_content" |
|
10 |
android:paddingBottom="5dip" |
|
11 |
android:paddingTop="5dip" > |
|
12 |
|
|
13 |
<TableRow |
|
14 |
android:layout_width="wrap_content" |
|
15 |
android:layout_height="wrap_content" > |
|
16 |
|
|
17 |
<TextView |
|
18 |
android:layout_width="wrap_content" |
|
19 |
android:layout_height="wrap_content" |
|
20 |
android:layout_gravity="right|center_vertical" |
|
21 |
android:text="SubjectDN:" |
|
22 |
android:typeface="monospace" /> |
|
23 |
|
|
24 |
<TextView |
|
25 |
android:id="@+id/subjectdn" |
|
26 |
android:layout_width="wrap_content" |
|
27 |
android:layout_height="wrap_content" /> |
|
28 |
</TableRow> |
|
29 |
|
|
30 |
<TableRow |
|
31 |
android:layout_width="wrap_content" |
|
32 |
android:layout_height="wrap_content" > |
|
33 |
|
|
34 |
<TextView |
|
35 |
android:layout_width="wrap_content" |
|
36 |
android:layout_height="wrap_content" |
|
37 |
android:layout_gravity="right|center_vertical" |
|
38 |
android:text="IssuerDN:" |
|
39 |
android:typeface="monospace" /> |
|
40 |
|
|
41 |
<TextView |
|
42 |
android:id="@+id/issuerdn" |
|
43 |
android:layout_width="wrap_content" |
|
44 |
android:layout_height="wrap_content" /> |
|
45 |
</TableRow> |
|
46 |
</TableLayout> |
|
5 | 47 |
|
6 | 48 |
<fragment |
7 | 49 |
android:id="@+id/fragment_app_list" |
50 |
android:name="info.guardianproject.checkey.MainActivity$AppListFragment" |
|
8 | 51 |
android:layout_width="fill_parent" |
9 |
android:layout_height="fill_parent" |
|
10 |
class="info.guardianproject.checkey.AppListFragment" /> |
|
52 |
android:layout_height="wrap_content" /> |
|
11 | 53 |
|
12 |
</RelativeLayout> |
|
54 |
</LinearLayout> |
res/menu/context.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8"?> |
|
2 |
<menu xmlns:android="http://schemas.android.com/apk/res/android" > |
|
3 |
|
|
4 |
<item |
|
5 |
android:id="@+id/save" |
|
6 |
android:icon="@android:drawable/ic_menu_save" |
|
7 |
android:showAsAction="ifRoom|withText" |
|
8 |
android:title="@string/save"/> |
|
9 |
<item |
|
10 |
android:id="@+id/virustotal" |
|
11 |
android:icon="@drawable/virustotal" |
|
12 |
android:showAsAction="ifRoom|withText" |
|
13 |
android:title="@string/virustotal"/> |
|
14 |
<item |
|
15 |
android:id="@+id/by_apk_hash" |
|
16 |
android:showAsAction="ifRoom|withText" |
|
17 |
android:title="@string/by_apk_hash"/> |
|
18 |
<item |
|
19 |
android:id="@+id/by_package_name" |
|
20 |
android:showAsAction="ifRoom|withText" |
|
21 |
android:title="@string/by_package_name"/> |
|
22 |
<item |
|
23 |
android:id="@+id/by_signing_certificate" |
|
24 |
android:showAsAction="ifRoom|withText" |
|
25 |
android:title="@string/by_signing_certificate"/> |
|
26 |
|
|
27 |
</menu> |
res/menu/main.xml | ||
---|---|---|
4 | 4 |
tools:context="info.guardianproject.checkey.MainActivity" > |
5 | 5 |
|
6 | 6 |
<item |
7 |
android:id="@+id/save" |
|
8 |
android:icon="@android:drawable/ic_menu_save" |
|
9 |
android:showAsAction="ifRoom|withText" |
|
10 |
android:title="@string/save"/> |
|
11 |
<item |
|
12 |
android:id="@+id/virustotal" |
|
13 |
android:icon="@drawable/virustotal" |
|
14 |
android:showAsAction="ifRoom|withText" |
|
15 |
android:title="@string/virustotal"/> |
|
16 |
<item |
|
17 |
android:id="@+id/by_apk_hash" |
|
18 |
android:showAsAction="ifRoom|withText" |
|
19 |
android:title="@string/by_apk_hash"/> |
|
20 |
<item |
|
21 |
android:id="@+id/by_package_name" |
|
22 |
android:showAsAction="ifRoom|withText" |
|
23 |
android:title="@string/by_package_name"/> |
|
24 |
<item |
|
25 |
android:id="@+id/by_signing_certificate" |
|
26 |
android:showAsAction="ifRoom|withText" |
|
27 |
android:title="@string/by_signing_certificate"/> |
|
28 |
<item |
|
7 | 29 |
android:id="@+id/action_settings" |
8 | 30 |
android:icon="@android:drawable/ic_menu_preferences" |
9 | 31 |
android:orderInCategory="100" |
src/info/guardianproject/checkey/AppListFragment.java | ||
---|---|---|
1 |
/* |
|
2 |
Licensed under the Apache License, Version 2.0 (the "License"); |
|
3 |
you may not use this file except in compliance with the License. |
|
4 |
You may obtain a copy of the License at |
|
5 |
|
|
6 |
http://www.apache.org/licenses/LICENSE-2.0 |
|
7 |
|
|
8 |
Unless required by applicable law or agreed to in writing, software |
|
9 |
distributed under the License is distributed on an "AS IS" BASIS, |
|
10 |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
11 |
See the License for the specific language governing permissions and |
|
12 |
limitations under the License. |
|
13 |
|
|
14 |
Based on Paul Blundell's Tutorial: |
|
15 |
http://blog.blundell-apps.com/tut-asynctask-loader-using-support-library/ |
|
16 |
|
|
17 |
which is originally based on: |
|
18 |
https://developer.android.com/reference/android/content/AsyncTaskLoader.html |
|
19 |
*/ |
|
20 |
|
|
21 |
package info.guardianproject.checkey; |
|
22 |
|
|
23 |
import android.app.Activity; |
|
24 |
import android.content.Context; |
|
25 |
import android.content.Intent; |
|
26 |
import android.content.pm.PackageInfo; |
|
27 |
import android.content.pm.PackageManager; |
|
28 |
import android.content.pm.PackageManager.NameNotFoundException; |
|
29 |
import android.content.pm.Signature; |
|
30 |
import android.net.Uri; |
|
31 |
import android.os.Bundle; |
|
32 |
import android.support.v4.app.ListFragment; |
|
33 |
import android.support.v4.app.LoaderManager.LoaderCallbacks; |
|
34 |
import android.support.v4.content.Loader; |
|
35 |
import android.support.v7.app.ActionBarActivity; |
|
36 |
import android.support.v7.view.ActionMode; |
|
37 |
import android.view.Menu; |
|
38 |
import android.view.MenuInflater; |
|
39 |
import android.view.MenuItem; |
|
40 |
import android.view.View; |
|
41 |
import android.webkit.WebView; |
|
42 |
import android.widget.ListView; |
|
43 |
import android.widget.Toast; |
|
44 |
|
|
45 |
import java.io.ByteArrayInputStream; |
|
46 |
import java.io.FileNotFoundException; |
|
47 |
import java.io.FileOutputStream; |
|
48 |
import java.io.IOException; |
|
49 |
import java.io.InputStream; |
|
50 |
import java.io.UnsupportedEncodingException; |
|
51 |
import java.security.NoSuchAlgorithmException; |
|
52 |
import java.security.cert.CertificateException; |
|
53 |
import java.security.cert.CertificateFactory; |
|
54 |
import java.security.cert.X509Certificate; |
|
55 |
import java.util.List; |
|
56 |
|
|
57 |
public class AppListFragment extends ListFragment implements LoaderCallbacks<List<AppEntry>> { |
|
58 |
|
|
59 |
private PackageManager pm; |
|
60 |
private AppListAdapter adapter; |
|
61 |
private ActionMode actionMode; |
|
62 |
private ListView listView; |
|
63 |
private int selectedItem = -1; |
|
64 |
private static final String STATE_CHECKED = "info.guardianproject.checkey.STATE_CHECKED"; |
|
65 |
WebView androidObservatoryView; |
|
66 |
|
|
67 |
@Override |
|
68 |
public void onActivityCreated(Bundle savedInstanceState) { |
|
69 |
super.onActivityCreated(savedInstanceState); |
|
70 |
|
|
71 |
setEmptyText(getString(R.string.no_applications_found)); |
|
72 |
|
|
73 |
adapter = new AppListAdapter(getActivity()); |
|
74 |
setListAdapter(adapter); |
|
75 |
setListShown(false); |
|
76 |
|
|
77 |
listView = getListView(); |
|
78 |
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); |
|
79 |
|
|
80 |
if (savedInstanceState != null) { |
|
81 |
int position = savedInstanceState.getInt(STATE_CHECKED, -1); |
|
82 |
if (position > -1) { |
|
83 |
listView.setItemChecked(position, true); |
|
84 |
} |
|
85 |
} |
|
86 |
|
|
87 |
// Prepare the loader |
|
88 |
// either reconnect with an existing one or start a new one |
|
89 |
getLoaderManager().initLoader(0, null, this); |
|
90 |
} |
|
91 |
|
|
92 |
@Override |
|
93 |
public void onSaveInstanceState(Bundle savedInstanceState) { |
|
94 |
super.onSaveInstanceState(savedInstanceState); |
|
95 |
savedInstanceState.putInt(STATE_CHECKED, selectedItem); |
|
96 |
} |
|
97 |
|
|
98 |
@Override |
|
99 |
public void onListItemClick(ListView l, View v, int position, long id) { |
|
100 |
if (actionMode != null) { |
|
101 |
return; |
|
102 |
} |
|
103 |
|
|
104 |
// Start the CAB using the ActionMode.Callback defined above |
|
105 |
ActionBarActivity activity = (ActionBarActivity) getActivity(); |
|
106 |
actionMode = activity.startSupportActionMode(mActionModeCallback); |
|
107 |
selectedItem = position; |
|
108 |
} |
|
109 |
|
|
110 |
private void saveCertificate(AppEntry appEntry, Intent intent) { |
|
111 |
if (pm == null) |
|
112 |
pm = getActivity().getApplicationContext().getPackageManager(); |
|
113 |
Activity activity = getActivity(); |
|
114 |
String packageName = appEntry.getPackageName(); |
|
115 |
PackageInfo pkgInfo; |
|
116 |
try { |
|
117 |
pkgInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); |
|
118 |
CertificateFactory certFactory = CertificateFactory.getInstance("X509"); |
|
119 |
for (Signature sig : pkgInfo.signatures) { |
|
120 |
byte[] cert = sig.toByteArray(); |
|
121 |
InputStream inStream = new ByteArrayInputStream(cert); |
|
122 |
X509Certificate x509 = (X509Certificate) certFactory.generateCertificate(inStream); |
|
123 |
|
|
124 |
String fileName = packageName + ".cer"; |
|
125 |
final FileOutputStream os = activity.openFileOutput(fileName, |
|
126 |
Context.MODE_WORLD_READABLE); |
|
127 |
os.write(cert); |
|
128 |
os.close(); |
|
129 |
|
|
130 |
String subject = packageName + " - " + x509.getIssuerDN().getName() |
|
131 |
+ " - " + x509.getNotAfter(); |
|
132 |
Uri uri = Uri.fromFile(activity.getFileStreamPath(fileName)); |
|
133 |
Intent i = new Intent(Intent.ACTION_SEND); |
|
134 |
i.setType("application/pkix-cert"); |
|
135 |
i.putExtra(Intent.EXTRA_STREAM, uri); |
|
136 |
i.putExtra(Intent.EXTRA_TITLE, subject); |
|
137 |
i.putExtra(Intent.EXTRA_SUBJECT, subject); |
|
138 |
startActivity(Intent.createChooser(i, getString(R.string.save_cert_using))); |
|
139 |
} |
|
140 |
} catch (NameNotFoundException e) { |
|
141 |
e.printStackTrace(); |
|
142 |
} catch (CertificateException e) { |
|
143 |
e.printStackTrace(); |
|
144 |
} catch (FileNotFoundException e) { |
|
145 |
e.printStackTrace(); |
|
146 |
} catch (UnsupportedEncodingException e) { |
|
147 |
e.printStackTrace(); |
|
148 |
} catch (IOException e) { |
|
149 |
e.printStackTrace(); |
|
150 |
} |
|
151 |
} |
|
152 |
|
|
153 |
private void virustotal(AppEntry appEntry, Intent intent) { |
|
154 |
String urlString = "https://www.virustotal.com/en/file/" |
|
155 |
+ Utils.getBinaryHash(appEntry.getApkFile(), "sha256") + "/analysis/"; |
|
156 |
intent.setData(Uri.parse(urlString)); |
|
157 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.virustotal); |
|
158 |
startActivity(intent); |
|
159 |
} |
|
160 |
|
|
161 |
private void byApkHash(AppEntry appEntry, Intent intent) { |
|
162 |
String urlString = "https://androidobservatory.org/?searchby=binhash&q=" |
|
163 |
+ Utils.getBinaryHash(appEntry.getApkFile(), "sha1"); |
|
164 |
intent.setData(Uri.parse(urlString)); |
|
165 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.by_apk_hash); |
|
166 |
startActivity(intent); |
|
167 |
} |
|
168 |
|
|
169 |
private void byPackageName(AppEntry appEntry, Intent intent) { |
|
170 |
String urlString = "https://androidobservatory.org/?searchby=pkg&q=" |
|
171 |
+ appEntry.getPackageName(); |
|
172 |
intent.setData(Uri.parse(urlString)); |
|
173 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.by_package_name); |
|
174 |
startActivity(intent); |
|
175 |
} |
|
176 |
|
|
177 |
private void bySigningCertificate(AppEntry appEntry, Intent intent) { |
|
178 |
String sha1; |
|
179 |
try { |
|
180 |
sha1 = Utils.getCertificateFingerprint(appEntry.getApkFile(), "sha1"); |
|
181 |
} catch (NoSuchAlgorithmException e) { |
|
182 |
e.printStackTrace(); |
|
183 |
Toast.makeText(getActivity(), "Cannot make fingerprint of signing certificate", |
|
184 |
Toast.LENGTH_LONG).show(); |
|
185 |
return; |
|
186 |
} |
|
187 |
intent.setData(Uri.parse("https://androidobservatory.org/?searchby=certhash&q=" + sha1)); |
|
188 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.by_signing_certificate); |
|
189 |
startActivity(intent); |
|
190 |
} |
|
191 |
|
|
192 |
@Override |
|
193 |
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) { |
|
194 |
// This is called when a new loader needs to be created. |
|
195 |
// This sample only has one Loader with no arguments, so it is simple. |
|
196 |
return new AppListLoader(getActivity()); |
|
197 |
} |
|
198 |
|
|
199 |
@Override |
|
200 |
public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) { |
|
201 |
adapter.setData(data); |
|
202 |
|
|
203 |
// The list should now be shown |
|
204 |
if (isResumed()) { |
|
205 |
setListShown(true); |
|
206 |
} else { |
|
207 |
setListShownNoAnimation(true); |
|
208 |
} |
|
209 |
} |
|
210 |
|
|
211 |
@Override |
|
212 |
public void onLoaderReset(Loader<List<AppEntry>> loader) { |
|
213 |
// Clear the data in the adapter |
|
214 |
adapter.setData(null); |
|
215 |
} |
|
216 |
|
|
217 |
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() { |
|
218 |
|
|
219 |
// Called when the action mode is created; startActionMode() was called |
|
220 |
@Override |
|
221 |
public boolean onCreateActionMode(ActionMode mode, Menu menu) { |
|
222 |
// Inflate a menu resource providing context menu items |
|
223 |
MenuInflater inflater = mode.getMenuInflater(); |
|
224 |
inflater.inflate(R.menu.context, menu); |
|
225 |
return true; |
|
226 |
} |
|
227 |
|
|
228 |
@Override |
|
229 |
public boolean onPrepareActionMode(ActionMode mode, Menu menu) { |
|
230 |
return false; |
|
231 |
} |
|
232 |
|
|
233 |
// Called when the user selects a contextual menu item |
|
234 |
@Override |
|
235 |
public boolean onActionItemClicked(ActionMode mode, MenuItem item) { |
|
236 |
AppEntry appEntry = (AppEntry) adapter.getItem(selectedItem); |
|
237 |
Intent intent = new Intent(getActivity(), WebViewActivity.class); |
|
238 |
intent.putExtra(Intent.EXTRA_TEXT, appEntry.getLabel()); |
|
239 |
switch (item.getItemId()) { |
|
240 |
case R.id.save: |
|
241 |
saveCertificate(appEntry, intent); |
|
242 |
break; |
|
243 |
case R.id.virustotal: |
|
244 |
virustotal(appEntry, intent); |
|
245 |
break; |
|
246 |
case R.id.by_apk_hash: |
|
247 |
byApkHash(appEntry, intent); |
|
248 |
break; |
|
249 |
case R.id.by_package_name: |
|
250 |
byPackageName(appEntry, intent); |
|
251 |
break; |
|
252 |
case R.id.by_signing_certificate: |
|
253 |
bySigningCertificate(appEntry, intent); |
|
254 |
break; |
|
255 |
|
|
256 |
default: |
|
257 |
return false; |
|
258 |
} |
|
259 |
mode.finish(); |
|
260 |
return true; |
|
261 |
} |
|
262 |
|
|
263 |
// Called when the user exits the action mode |
|
264 |
@Override |
|
265 |
public void onDestroyActionMode(ActionMode mode) { |
|
266 |
listView.setItemChecked(selectedItem, false); |
|
267 |
listView.clearChoices(); |
|
268 |
selectedItem = -1; |
|
269 |
actionMode = null; |
|
270 |
} |
|
271 |
}; |
|
272 |
} |
src/info/guardianproject/checkey/MainActivity.java | ||
---|---|---|
1 | 1 |
|
2 | 2 |
package info.guardianproject.checkey; |
3 | 3 |
|
4 |
import android.annotation.SuppressLint; |
|
5 |
import android.app.Activity; |
|
6 |
import android.content.Context; |
|
7 |
import android.content.Intent; |
|
8 |
import android.content.pm.PackageInfo; |
|
9 |
import android.content.pm.PackageManager; |
|
10 |
import android.content.pm.PackageManager.NameNotFoundException; |
|
11 |
import android.net.Uri; |
|
4 | 12 |
import android.os.Bundle; |
13 |
import android.support.v4.app.ListFragment; |
|
14 |
import android.support.v4.app.LoaderManager.LoaderCallbacks; |
|
15 |
import android.support.v4.content.Loader; |
|
5 | 16 |
import android.support.v7.app.ActionBarActivity; |
17 |
import android.util.Log; |
|
6 | 18 |
import android.view.Menu; |
7 | 19 |
import android.view.MenuItem; |
20 |
import android.view.View; |
|
21 |
import android.webkit.WebView; |
|
22 |
import android.widget.ListAdapter; |
|
23 |
import android.widget.ListView; |
|
24 |
import android.widget.TextView; |
|
25 |
import android.widget.Toast; |
|
26 |
|
|
27 |
import java.io.ByteArrayInputStream; |
|
28 |
import java.io.FileNotFoundException; |
|
29 |
import java.io.FileOutputStream; |
|
30 |
import java.io.IOException; |
|
31 |
import java.io.InputStream; |
|
32 |
import java.io.StringBufferInputStream; |
|
33 |
import java.io.UnsupportedEncodingException; |
|
34 |
import java.security.NoSuchAlgorithmException; |
|
35 |
import java.security.cert.CertificateException; |
|
36 |
import java.security.cert.CertificateFactory; |
|
37 |
import java.security.cert.X509Certificate; |
|
38 |
import java.util.Arrays; |
|
39 |
import java.util.List; |
|
40 |
import java.util.Properties; |
|
8 | 41 |
|
9 | 42 |
public class MainActivity extends ActionBarActivity { |
10 | 43 |
private final String TAG = "MainActivity"; |
44 |
|
|
45 |
private static PackageManager pm; |
|
46 |
private static CertificateFactory certificateFactory; |
|
47 |
private static int selectedItem = -1; |
|
11 | 48 |
private AppListFragment appListFragment = null; |
12 | 49 |
|
13 | 50 |
@Override |
... | ... | |
32 | 69 |
|
33 | 70 |
@Override |
34 | 71 |
public boolean onOptionsItemSelected(MenuItem item) { |
72 |
ListAdapter adapter = appListFragment.getListAdapter(); |
|
73 |
AppEntry appEntry = (AppEntry) adapter.getItem(selectedItem); |
|
74 |
Intent intent = new Intent(this, WebViewActivity.class); |
|
75 |
intent.putExtra(Intent.EXTRA_TEXT, appEntry.getLabel()); |
|
35 | 76 |
switch (item.getItemId()) { |
36 | 77 |
case android.R.id.home: |
37 | 78 |
setResult(RESULT_CANCELED); |
38 | 79 |
finish(); |
39 | 80 |
return true; |
81 |
case R.id.save: |
|
82 |
saveCertificate(appEntry, intent); |
|
83 |
return true; |
|
84 |
case R.id.virustotal: |
|
85 |
virustotal(appEntry, intent); |
|
86 |
return true; |
|
87 |
case R.id.by_apk_hash: |
|
88 |
byApkHash(appEntry, intent); |
|
89 |
return true; |
|
90 |
case R.id.by_package_name: |
|
91 |
byPackageName(appEntry, intent); |
|
92 |
return true; |
|
93 |
case R.id.by_signing_certificate: |
|
94 |
bySigningCertificate(appEntry, intent); |
|
95 |
return true; |
|
40 | 96 |
case R.id.action_settings: |
41 | 97 |
return true; |
42 | 98 |
} |
43 | 99 |
return super.onOptionsItemSelected(item); |
44 | 100 |
} |
101 |
|
|
102 |
private static X509Certificate[] getX509Certificates(Context context, String packageName) { |
|
103 |
X509Certificate[] certs = null; |
|
104 |
if (pm == null) |
|
105 |
pm = context.getApplicationContext().getPackageManager(); |
|
106 |
try { |
|
107 |
PackageInfo pkgInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); |
|
108 |
if (certificateFactory == null) |
|
109 |
certificateFactory = CertificateFactory.getInstance("X509"); |
|
110 |
certs = new X509Certificate[pkgInfo.signatures.length]; |
|
111 |
for (int i = 0; i < certs.length; i++) { |
|
112 |
byte[] cert = pkgInfo.signatures[i].toByteArray(); |
|
113 |
InputStream inStream = new ByteArrayInputStream(cert); |
|
114 |
certs[i] = (X509Certificate) certificateFactory.generateCertificate(inStream); |
|
115 |
} |
|
116 |
} catch (NameNotFoundException e) { |
|
117 |
e.printStackTrace(); |
|
118 |
} catch (CertificateException e) { |
|
119 |
e.printStackTrace(); |
|
120 |
} |
|
121 |
return certs; |
|
122 |
} |
|
123 |
|
|
124 |
private static void showCertificateInfo(Activity activity, AppEntry appEntry) { |
|
125 |
String packageName = appEntry.getPackageName(); |
|
126 |
X509Certificate[] certs = getX509Certificates(activity, packageName); |
|
127 |
if (certs == null || certs.length < 1) |
|
128 |
return; |
|
129 |
// for now, just support the first cert since that is far and away |
|
130 |
// the |
|
131 |
// most common |
|
132 |
X509Certificate cert = certs[0]; |
|
133 |
TextView issuerdn = (TextView) activity.findViewById(R.id.issuerdn); |
|
134 |
issuerdn.setText(cert.getIssuerDN().getName()); |
|
135 |
TextView subjectdn = (TextView) activity.findViewById(R.id.subjectdn); |
|
136 |
subjectdn.setText(cert.getSubjectDN().getName()); |
|
137 |
} |
|
138 |
|
|
139 |
@SuppressLint("WorldReadableFiles") |
|
140 |
private void saveCertificate(AppEntry appEntry, Intent intent) { |
|
141 |
String packageName = appEntry.getPackageName(); |
|
142 |
try { |
|
143 |
for (X509Certificate x509 : getX509Certificates(this, packageName)) { |
|
144 |
String fileName = packageName + ".cer"; |
|
145 |
@SuppressWarnings("deprecation") |
|
146 |
final FileOutputStream os = openFileOutput(fileName, |
|
147 |
Context.MODE_WORLD_READABLE); |
|
148 |
os.write(x509.getEncoded()); |
|
149 |
os.close(); |
|
150 |
|
|
151 |
String subject = packageName + " - " + x509.getIssuerDN().getName() |
|
152 |
+ " - " + x509.getNotAfter(); |
|
153 |
Uri uri = Uri.fromFile(getFileStreamPath(fileName)); |
|
154 |
Intent i = new Intent(Intent.ACTION_SEND); |
|
155 |
i.setType("application/pkix-cert"); |
|
156 |
i.putExtra(Intent.EXTRA_STREAM, uri); |
|
157 |
i.putExtra(Intent.EXTRA_TITLE, subject); |
|
158 |
i.putExtra(Intent.EXTRA_SUBJECT, subject); |
|
159 |
startActivity(Intent.createChooser(i, getString(R.string.save_cert_using))); |
|
160 |
} |
|
161 |
} catch (CertificateException e) { |
|
162 |
e.printStackTrace(); |
|
163 |
} catch (FileNotFoundException e) { |
|
164 |
e.printStackTrace(); |
|
165 |
} catch (UnsupportedEncodingException e) { |
|
166 |
e.printStackTrace(); |
|
167 |
} catch (IOException e) { |
|
168 |
e.printStackTrace(); |
|
169 |
} |
|
170 |
} |
|
171 |
|
|
172 |
private void virustotal(AppEntry appEntry, Intent intent) { |
|
173 |
String urlString = "https://www.virustotal.com/en/file/" |
|
174 |
+ Utils.getBinaryHash(appEntry.getApkFile(), "sha256") + "/analysis/"; |
|
175 |
intent.setData(Uri.parse(urlString)); |
|
176 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.virustotal); |
|
177 |
startActivity(intent); |
|
178 |
} |
|
179 |
|
|
180 |
private void byApkHash(AppEntry appEntry, Intent intent) { |
|
181 |
String urlString = "https://androidobservatory.org/?searchby=binhash&q=" |
|
182 |
+ Utils.getBinaryHash(appEntry.getApkFile(), "sha1"); |
|
183 |
intent.setData(Uri.parse(urlString)); |
|
184 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.by_apk_hash); |
|
185 |
startActivity(intent); |
|
186 |
} |
|
187 |
|
|
188 |
private void byPackageName(AppEntry appEntry, Intent intent) { |
|
189 |
String urlString = "https://androidobservatory.org/?searchby=pkg&q=" |
|
190 |
+ appEntry.getPackageName(); |
|
191 |
intent.setData(Uri.parse(urlString)); |
|
192 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.by_package_name); |
|
193 |
startActivity(intent); |
|
194 |
} |
|
195 |
|
|
196 |
private void bySigningCertificate(AppEntry appEntry, Intent intent) { |
|
197 |
String sha1; |
|
198 |
try { |
|
199 |
sha1 = Utils.getCertificateFingerprint(appEntry.getApkFile(), "sha1"); |
|
200 |
} catch (NoSuchAlgorithmException e) { |
|
201 |
e.printStackTrace(); |
|
202 |
Toast.makeText(this, "Cannot make fingerprint of signing certificate", |
|
203 |
Toast.LENGTH_LONG).show(); |
|
204 |
return; |
|
205 |
} |
|
206 |
intent.setData(Uri.parse("https://androidobservatory.org/?searchby=certhash&q=" + sha1)); |
|
207 |
intent.putExtra(Intent.EXTRA_TITLE, R.string.by_signing_certificate); |
|
208 |
startActivity(intent); |
|
209 |
} |
|
210 |
|
|
211 |
public static class AppListFragment extends ListFragment implements |
|
212 |
LoaderCallbacks<List<AppEntry>> { |
|
213 |
|
|
214 |
private AppListAdapter adapter; |
|
215 |
private ListView listView; |
|
216 |
private static final String STATE_CHECKED = "info.guardianproject.checkey.STATE_CHECKED"; |
|
217 |
WebView androidObservatoryView; |
|
218 |
|
|
219 |
public AppListFragment() { |
|
220 |
super(); |
|
221 |
} |
|
222 |
|
|
223 |
@Override |
|
224 |
public void onActivityCreated(Bundle savedInstanceState) { |
|
225 |
super.onActivityCreated(savedInstanceState); |
|
226 |
|
|
227 |
setEmptyText(getString(R.string.no_applications_found)); |
|
228 |
|
|
229 |
adapter = new AppListAdapter(getActivity()); |
|
230 |
setListAdapter(adapter); |
|
231 |
setListShown(false); |
|
232 |
|
|
233 |
listView = getListView(); |
|
234 |
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); |
|
235 |
|
|
236 |
if (savedInstanceState != null) { |
|
237 |
int position = savedInstanceState.getInt(STATE_CHECKED, -1); |
|
238 |
if (position > -1) { |
|
239 |
listView.setItemChecked(position, true); |
|
240 |
} |
|
241 |
} |
|
242 |
|
|
243 |
// Prepare the loader |
|
244 |
// either reconnect with an existing one or start a new one |
|
245 |
getLoaderManager().initLoader(0, null, this); |
|
246 |
} |
|
247 |
|
|
248 |
@Override |
|
249 |
public void onSaveInstanceState(Bundle savedInstanceState) { |
|
250 |
super.onSaveInstanceState(savedInstanceState); |
|
251 |
savedInstanceState.putInt(STATE_CHECKED, selectedItem); |
|
252 |
} |
|
253 |
|
|
254 |
@Override |
|
255 |
public void onListItemClick(ListView l, View v, int position, long id) { |
|
256 |
// Start the CAB using the ActionMode.Callback defined above |
|
257 |
ActionBarActivity activity = (ActionBarActivity) getActivity(); |
|
258 |
selectedItem = position; |
|
259 |
AppEntry appEntry = (AppEntry) adapter.getItem(selectedItem); |
|
260 |
showCertificateInfo(activity, appEntry); |
|
261 |
} |
|
262 |
|
|
263 |
@Override |
|
264 |
public Loader<List<AppEntry>> onCreateLoader(int id, Bundle args) { |
|
265 |
// This is called when a new loader needs to be created. |
|
266 |
// This sample only has one Loader with no arguments, so it is |
|
267 |
// simple. |
|
268 |
return new AppListLoader(getActivity()); |
|
269 |
} |
|
270 |
|
|
271 |
@Override |
|
272 |
public void onLoadFinished(Loader<List<AppEntry>> loader, List<AppEntry> data) { |
|
273 |
adapter.setData(data); |
|
274 |
|
|
275 |
// The list should now be shown |
|
276 |
if (isResumed()) { |
|
277 |
setListShown(true); |
|
278 |
} else { |
|
279 |
setListShownNoAnimation(true); |
|
280 |
} |
|
281 |
} |
|
282 |
|
|
283 |
@Override |
|
284 |
public void onLoaderReset(Loader<List<AppEntry>> loader) { |
|
285 |
// Clear the data in the adapter |
|
286 |
adapter.setData(null); |
|
287 |
} |
|
288 |
} |
|
45 | 289 |
} |
Also available in: Unified diff