[ACCEPTED]-Self-signed SSL acceptance on Android-self-signed
I have this functionality in exchangeIt, which 10 connects to Microsoft exchange via WebDav. Here's 9 some code to create an HttpClient which 8 will connect to self signed cert's via SSL:
SchemeRegistry schemeRegistry = new SchemeRegistry();
// http scheme
schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
// https scheme
schemeRegistry.register(new Scheme("https", new EasySSLSocketFactory(), 443));
HttpParams params = new BasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS, 30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, new ConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
ClientConnectionManager cm = new ThreadSafeClientConnManager(params, schemeRegistry);
The 7 EasySSLSocketFactory is here, and the EasyX509TrustManager 6 is here.
The code for exchangeIt is open source, and 5 hosted on googlecode here, if you have any issues. I'm 4 not actively working on it anymore, but 3 the code should work.
Note that since Android 2 2.2 the process has changed a bit, so check 1 this to make the code above work.
As EJP correctly commented, "Readers should note that this technique is radically insecure. SSL is not secure unless at least one peer is authenticated. See RFC 2246."
Having said 2 that, here's another way, without any extra 1 classes:
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
private void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier(){
public boolean verify(String hostname, SSLSession session) {
return true;
}});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager(){
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(
context.getSocketFactory());
} catch (Exception e) { // should never happen
e.printStackTrace();
}
}
I faced this issue yesterday, while migrating 26 our company's RESTful API to HTTPS, but 25 using self-signed SSL certificates.
I've 24 looking everywhere, but all the "correct" marked 23 answers I've found consisted of disabling 22 certificate validation, clearly overriding 21 all the sense of SSL.
I finally came to a 20 solution:
Create Local KeyStore
To enable your app to validate 19 your self-signed certificates, you need 18 to provide a custom keystore with the certificates 17 in a manner that Android can trust your 16 endpoint.
The format for such custom keystores 15 is "BKS" from BouncyCastle, so you need the 14 1.46 version of BouncyCastleProvider that 13 you can download here.
You also need your self-signed 12 certificate, I will assume it's named self_cert.pem
.
Now 11 the command for creating your keystore is:
<!-- language: lang-sh -->
$ keytool -import -v -trustcacerts -alias 0 \
-file *PATH_TO_SELF_CERT.PEM* \
-keystore *PATH_TO_KEYSTORE* \
-storetype BKS \
-provider org.bouncycastle.jce.provider.BouncyCastleProvider \
-providerpath *PATH_TO_bcprov-jdk15on-146.jar* \
-storepass *STOREPASS*
PATH_TO_KEYSTORE
points 10 to a file where your keystore will be created. It 9 MUST NOT EXIST.
PATH_TO_bcprov-jdk15on-146.jar.JAR
is the path to the downloaded .jar libary.
STOREPASS
is 8 your newly created keystore password.
- Include KeyStore in your Application
Copy 7 your newly created keystore from PATH_TO_KEYSTORE
to res/raw/certs.bks
(certs.bks is 6 just the file name; you can use whatever 5 name you wish)
Create a key in res/values/strings.xml
with
<!-- language: lang-xml -->
<resources>
...
<string name="store_pass">*STOREPASS*</string>
...
</resources>
Create a this class that inherits
DefaultHttpClient
import android.content.Context; import android.util.Log; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.HttpParams; import java.io.IOException; import java.io.InputStream; import java.security.*; public class MyHttpClient extends DefaultHttpClient { private static Context appContext = null; private static HttpParams params = null; private static SchemeRegistry schmReg = null; private static Scheme httpsScheme = null; private static Scheme httpScheme = null; private static String TAG = "MyHttpClient"; public MyHttpClient(Context myContext) { appContext = myContext; if (httpScheme == null || httpsScheme == null) { httpScheme = new Scheme("http", PlainSocketFactory.getSocketFactory(), 80); httpsScheme = new Scheme("https", mySSLSocketFactory(), 443); } getConnectionManager().getSchemeRegistry().register(httpScheme); getConnectionManager().getSchemeRegistry().register(httpsScheme); } private SSLSocketFactory mySSLSocketFactory() { SSLSocketFactory ret = null; try { final KeyStore ks = KeyStore.getInstance("BKS"); final InputStream inputStream = appContext.getResources().openRawResource(R.raw.certs); ks.load(inputStream, appContext.getString(R.string.store_pass).toCharArray()); inputStream.close(); ret = new SSLSocketFactory(ks); } catch (UnrecoverableKeyException ex) { Log.d(TAG, ex.getMessage()); } catch (KeyStoreException ex) { Log.d(TAG, ex.getMessage()); } catch (KeyManagementException ex) { Log.d(TAG, ex.getMessage()); } catch (NoSuchAlgorithmException ex) { Log.d(TAG, ex.getMessage()); } catch (IOException ex) { Log.d(TAG, ex.getMessage()); } catch (Exception ex) { Log.d(TAG, ex.getMessage()); } finally { return ret; } } }
Now simply 4 use an instance of **MyHttpClient**
as you would with **DefaultHttpClient**
to 3 make your HTTPS queries, and it will use 2 and validate correctly your self-signed 1 SSL certificates.
HttpResponse httpResponse;
HttpPost httpQuery = new HttpPost("https://yourserver.com");
... set up your query ...
MyHttpClient myClient = new MyHttpClient(myContext);
try{
httpResponse = myClient.(peticionHttp);
// Check for 200 OK code
if (httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {
... do whatever you want with your response ...
}
}catch (Exception ex){
Log.d("httpError", ex.getMessage());
}
Unless I missed something, the other answers 24 on this page are DANGEROUS, and are functionally 23 equivalent to not using SSL at all. If 22 you trust self-signed certificates without 21 doing further checks to make sure the certificates 20 are the ones that you are expecting, then 19 anyone can create a self-signed certificate 18 and can pretend to be your server. At that 17 point, you have no real security.
The only legitimate 16 way to do this (without writing a full SSL 15 stack) is to add an additional trusted anchor 14 to be trusted during the certificate verification 13 process. Both involve hard-coding the trusted 12 anchor certificate into your app and adding 11 it to whatever trusted anchors that the 10 OS provides (or else you won't be able to 9 connect to your site if you get a real certificate).
I'm 8 aware of two ways to do this:
Create a custom 7 trust store as described at http://www.ibm.com/developerworks/java/library/j-customssl/#8
Create a custom 6 instance of X509TrustManager and override 5 the getAcceptedIssuers method to return 4 an array that contains your certificate:
public X509Certificate[] getAcceptedIssuers() { X509Certificate[] trustedAnchors = super.getAcceptedIssuers(); /* Create a new array with room for an additional trusted certificate. */ X509Certificate[] myTrustedAnchors = new X509Certificate[trustedAnchors.length + 1]; System.arraycopy(trustedAnchors, 0, myTrustedAnchors, 0, trustedAnchors.length); /* Load your certificate. Thanks to http://stackoverflow.com/questions/11857417/x509trustmanager-override-without-allowing-all-certs for this bit. */ InputStream inStream = new FileInputStream("fileName-of-cert"); CertificateFactory cf = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate)cf.generateCertificate(inStream); inStream.close(); /* Add your anchor cert as the last item in the array. */ myTrustedAnchors[trustedAnchors.length] = cert; return myTrustedAnchors; }
Note 3 that this code is completely untested and 2 may not even compile, but should at least 1 steer you in the right direction.
Brian Yarger's answer works in Android 2.2 3 as well if you modify the bigger createSocket 2 method overload as follows. It took me a 1 while to get self-signed SSLs working.
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose);
}
On Android, HttpProtocolParams
accepts ProtocolVersion
rather than HttpVersion
.
ProtocolVersion pv = new ProtocolVersion("HTTP", 1, 1);
HttpProtocolParams.setVersion(params, pv);
0
@Chris - Posting this as an answer since 11 I can't add comments (yet). I'm wondering 10 if your approach is supposed to work when 9 using a webView. I can't get it do so on 8 Android 2.3 - instead I just get a white 7 screen.
After some more searching, I came 6 across this simple fix for handling SSL errors in a webView which worked like a charm for 5 me.
In the handler I check to see if I'm 4 in a special dev mode and call handler.proceed(), otherwise 3 I call handler.cancel(). This allows 2 me to do development against a self-signed 1 cert on a local website.
There are a-lot alternatives for this use 4 case. If you don't want to have any custom 3 code in your code base such as custom TrustManager
I 2 would suggest to try GitHub - SSLContext Kickstart and the following 1 code snippet:
<dependency>
<groupId>io.github.hakky54</groupId>
<artifactId>sslcontext-kickstart</artifactId>
<version>6.7.0</version>
</dependency>
SSL configuration
SSLFactory sslFactory = SSLFactory.builder()
.withUnsafeTrustMaterial()
.build();
SSLContext sslContext = sslFactory.getSslContext();
SSLSocketFactory sslSocketFactory = sslFactory.getSslSocketFactory();
HttpClient configuration
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("https", sslSocketFactory, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
HttpClient httpClient = new DefaultHttpClient(ccm, params);
HttpsUrlConnection
HttpsURLConnection.setDefaultSSLSocketFactory(sslSocketFactory);
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.