Skip to content

Commit ba17f5a

Browse files
committed
Fixes to Custom CA Sample
1 parent eddcf3e commit ba17f5a

File tree

3 files changed

+138
-105
lines changed

3 files changed

+138
-105
lines changed

sample/src/main/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<activity android:name=".IntentServiceSample"/>
3333
<activity android:name=".SaxSample"/>
3434
<activity android:name=".FilesSample"/>
35+
<activity android:name=".CustomCASample"/>
3536

3637
<service android:name=".services.ExampleIntentService"/>
3738
</application>

sample/src/main/java/com/loopj/android/http/sample/CustomCASample.java

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,23 @@
1818

1919
package com.loopj.android.http.sample;
2020

21+
import android.app.AlertDialog;
22+
import android.content.DialogInterface;
2123
import android.os.Bundle;
2224
import android.util.Log;
2325
import android.widget.Toast;
2426

25-
import java.io.IOException;
26-
import java.io.InputStream;
27-
28-
import org.apache.http.Header;
29-
import org.apache.http.HttpEntity;
30-
31-
3227
import com.loopj.android.http.AsyncHttpClient;
3328
import com.loopj.android.http.BinaryHttpResponseHandler;
3429
import com.loopj.android.http.RequestHandle;
3530
import com.loopj.android.http.ResponseHandlerInterface;
3631
import com.loopj.android.http.sample.util.SecureSocketFactory;
3732

33+
import org.apache.http.Header;
34+
import org.apache.http.HttpEntity;
35+
36+
import java.io.IOException;
37+
import java.io.InputStream;
3838
import java.security.KeyManagementException;
3939
import java.security.KeyStore;
4040
import java.security.KeyStoreException;
@@ -43,10 +43,9 @@
4343
import java.security.cert.CertificateException;
4444

4545
/**
46-
* This sample demonstrates the implementation of self-signed CA's and connection
47-
* to servers with such certificates. Be sure to read 'res/raw/custom_ca.txt'
48-
* for how-to instructions on how to generate a BKS file necessary for this
49-
* sample.
46+
* This sample demonstrates the implementation of self-signed CA's and connection to servers with
47+
* such certificates. Be sure to read 'res/raw/custom_ca.txt' for how-to instructions on how to
48+
* generate a BKS file necessary for this sample.
5049
*
5150
* @author Noor Dawod <[email protected]>
5251
*/
@@ -75,28 +74,66 @@ protected void onCreate(Bundle savedInstanceState) {
7574
is = getResources().openRawResource(R.raw.store);
7675
store.load(is, STORE_PASS.toCharArray());
7776
getAsyncHttpClient().setSSLSocketFactory(new SecureSocketFactory(store, STORE_ALIAS));
78-
} catch(IOException e) {
77+
} catch (IOException e) {
7978
throw new KeyStoreException(e);
80-
} catch(CertificateException e) {
79+
} catch (CertificateException e) {
8180
throw new KeyStoreException(e);
82-
} catch(NoSuchAlgorithmException e) {
81+
} catch (NoSuchAlgorithmException e) {
8382
throw new KeyStoreException(e);
84-
} catch(KeyManagementException e) {
83+
} catch (KeyManagementException e) {
8584
throw new KeyStoreException(e);
86-
} catch(UnrecoverableKeyException e) {
85+
} catch (UnrecoverableKeyException e) {
8786
throw new KeyStoreException(e);
8887
} finally {
8988
AsyncHttpClient.silentCloseInputStream(is);
9089
}
91-
} catch(KeyStoreException e) {
90+
} catch (KeyStoreException e) {
9291
Log.e(LOG_TAG, "Unable to initialize key store", e);
9392
Toast.makeText(
94-
this,
95-
"Please read res/raw/custom_ca.txt\nto learn how to create your own\nkey store containing a custom CA",
96-
Toast.LENGTH_LONG).show();
93+
this,
94+
"Please read res/raw/custom_ca.txt\nto learn how to create your own\nkey store containing a custom CA",
95+
Toast.LENGTH_LONG).show();
96+
showCustomCAHelp();
9797
}
9898
}
9999

