Tag-Archive for » android «

Debug- und Testcode haben nichts im Release zu suchen

Jaja, da hab ich mir doch die Sunrise Mein Konto App von Google Play runtergeladen. Mit der App kann man unter sein eigenes Sunrise Konto zugreifen und dabei laufende Kosten, Inklusivleistungen und Rechnungen der eigenen Produkte einsehen. Es ist wirklich eine gute App, teilweise umständlich zu navigieren aber ansonsten ist alles ersichtlich was man an Kontoinformationen braucht.

Und Windows Phone 7?

Da dachte ich mir doch: Hey, eine solche App muss ich auf dem Windows Phone 7 auch haben. Aber leider Fehlanzeige. Für WP7 gibts von Sunrise noch keine App.
Ja gut, dann schreib ich doch selber eine Sunrise Mein Konto App fürs WP7.

Doch so einfach ist das nicht: Die Verbindung zwischen App und Server ist mit SSL gesichert. Ich habe keine Ahnung welche Services (REST, SOAP?) an welchen Endpunkten aufgerufen werden. Was für Daten und in welcher Form (JSON, XML?) diese übertragen werden. Wie läuft der Login Vorgang, HTTP Authentication, GET/POST Variablen, selbstgemachtes Gemüse? Hier wurden die Hausaufgaben gut gemacht, der Datenverkehr ist verschlüsselt.

Aber ich möchte dennoch herausfinden was so vor sich geht. Machen wir doch eine Man in the Middle Attacke mit dem Emulator. Dafür werde ich meine persönlichen Login Daten opfern müssen.
Das Übliche halt:

  • Meinen PC als Proxy dazwischenschalten, am Besten mit Charles Web Debugging Proxy
  • Lokales Zertifikat erstellen wo www1.sunrise.ch als Common Name eingetragen ist
  • Das “Sunrise” in den Zertifikatsspeicher des Emulators oder eines echten Android Gerätes (Root Zugriff erforderlich) installieren
  • Applikation auf Emulator oder Android Gerät laufen lassen und mit LogCat zugleich noch den Output der App aufzeigen

Aber ich bin gerade auf Wohnungssuche, habe also keine Zeit und brauche eine schnellere Lösung 😉

Hey App, lass mich rein, ich bin Entwickler

Meine Faulheit muss ja bekanntlich belohnt werden. Und das wurde ich auch. 😀
Ich wollte sehen ob die App irgendwelchen Debug Output an LogCat sendet und habe die offizielle App von Google Play runtergeladen. Der Output war jedoch minimal. Einige Standardmeldungen wie “Login successful” kamen heraus aber nichts Auffschlussreiches über die Internals der App.

Also habe ich mich etwas am APK File zu schaffen gemacht. Mit dem APK MultiTool die App dekompiliert, und anschliessend das AppManifest.xml in Klartext geöffnet.
Folgendes ist mir aufgefallen:

  1. android:debuggable=”true” ist nicht gesetzt. Ist auch gut so, ist ja schliesslich ein Release und keine Debug Version.
  2. Eine Activity mit Namen Administration ist registriert. Lässt vermuten, dass es irgendwo in der ofiziellen App einen ServiceDialog o.ä. gibt.
Also schön das Debug Flag setzen und die XML Datei speichern. Mit dem APK MultiTool die “modifizierte” Applikation wieder in ein APK File builden und anschliessend noch erneut signieren.

Oh, ich bin ja schon drin

Und wer hätte das gedacht: Die Debug Statements wurden alle schön brav mit in die ofizielle App kompiliert. LogCat spuckte fröhlich alle möglichen Statements raus. JSON Objekte und Inhalt, Hostnamen und Pfade der jeweiligen Services, Login Infos uvm.

LogCat Output

Debug Infos im LogCat Output

