android用okhttp3连接自签名的https
想在android中使用加密的https连接,但机构颁发的加密证书很贵?可以使用自签名的证书哦,零成本。android访问自签名的https会报错,通常做法是闭着眼睛,直接忽略掉证书验证。如果可以简单忽略,那何必使用加密https呢;如果可以简单忽略,那安全性如何保证呢?这里有不忽略的方法。
准备工作
- 生成一个自签名证书(待续)
- 使用这个自签名证书,提供https服务(待续)
- 使用这个自签名证书,生成hello.cer证书文件,供android上使用
生成自签名证书
openssl genrsa -des3 -out server.key.en 2048
openssl rsa -in server.key.en -out server.key
openssl req -new -key server.key -out server.csr
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
openssl x509 -in server.crt -out server.cer -outform der
- 生成一个非对称加密算法rsa的密钥文件(server.key.en),包含密钥对,公钥和私钥。
- 生成密钥文件必须输入密码,所以密钥文件(server.key.en)有密码包含,第二步删除这个密码。
- 在密钥文件的基础上,生成证书请求文件(server.csr)。证书请求文件需要得到CA组织的签名认证后,才是证书。
- CA组织的签名认证是收费服务,所以我们选择自签名,自己给自己签名。把自己当作是CA组织,用自己的密钥文件,对自己的证书请求文件签名。自己给自己认证。文件(server.crt)就是自签名证书。
- 自签名证书(server.crt)因为格式的原因,不能在android中直接使用。把它转换为android接受的cer格式。
在android上的处理
- 思路。因为自签名证书不在android信任的KeyStore中,所以就报错。现在使用自签名的证书hello.cer,生成一个新的KeyStore,把它作为受信任的KeyStore提供给okhhtp3。在https请求时,okhhtp3用我们自己受信任的KeyStore去验证服务器,就保证了安全性。
- java实现:
public static String httpsGet(Context context, String url) {
String rst = null;
SSLSocketFactory sslSocketFactory = null;
X509TrustManager trustManager = null;
HostnameVerifier hostnameVerifier = null;
InputStream is = null;
try {
is = context.getAssets().open("hello.cer");
Certificate certificate = CertificateFactory.getInstance("X.509")
.generateCertificate(is);
if (is != null) {
is.close();
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
keyStore.setCertificateEntry("ca", certificate);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory
.getTrustManagers();
if (trustManagers.length != 1
|| !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException(
"Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
trustManager = (X509TrustManager) trustManagers[0];
hostnameVerifier = new HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
};
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { trustManager }, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (CertificateException e1) {
e1.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
Request request = new Request.Builder().url(url).build();
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.hostnameVerifier(hostnameVerifier)
.sslSocketFactory(sslSocketFactory, trustManager).build();
try {
Response response = okHttpClient.newCall(request).execute();
if (response.isSuccessful()) {
rst = response.body().string();
}
} catch (IOException e) {
e.printStackTrace();
}
return rst;
}