Category: Android

Resources – Apps Users Want to Use

DetroitDevDay

I am presenting this talk at DetroitDevDay on November 12th. It’s a look into some differing views on psychology and app composition and how it affects users of software. As a mobile developer, this kind of work had come to the forefront as the bar for mobile apps continues to rise.

Books

Articles

Videos

Adding Keyboard Navigation to a RecyclerView

List showing selected tab only

I’ve created a list of items using a RecyclerView which works fine (see the screenshot). But, in order to keep my app accessible, I need to support keyboard-based navigation. Just using the normal Recyclerview pattern and the associated layout was not good enough. If I use a keyboard, I am unable to move the focus down into the list, the focus stops in the tab control and then cycles back up to the app bar. Tapping the items with your finger works fine, and even switch access moves the focus properly to the list items. But it doesn’t work for the keyboard.

The trick is to set the LinearLayout to focusable and clickable, and set the background of the LinearLayout to:

android:background="?android:attr/selectableItemBackground"

as shown below in the XML layout snippet below. These three changes allow the list items to be selectable by the keyboard.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:focusable="true"
    android:clickable="true"
    android:paddingBottom="5dp">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_marginTop="8dp"
        android:adjustViewBounds="true"
        android:layout_weight="1"/>
    <TextView
        android:id="@+id/item_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:paddingTop="12dp"
        android:layout_weight="8"/>

</LinearLayout>

Android Certificate Pinning

Barnum.aktie

Securing your web sites and services using HTTPS is something you should be doing no matter what. Last year the government mandated all their sites move to HTTPS, and even Google is rewarding secure sites in its ranking algorithm. There is no reason for not using HTTPS any more. Since HTTPS is the baseline for web apps, certificate pinning should be the baseline for mobile apps interacting with the web.

OWASP published a good description of certificate pinning. To summarize, pinning a certificate means that your app is verifying that the site the app is communicating with is the actual site by comparing the certificate presented by the site to one bundled in the app. This prevents a man-in-the-middle attack on your app.

Why is this important to your app? It matters greatly if you also own the web service API or use a proprietary or paid API. Attackers can use a man-in-the-middle attack to reverse-engineer the web service interface, or to inject malicious data into the payload sent by your app to the web service. In fact, I have used this technique in the past to deconstruct a vendor’s API to better understand how to call it. Lucky for me (but bad for consumers) that they didn’t pin certificates in their Android app in the Play Store.

How Do I Pin a Certificate?

Get the Certificate

First, you must acquire the certificate. Luckily you only need the certificate, and not the private key. If your team/company built the web services, you can get the certificate from them. If you are consuming a public API, there are a variety of ways to get the certificate. I just use OpenSSL from the command line:

ex +'g/BEGIN CERTIFICATE/,/END CERTIFICATE/p' <(echo | openssl s_client -showcerts -connect your.company.com:443) -scq > export.crt

In all likelihood, there will be more than one certificate found by that command, known as the certificate chain. You need the whole chain, and the above command will place those into the file. This file will be placed into the Java keystore.

Create the Keystore

You will need to download the BouncyCastle jar which is a cryptography API we’ll use to convert the certificate into the required .bks keystore. Create the keystore from the command line:

keytool -importcert -v -trustcacerts -file export.crt -alias ca -keystore pin.bks -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath /path/to/bcprov-jdk15on-155.jar -storetype BKS -storepass thekeystorepassword

Move the resulting .bks file into the res/raw folder of your Android Studio project.

Using the Keystore in Code

To keep the example simpler, we’ll look at how to use it directly with the HttpsURLConnection object. We’ll open the keystore, set its contents to an SSLContext, and then add a TrustManagerFactory to the SSLContext. Finally, we’ll associate the SSLContext object to the HttpsURLConnection and then the code can proceed as normal from there.


KeyStore trusted = KeyStore.getInstance("BKS");
InputStream store = app.getResources().openRawResource(R.raw.pin);
trusted.load(store, "thekeystorepassword");
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trusted);
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);

final URL networkUrl = new URL("https://your.url.com/resource");
final HttpsURLConnection conn = (HttpsURLConnection) networkUrl.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
conn.setRequestMethod("GET");

final InputStream inputFromServer = conn.getInputStream();

But I Use Retrofit!

(updated 4-19-2017)

Retrofit and the OkHttpClient make it even easier. You don’t need the certificate in a keystore. All you need is a hash of the public key, which you can get using this command:

openssl s_client -connect www.yourdomain.com:443 | openssl x509 -pubkey -noout | openssl rsa -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64

In your Android code you use the CertificatePinner object and inject that into the OkHTTPClient builder before building your Retrofit object. Note the prepending of the hash type and a slash before the actual hash value:


CertificatePinner certificatePinner = new CertificatePinner.Builder()
    .add("www.yourdomain.com", "sha256/UXRFPJGwFvvyJI3vFOMIc19r0JNlNSQydEnYRrZI/W4=")
    .build();
client = httpClient.certificatePinner(certificatePinner).build();

builder = new Retrofit.Builder()
    .baseUrl(Settings.BASE_URL)
    .client(client)
    .addConverterFactory(GsonConverterFactory.create());

Security

So some of you may be thinking:

Hey, hold on there. The certificate (and keystore password) is in my app now. Can’t someone decompile the app and get the certificate?

Yep, that’s true, someone could get the certificate (unless you used Retrofit), which they can technically fetch even without your app. The good news is that they can’t use the certificate to fake being the web server without the private key, which we never used here or included in the keystore. The certificate is publicly available, you aren’t decreasing the security of your app, you are increasing it!

Also, I’d like to point out that I didn’t use the term SSL. At this point in time, well-hardened web servers shouldn’t be using SSL, they are actually using TLS. SSL has become synonymous with HTTPS and that’s just wrong, and even SSL vs TLS is not a completely equitable comparison. HTTPS means the connection is secure, SSL or TLS is the method used to secure the connection.