Nun gut, aber was ist mit der registrierten Administration Activity? War nicht schwer herauszufinden. Sobald man im Login Screen ist, einfach mal den “Search” Button drücken (F5 im Emulator) und schon kommt man ins Service Menü, oder sollte ich besser sagen in den Test Dialog? Das neue APK File auf mein Android Gerät installiert. Übrigens: Die App lässt sich auch installieren, wenn “Installation von Nicht-Market Anwendungen zulassen” deaktiviert ist.

Fazit

Service Dialog

Service Dialog

Es ist nichts sensitives ans Tageslicht gerückt. Ich habe weder Zugriff auf vertrauliche Kundendaten erhalten oder sonstige Hinweise auf sicherheitskritische Bereiche. Da haben die Entwickler der App gute Arbeit geleistet.

Aber dennoch sollte man darum bemüht sein Debug Informationen, Testdaten, Testabläufe und sonstige interne Informationen nicht in den offiziellen Release zu veröffentlichen. Solche Daten verschwenden nicht nur zusätzlichen Speicher auf dem Phone, sondern sie können sicherheitskritische Informationen an jedermann preisgeben.

Testabläufe sollten auch möglichst mithilfe von Tools und Unit Tests vorgenommen werden und nicht mit echten Testern, welche auf Test Screens mit den Fingern herumdrücken. Solche Tests sind nicht immer einfach. Dies resultiert auch oft daraus, dass eine Entkopplung vom User Interface und der Business Logic nicht einfach zu bewerkstelligen ist. Ein guter Ansatz um eine gute Entkopplung zu erzielen und somit die Applikation “testbarer” zu machen wäre das Model View ViewModel (MVVM) Pattern.

Ich persönlich freue mich sehr über die ganze Sache. Dadurch habe ich genug Einblicke bekommen um meine Windows Phone App zu entwickeln. Die Authentisierung läuft über HTTP POST. Die Session wird mittels eines Login Keys, welcher vom Server zugewiesen wird, identifiziert und die Daten werden im JSON Format vom Server zurückgesandt.

Category: android, Coding  Tags: , ,  Leave a Comment

Android: Trusting SSL certificates

Two weeks ago I got the task to establish TLS secured connections via certificates to a service endpoint.
I thought it’s not a big deal, because the endpoint already uses an EV certificate from a trusted CA (SwissSign) in Switzerland. Therefore I shouldn’t have to worry that the certificate would be considered as untrusted so I don’t have to import it to the trusted certs in the  Java  key store etc.
FAIL! I’ve got a security exception, cert is not trusted. Same problem when I visit the website with the browser. Ok, that’s bad, SwissSign is not such a big player like thawte, so, it needs some time till it will be added to the android trusted CA list. But, when I visit thawte.com, their cert is also not trusted by android. WTF?
Windows Phone and iPhone trust my SwissSign CA and don’t complain.

So, let’s ask google, stackoverflow and the blogosphere. Found a lot of solutions how to disable certificate checking entirely.
Yeah, great, this will solve my problem, my connection will be “secure” and everyone will be able to intercept my connection and inject his own certificate. But I finally found the solution with help from other sites and some testing and debugging.

The Solution

The following main steps are required to achieve a secured connection from trusted Certification Authorities.

  1. Grab all required certificates (root and any intermediate CA’s)
  2. Create a keystore with keytool and the BouncyCastle provider and import the certs
  3. Load the keystore in your android app and use it for the secured connections
    • Don’t use the standard java.net.ssl.HttpsURLConnection for the secure connection. Use the Apache HttpClient (Version 4 atm) library, which is already built-in in android. It’s built on top of the java connection libraries and is, in my opinion, faster, better modularized and easier to understand.

Step 1: Grab the certs

You have to obtain all certificates that build a chain from the endpoint certificate the whole way up to the Root CA. This means, any (if present) Intermediate CA certs and also the Root CA cert. You don’t need to obtain the endpoint certificate.
You can obtain those certs from the chain (if provided) included in the endpoint certificate or from the official site of the issuer (in my case SwissSign).

Ensure that you save the obtained certificates in the Base64 encoded X.509 format. The content should look similar to this:

-----BEGIN CERTIFICATE-----
MIIGqTC.....
-----END CERTIFICATE-----