100+
/**
101+
* Returns contents of `custom_ca.txt` as CharSequence
102+
*
103+
* @return contents of custom_ca.txt from Assets
104+
*/
105+
private CharSequence getReadmeText() {
106+
String rtn = "";
107+
try {
108+
InputStream stream = getResources().openRawResource(R.raw.custom_ca);
109+
java.util.Scanner s = new java.util.Scanner(stream)
110+
.useDelimiter("\\A");
111+
rtn = s.hasNext() ? s.next() : "";
112+
} catch (Exception | Error e) {
113+
Log.e(LOG_TAG, "License couldn't be retrieved", e);
114+
}
115+
return rtn;
116+
}
117+
118+
/**
119+
* Will display AlertDialog reading `custom_ca.txt` from Assets, to avoid strict Lint issue
120+
*/
121+
private void showCustomCAHelp() {
122+
AlertDialog.Builder builder = new AlertDialog.Builder(this);
123+
builder.setTitle(R.string.title_custom_ca);
124+
builder.setMessage(getReadmeText());
125+
builder.setNeutralButton(android.R.string.cancel,
126+
new DialogInterface.OnClickListener() {
127+
128+
@Override
129+
public void onClick(DialogInterface dialog, int which) {
130+
dialog.dismiss();
131+
}
132+
}
133+
);
134+
builder.show();
135+
}
136+
100137
@Override
101138
public int getSampleTitle() {
102139
return R.string.title_custom_ca;

sample/src/main/java/com/loopj/android/http/sample/util/SecureSocketFactory.java

Lines changed: 80 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,19 @@
1818

1919
package com.loopj.android.http.sample.util;
2020

21+
import android.annotation.TargetApi;
2122
import android.util.Log;
2223

2324
import com.loopj.android.http.AsyncHttpClient;
25+
2426
import org.apache.http.conn.ssl.SSLSocketFactory;
2527

26-
import java.lang.reflect.Field;
2728
import java.io.ByteArrayInputStream;
2829
import java.io.IOException;
2930
import java.io.InputStream;
31+
import java.lang.reflect.Field;
3032
import java.net.InetAddress;
3133
import java.net.Socket;
32-
import java.net.UnknownHostException;
33-
import java.security.cert.Certificate;
34-
import java.security.cert.X509Certificate;
3534
import java.security.InvalidKeyException;
3635
import java.security.KeyManagementException;
3736
import java.security.KeyStore;
@@ -40,16 +39,17 @@
4039
import java.security.NoSuchProviderException;
4140
import java.security.SignatureException;
4241
import java.security.UnrecoverableKeyException;
42+
import java.security.cert.Certificate;
4343
import java.security.cert.CertificateException;
4444
import java.security.cert.CertificateFactory;
45+
import java.security.cert.X509Certificate;
4546

4647
import javax.net.ssl.SSLContext;
4748
import javax.net.ssl.TrustManager;
4849
import javax.net.ssl.X509TrustManager;
4950

5051
/**
51-
* A class to authenticate a secured connection against a custom CA using a
52-
* BKS store.
52+
* A class to authenticate a secured connection against a custom CA using a BKS store.
5353
*
5454
* @author Noor Dawod <[email protected]>
5555
*/
@@ -61,25 +61,20 @@ public class SecureSocketFactory extends SSLSocketFactory {
6161
private final X509Certificate[] acceptedIssuers;
6262

6363
/**
64-
* Instantiate a new secured factory pertaining to the passed store. Be sure
65-
* to initialize the store with the password using
66-
* {@link java.security.KeyStore#load(java.io.InputStream, char[])} method.
64+
* Instantiate a new secured factory pertaining to the passed store. Be sure to initialize the
65+
* store with the password using {@link java.security.KeyStore#load(java.io.InputStream,
66+
* char[])} method.
6767
*
6868
* @param store The key store holding the certificate details
6969
* @param alias The alias of the certificate to use
70-
* @throws CertificateException
71-
* @throws NoSuchAlgorithmException
72-
* @throws KeyManagementException
73-
* @throws KeyStoreException
74-
* @throws UnrecoverableKeyException
7570
*/
7671
public SecureSocketFactory(KeyStore store, String alias)
77-
throws
78-
CertificateException,
79-
NoSuchAlgorithmException,
80-
KeyManagementException,
81-
KeyStoreException,
82-
UnrecoverableKeyException {
72+
throws
73+
CertificateException,
74+
NoSuchAlgorithmException,
75+
KeyManagementException,
76+
KeyStoreException,
77+
UnrecoverableKeyException {
8378

8479
super(store);
8580

@@ -88,10 +83,10 @@ public SecureSocketFactory(KeyStore store, String alias)
8883

8984
// Turn it to X509 format.
9085
InputStream is = new ByteArrayInputStream(rootca.getEncoded());
91-
X509Certificate x509ca = (X509Certificate)CertificateFactory.getInstance("X.509").generateCertificate(is);
86+
X509Certificate x509ca = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is);
9287
AsyncHttpClient.silentCloseInputStream(is);
9388

94-
if(null == x509ca) {
89+
if (null == x509ca) {
9590
throw new CertificateException("Embedded SSL certificate has expired.");
9691
}
9792

@@ -103,72 +98,72 @@ public SecureSocketFactory(KeyStore store, String alias)
10398

10499
sslCtx = SSLContext.getInstance("TLS");
105100
sslCtx.init(
106-
null,
107-
new TrustManager[] {
108-
new X509TrustManager() {
109-
@Override
110-
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
111-
}
112-
113-
@Override
114-
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
115-
Exception error = null;
116-
117-
if(null == chain || 0 == chain.length) {
118-
error = new CertificateException("Certificate chain is invalid.");
119-
} else if(null == authType || 0 == authType.length()) {
120-
error = new CertificateException("Authentication type is invalid.");
121-
} else {
122-
Log.i(LOG_TAG, "Chain includes " + chain.length + " certificates.");
123-
try {
124-
for(X509Certificate cert : chain) {
125-
Log.i(LOG_TAG, "Server Certificate Details:");
126-
Log.i(LOG_TAG, "---------------------------");
127-
Log.i(LOG_TAG, "IssuerDN: " + cert.getIssuerDN().toString());
128-
Log.i(LOG_TAG, "SubjectDN: " + cert.getSubjectDN().toString());
129-
Log.i(LOG_TAG, "Serial Number: " + cert.getSerialNumber());
130-
Log.i(LOG_TAG, "Version: " + cert.getVersion());
131-
Log.i(LOG_TAG, "Not before: " + cert.getNotBefore().toString());
132-
Log.i(LOG_TAG, "Not after: " + cert.getNotAfter().toString());
133-
Log.i(LOG_TAG, "---------------------------");
134-
135-
// Make sure that it hasn't expired.
136-
cert.checkValidity();
137-
138-
// Verify the certificate's public key chain.
139-
cert.verify(rootca.getPublicKey());
101+
null,
102+
new TrustManager[]{
103+
new X509TrustManager() {
104+
@Override
105+
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
106+
}
107+
108+
@Override
109+
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
110+
Exception error = null;
111+
112+
if (null == chain || 0 == chain.length) {
113+
error = new CertificateException("Certificate chain is invalid.");
114+
} else if (null == authType || 0 == authType.length()) {
115+
error = new CertificateException("Authentication type is invalid.");
116+
} else {
117+
Log.i(LOG_TAG, "Chain includes " + chain.length + " certificates.");
118+
try {
119+
for (X509Certificate cert : chain) {
120+
Log.i(LOG_TAG, "Server Certificate Details:");
121+
Log.i(LOG_TAG, "---------------------------");
122+
Log.i(LOG_TAG, "IssuerDN: " + cert.getIssuerDN().toString());
123+
Log.i(LOG_TAG, "SubjectDN: " + cert.getSubjectDN().toString());
124+
Log.i(LOG_TAG, "Serial Number: " + cert.getSerialNumber());
125+
Log.i(LOG_TAG, "Version: " + cert.getVersion());
126+
Log.i(LOG_TAG, "Not before: " + cert.getNotBefore().toString());
127+
Log.i(LOG_TAG, "Not after: " + cert.getNotAfter().toString());
128+
Log.i(LOG_TAG, "---------------------------");
129+
130+
// Make sure that it hasn't expired.
131+
cert.checkValidity();
132+
133+
// Verify the certificate's public key chain.
134+
cert.verify(rootca.getPublicKey());
135+
}
136+
} catch (InvalidKeyException e) {
137+
error = e;
138+
} catch (NoSuchAlgorithmException e) {
139+
error = e;
140+
} catch (NoSuchProviderException e) {
141+
error = e;
142+
} catch (SignatureException e) {
143+
error = e;
144+
}
145+
}
146+
if (null != error) {
147+
Log.e(LOG_TAG, "Certificate error", error);
148+
throw new CertificateException(error);
140149
}
141-
} catch(InvalidKeyException e) {
142-
error = e;
143-
} catch(NoSuchAlgorithmException e) {
144-
error = e;
145-
} catch(NoSuchProviderException e) {
146-
error = e;
147-
} catch(SignatureException e) {
148-
error = e;
150+
}
151+
152+
@Override
153+
public X509Certificate[] getAcceptedIssuers() {
154+
return acceptedIssuers;
149155
}
150156
}
151-
if(null != error) {
152-
Log.e(LOG_TAG, "Certificate error", error);
153-
throw new CertificateException(error);
154-
}
155-
}
156-
157-
@Override
158-
public X509Certificate[] getAcceptedIssuers() {
159-
return acceptedIssuers;
160-
}
161-
}
162-
},
163-
null
157+
},
158+
null
164159
);
165160

166161
setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
167162
}
168163

169164
@Override
170165
public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
171-
throws IOException, UnknownHostException {
166+
throws IOException {
172167

173168
injectHostname(socket, host);
174169
return sslCtx.getSocketFactory().createSocket(socket, host, port, autoClose);
@@ -180,21 +175,21 @@ public Socket createSocket() throws IOException {
180175
}
181176

182177
/**
183-
* Pre-ICS Android had a bug resolving HTTPS addresses. This workaround
184-
* fixes that bug.
178+
* Pre-ICS Android had a bug resolving HTTPS addresses. This workaround fixes that bug.
185179
*
186180
* @param socket The socket to alter
187-
* @param host Hostname to connect to
188-
* @see https://code.google.com/p/android/issues/detail?id=13117#c14
181+
* @param host Hostname to connect to
182+
* @see <a href="http://github.com/%3C/span%3Ehttps://code.google.com/p/android/issues/detail?id=13117#c14%3Cspan%20class="x x-first x-last">">https://code.google.com/p/android/issues/detail?id=13117#c14</a>
189183
*/
184+
@TargetApi(4)
190185
private void injectHostname(Socket socket, String host) {
191-
if(android.os.Build.VERSION.SDK_INT < 14) {
192-
try {
186+
try {
187+
if (android.os.Build.VERSION.SDK_INT < 14) {
193188
Field field = InetAddress.class.getDeclaredField("hostName");
194189
field.setAccessible(true);
195190
field.set(socket.getInetAddress(), host);
196-
} catch(Exception ignored) {
197191
}
192+
} catch (Exception ignored) {
198193
}
199194
}
200195
}

0 commit comments

Comments
 (0)