안드로이드에서 HTTPS 통신을 할 때, SSL 예외가 발생하는 경우, 아래와 같은 방법을 통해서 예외를 회피 할 수 있다.
이 방법은 모든 URL에 동작하는 방법으로 별도의 인증서 없이 사용하는 방법이다.
URLConnection을 이용한 HTTPS 통신
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());
URL url = new URL("https://www.example.com");
URLConnection conn = url.openConnection();
} catch (Exception e) { // should never happen
e.printStackTrace();
}
HTTPClient를 이용한 HTTPS 통신
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
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("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
HttpClient httpClient = new DefaultHttpClient(ccm, params);
HttpGet httpRequest = new HttpGet("https://www.example.com");
HttpResponse httpResponse = httpClient.execute(httpRequest);
} catch ( KeyStoreException e ) {
e.printStackTrace();
} catch ( KeyManagementException e ) {
e.printStackTrace();
} catch ( UnrecoverableKeyException e ) {
e.printStackTrace();
} catch ( NoSuchAlgorithmException e ) {
e.printStackTrace();
} catch ( CertificateException e ) {
e.printStackTrace();
}
public class MySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");
public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { }
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}
public MySSLSocketFactory(SSLContext context) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
super(null);
sslContext = context;
}
@Override
public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}
위의 ThreadSafeClientConnectionManager를 이용하여 서버에 여러번 요청을 하다보면 HttpClient.execute()를 수행할 때, 서버에 요청을 하지 못하고 Block되는 경우가 있습니다. 그래서 ThreadSafeClientConnectionManager 대신 SingleClientConnManager를 사용하여 테스트해 본 결과 Block되는 현상이 사라졌습니다. 위의 MySSLSocketFactory 클래스는 여전히 사용됩니다. 기본 SSLSocketFactory를 사용하면 GalaxyS3에는 예외가 발생하는군요. 코드는 아래와 같습니다.
public class MyHttpClient {
private final int DEFAULT_TIMEOUT = 5000;
private ClientConnectionManager mClientConnectionManager;
private HttpParams mHttpParams;
private HttpClient mHttpClient;
private void init() {
setupHttps();
mHttpClient = new DefaultHttpClient(mClientConnectionManager, mHttpParams);
setTimemoutMillis(DEFAULT_TIMEOUT);
}
private void setupHttps() {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new MySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
mHttpParams = params;
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
mClientConnectionManager = new SingleClientConnManager(params, registry);
}
public void setTimeoutMillis(int milliseconds) {
HttpParams params = mHttpClient.getParams();
HttpConnectionParams.setConnectionTimeout(params, milliseconds);
HttpConnectionParams.setSoTimeout(params, milliseconds);
}
}
링크 모음
http://stackoverflow.com/questions/2012497/accepting-a-certificate-for-https-on-android
http://dhjin.egloos.com/2527436
'안드로이드' 카테고리의 다른 글
롤리팝 메모리 누수 버그 (1) | 2015.11.07 |
---|---|
안드로이드 앱 강제 종료 재현하기 (0) | 2015.11.07 |
APK 확장 파일 사용하기 - APK Expansion Files (1) | 2014.07.21 |
안드로이드에서 구글 드라이브와 연동시 발생하는 오류 해결 (0) | 2014.03.07 |
한글이 포함된 javadoc을 생성할 때 발생하는 오류 (0) | 2014.02.27 |
버튼 패딩 제거하기 (0) | 2014.01.08 |
구글 플러그인 설치하기 (Google Plugin for Eclipse) (0) | 2014.01.07 |
[안드로이드] ImageCache 사용 중, 이미지의 크기가 변하는 현상 (0) | 2013.12.13 |
[안드로이드] RenderScript 빌드 오류 문제 해결 (0) | 2013.11.12 |
[안드로이드] ActionBar Indicator 커스텀마이징하기 (0) | 2013.10.30 |