Step 2: Create the keystore

Download the BouncyCastle Provider and store it to a known location.
Also ensure that you can invoke the keytool command (usually located under the bin folder of your JRE installation).

Now import the obtained certs (don’t import the endpoint cert) into a BouncyCastle formatted keystore.
I didn’t tested it, but I think the order of importing the certificates is important. This means, import the lowermost Intermediate CA certificate first and then all the way up to the Root CA certificate.

With the following command a new keystore (if not already present) with the password mysecret will be created and the Intermediate CA certificate will be imported. I also defined the BouncyCastle provider, where it can be found on my file system and the keystore format. Execute this command for each certificate in the chain.

keytool -importcert -v -trustcacerts -file "path_to_cert/interm_ca.cer" -alias IntermediateCA -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Verify if the certificates were imported correctly into the keystore:

keytool -list -keystore "res/raw/myKeystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path_to_bouncycastle/bcprov-jdk16-145.jar" -storetype BKS -storepass mysecret

Should output the whole chain:

RootCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 24:77:D9:A8:91:D1:3B:FA:88:2D:C2:FF:F8:CD:33:93
IntermediateCA, 22.10.2010, trustedCertEntry, Thumbprint (MD5): 98:0F:C3:F8:39:F7:D8:05:07:02:0D:E3:14:5B:29:43

Now you can copy the keystore as a raw resource in your android app under res/raw/

Step 3: Use the keystore in your app

First of all we have to create a custom Apache HttpClient that uses our keystore for HTTPS connections:

public class MyHttpClient extends DefaultHttpClient {

	final Context context;

	public MyHttpClient(Context context) {
		this.context = context;
	}

	@Override
	protected ClientConnectionManager createClientConnectionManager() {
		SchemeRegistry registry = new SchemeRegistry();
		registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
		// Register for port 443 our SSLSocketFactory with our keystore
		// to the ConnectionManager
		registry.register(new Scheme("https", newSslSocketFactory(), 443));
		return new SingleClientConnManager(getParams(), registry);
	}

	private SSLSocketFactory newSslSocketFactory() {
		try {
			// Get an instance of the Bouncy Castle KeyStore format
			KeyStore trusted = KeyStore.getInstance("BKS");
			// Get the raw resource, which contains the keystore with
			// your trusted certificates (root and any intermediate certs)
			InputStream in = context.getResources().openRawResource(R.raw.mykeystore);
			try {
				// Initialize the keystore with the provided trusted certificates
				// Also provide the password of the keystore
				trusted.load(in, "mysecret".toCharArray());
			} finally {
				in.close();
			}
			// Pass the keystore to the SSLSocketFactory. The factory is responsible
			// for the verification of the server certificate.
			SSLSocketFactory sf = new SSLSocketFactory(trusted);
			// Hostname verification from certificate
			// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
			sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
			return sf;
		} catch (Exception e) {
			throw new AssertionError(e);
		}
	}
}

We have created our custom HttpClient, now we can just use it for secure connections. For example when we make a GET call to a REST resource.

// Instantiate the custom HttpClient
DefaultHttpClient client = new MyHttpClient(getApplicationContext());
HttpGet get = new HttpGet("https://www.mydomain.ch/rest/contacts/23");
// Execute the GET call and obtain the response
HttpResponse getResponse = client.execute(get);
HttpEntity responseEntity = getResponse.getEntity();

That’s it. Took me long to figure it out, hope this helps and saves you that time.

I really hope that the android platform will implement a better mechanism in future releases for defining which Certification Authorities should be trusted or not or just expand their own trusted CA list. If they don’t, I can’t believe they will get good acceptance from the business sector. Ok, you can control which certificates you want to trust in your app, but you still can’t add thawte as a trusted CA in the android keystore and your browser will always complain about an untrusted CA. The only way I know to eliminate this problem is to root your phone (very user friendly) and add your CA manually to the android keystore.

Feel free to comment.

Category: android, Coding  Tags: , , ,  93 Comments