From 9aff6c7f8527f3eb78a14c62a677a2fd0631130e Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 9 May 2015 19:26:11 +0200 Subject: linked-ids: add certificate pinning, pin twitter api cert --- .../keychain/linked/LinkedTokenResource.java | 30 +++++++++++++++++----- .../keychain/linked/resources/DnsResource.java | 2 +- .../linked/resources/GenericHttpsResource.java | 5 ++-- .../keychain/linked/resources/GithubResource.java | 12 ++++----- .../keychain/linked/resources/TwitterResource.java | 24 ++++++++++------- 5 files changed, 49 insertions(+), 24 deletions(-) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java index 7eec2a66a..3f42355fc 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/LinkedTokenResource.java @@ -2,6 +2,7 @@ package org.sufficientlysecure.keychain.linked; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; @@ -16,6 +17,7 @@ import org.sufficientlysecure.keychain.operations.results.OperationResult.LogTyp import org.sufficientlysecure.keychain.operations.results.OperationResult.OperationLog; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.util.Log; +import org.thoughtcrime.ssl.pinning.util.PinningHelper; import java.io.BufferedReader; import java.io.IOException; @@ -30,6 +32,8 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import android.content.Context; + public abstract class LinkedTokenResource extends LinkedResource { @@ -166,7 +170,7 @@ public abstract class LinkedTokenResource extends LinkedResource { } - public LinkedVerifyResult verify(byte[] fingerprint) { + public LinkedVerifyResult verify(Context context, byte[] fingerprint) { OperationLog log = new OperationLog(); log.add(LogType.MSG_LV, 0); @@ -174,7 +178,7 @@ public abstract class LinkedTokenResource extends LinkedResource { // Try to fetch resource. Logs for itself String res = null; try { - res = fetchResource(log, 1); + res = fetchResource(context, log, 1); } catch (HttpStatusException e) { // log verbose output to logcat Log.e(Constants.TAG, "http error (" + e.getStatus() + "): " + e.getReason()); @@ -200,8 +204,8 @@ public abstract class LinkedTokenResource extends LinkedResource { } - protected abstract String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException, - JSONException; + protected abstract String fetchResource (Context context, OperationLog log, int indent) + throws HttpStatusException, IOException, JSONException; protected Matcher matchResource (OperationLog log, int indent, String res) { return magicPattern.matcher(res); @@ -231,12 +235,26 @@ public abstract class LinkedTokenResource extends LinkedResource { } @SuppressWarnings("deprecation") // HttpRequestBase is deprecated - public static String getResponseBody(HttpRequestBase request) throws IOException, HttpStatusException { + public static String getResponseBody(Context context, HttpRequestBase request) + throws IOException, HttpStatusException { + return getResponseBody(context, request, null); + } + + @SuppressWarnings("deprecation") // HttpRequestBase is deprecated + public static String getResponseBody(Context context, HttpRequestBase request, String[] pins) + throws IOException, HttpStatusException { StringBuilder sb = new StringBuilder(); request.setHeader("User-Agent", "Open Keychain"); - DefaultHttpClient httpClient = new DefaultHttpClient(new BasicHttpParams()); + + HttpClient httpClient; + if (pins == null) { + httpClient = new DefaultHttpClient(new BasicHttpParams()); + } else { + httpClient = PinningHelper.getPinnedHttpClient(context, pins); + } + HttpResponse response = httpClient.execute(request); int statusCode = response.getStatusLine().getStatusCode(); String reason = response.getStatusLine().getReasonPhrase(); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java index dd7662c6e..86b672cc1 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/DnsResource.java @@ -92,7 +92,7 @@ public class DnsResource extends LinkedTokenResource { } @Override - protected String fetchResource (OperationLog log, int indent) { + protected String fetchResource (Context context, OperationLog log, int indent) { Client c = new Client(); DNSMessage msg = c.query(new Question(mFqdn, mType, mClass)); diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java index 05939a2db..82240c405 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GenericHttpsResource.java @@ -34,11 +34,12 @@ public class GenericHttpsResource extends LinkedTokenResource { @SuppressWarnings("deprecation") // HttpGet is deprecated @Override - protected String fetchResource (OperationLog log, int indent) throws HttpStatusException, IOException { + protected String fetchResource (Context context, OperationLog log, int indent) + throws HttpStatusException, IOException { log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); HttpGet httpGet = new HttpGet(mSubUri); - return getResponseBody(httpGet); + return getResponseBody(context, httpGet); } diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java index ef042d540..30ce075b4 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/GithubResource.java @@ -49,14 +49,14 @@ public class GithubResource extends LinkedTokenResource { @SuppressWarnings("deprecation") // HttpGet is deprecated @Override - protected String fetchResource (OperationLog log, int indent) + protected String fetchResource (Context context, OperationLog log, int indent) throws HttpStatusException, IOException, JSONException { log.add(LogType.MSG_LV_FETCH, indent, mSubUri.toString()); indent += 1; HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + mGistId); - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet); JSONObject obj = new JSONObject(response); @@ -80,8 +80,8 @@ public class GithubResource extends LinkedTokenResource { } @SuppressWarnings("deprecation") - public static GithubResource searchInGithubStream(String screenName, String needle, - OperationLog log) { + public static GithubResource searchInGithubStream( + Context context, String screenName, String needle, OperationLog log) { // narrow the needle down to important part Matcher matcher = magicPattern.matcher(needle); @@ -98,7 +98,7 @@ public class GithubResource extends LinkedTokenResource { httpGet.setHeader("Content-Type", "application/json"); httpGet.setHeader("User-Agent", "OpenKeychain"); - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet); array = new JSONArray(response); } @@ -118,7 +118,7 @@ public class GithubResource extends LinkedTokenResource { HttpGet httpGet = new HttpGet("https://api.github.com/gists/" + id); httpGet.setHeader("User-Agent", "OpenKeychain"); - JSONObject gistObj = new JSONObject(getResponseBody(httpGet)); + JSONObject gistObj = new JSONObject(getResponseBody(context, httpGet)); JSONObject gistFiles = gistObj.getJSONObject("files"); Iterator gistIt = gistFiles.keys(); if (!gistIt.hasNext()) { diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java index 36100fe58..d6b806ee6 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/linked/resources/TwitterResource.java @@ -32,6 +32,11 @@ import java.util.regex.Pattern; public class TwitterResource extends LinkedTokenResource { + public static final String[] CERT_PINS = new String[] { + // antec Class 3 Secure Server CA - G4 + "513fb9743870b73440418d30930699ff" + }; + final String mHandle; final String mTweetId; @@ -68,12 +73,12 @@ public class TwitterResource extends LinkedTokenResource { @SuppressWarnings("deprecation") @Override - protected String fetchResource(OperationLog log, int indent) throws IOException, HttpStatusException, - JSONException { + protected String fetchResource(Context context, OperationLog log, int indent) + throws IOException, HttpStatusException, JSONException { String authToken; try { - authToken = getAuthToken(); + authToken = getAuthToken(context); } catch (IOException | HttpStatusException | JSONException e) { log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, indent); return null; @@ -90,7 +95,7 @@ public class TwitterResource extends LinkedTokenResource { httpGet.setHeader("Content-Type", "application/json"); try { - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet, CERT_PINS); JSONObject obj = new JSONObject(response); JSONObject user = obj.getJSONObject("user"); if (!mHandle.equalsIgnoreCase(user.getString("screen_name"))) { @@ -142,11 +147,11 @@ public class TwitterResource extends LinkedTokenResource { @SuppressWarnings("deprecation") public static TwitterResource searchInTwitterStream( - String screenName, String needle, OperationLog log) { + Context context, String screenName, String needle, OperationLog log) { String authToken; try { - authToken = getAuthToken(); + authToken = getAuthToken(context); } catch (IOException | HttpStatusException | JSONException e) { log.add(LogType.MSG_LV_ERROR_TWITTER_AUTH, 1); return null; @@ -166,7 +171,7 @@ public class TwitterResource extends LinkedTokenResource { httpGet.setHeader("Content-Type", "application/json"); try { - String response = getResponseBody(httpGet); + String response = getResponseBody(context, httpGet, CERT_PINS); JSONArray array = new JSONArray(response); for (int i = 0; i < array.length(); i++) { @@ -203,7 +208,8 @@ public class TwitterResource extends LinkedTokenResource { private static String cachedAuthToken; @SuppressWarnings("deprecation") - private static String getAuthToken() throws IOException, HttpStatusException, JSONException { + private static String getAuthToken(Context context) + throws IOException, HttpStatusException, JSONException { if (cachedAuthToken != null) { return cachedAuthToken; } @@ -215,7 +221,7 @@ public class TwitterResource extends LinkedTokenResource { httpPost.setHeader("Authorization", "Basic " + base64Encoded); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8"); httpPost.setEntity(new StringEntity("grant_type=client_credentials")); - JSONObject rawAuthorization = new JSONObject(getResponseBody(httpPost)); + JSONObject rawAuthorization = new JSONObject(getResponseBody(context, httpPost, CERT_PINS)); // Applications should verify that the value associated with the // token_type key of the returned object is bearer -- cgit v1.2.3