之前用的http,一切正常,自从改为https,发现好多问题,在此写了https认证的步骤以及怎么解决其中遇到的难点。
首先需要两份证书,服务器的证书ca.cer 和客户端证书client.bks。
当初给到客户端的是 client.p12,p12证书安卓也能支持。
证书有多种格式,如cer bks p12 jks, 涉及的证书转化推荐一个转化工具portecle,下载地址:https://sourceforge.net/projects/portecle/,还需要加载bcprov-jdk15on-1.59 jar包,放到C:\Program Files\Java\jre1.8.0_152\lib\ext目录。
okhttp请求的认证流程
SslOkHttpClientUtils工具类,生成OkHttpClient,执行一次即可。
public class SslOkHttpClientUtils {
public static final String TAG = "SslOkHttpClientUtils";
public static final String KEY_STORE_TYPE_P12 = "PKCS12";//证书类型
private static OkHttpClient client;
private static SSLContext sslContext;
public static String sessionid;
public static String cookieval;
public static OkHttpClient getSslClient(Context context) {
try {
if(client == null) {
InputStream trustKey = context.getAssets().open("ca.cer");
InputStream clientKeyP12 = context.getAssets().open("client.p12");
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
sslContext = SSLContext.getInstance("TLS");
Log.d(TAG, "getSslClient: 0");
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
Log.d(TAG, "getSslClient: 0.1");
trustStore.load(null);
// trustStore.load(trustKey, trustPassword.toCharArray());
Log.d(TAG, "getSslClient: 1");
trustStore.setCertificateEntry("0", certificateFactory.generateCertificate(trustKey));
if (trustKey != null) {
trustKey.close();
}
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
keyStore.load(clientKeyP12, "123456".toCharArray());
/* KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(clientKeyP12, clientPassword.toCharArray());*/
Log.d(TAG, "getSslClient: 2");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, "123456".toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
client = new OkHttpClient().newBuilder()
.sslSocketFactory(sslContext.getSocketFactory(), trustManager)
.followRedirects(false)
.followSslRedirects(false)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
Log.d(TAG, "verify: "+hostname);
if(hostname.equals("要加密的域名"))
return true;
return false;
}
})
.build();
return client;
}
return client;
} catch (Exception e) {
e.printStackTrace();
android.util.Log.d(TAG, "exception222:"+e.toString());
return null;
}
}
}
然后选择在需要https请求的地方执行以下代码
try {
JSONObject result = new JSONObject();
try {
result.put("version", CommonUtils.getVersionName(mContext));
result.put("system", "android");
result.put("appType", "familys");
} catch (JSONException e) {
e.printStackTrace();
}
final RequestBody body = RequestBody.create(MediaType.parse("application/json"), result.toString());
String requestUrl = "指定的https地址";
android.util.Log.d(TAG, "url: "+requestUrl+",, body:"+result.toString());
final Retrofit retrofit = new Retrofit.Builder()
.baseUrl(requestUrl)
.client(SslOkHttpClientUtils.getSslClient(mContext))
.addConverterFactory(new StringConverterFactory())
.build();
WebRepository repository= retrofit.create(WebRepository.class);
retrofit2.Call data = repository.getMessage(body);
data.enqueue(new retrofit2.Callback() {
@Override
public void onResponse(retrofit2.Call call, retrofit2.Response response) {
Log.d(TAG, "onResponse: --ok--"+response.headers());
Log.d(TAG, "onResponse: --ok--"+response.body());
}
@Override
public void onFailure(retrofit2.Call call, Throwable t) {
Log.d(TAG, "onFailure: --err--"+t.toString());
}
});
}catch (Exception ex){
ex.printStackTrace();
android.util.Log.d(TAG, "exception:"+ex.toString());
}
用的是Retrofit网络框架,对指定https地址进行网络请求,回调成功或失败的结果。
public interface WebRepository {
@GET("/")
Call getHtml();
@Headers({"Content-Type: application/json","Accept: application/json"})//需要添加头
@POST("/appconf/urlAppType")
Call getMessage(@Body RequestBody info);
}
总体来说安卓https认证比较简单,另一篇主要讲解webView中https的认证。