我几乎在Stack Overflow上看到了与此主题相关的所有问题,但找不到解决方案。
根据documentation,我们可以使用自签名证书,如下所示:
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(new FileInputStream("mycert.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the URLConnection to use a SocketFactory from our SSLContext
URL url = new URL("https://ip/address");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
但这会抛出java.security.cert.CertPathValidatorException:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException:的信任锚 找不到认证路径。
也尝试过:
InputStream keystoreInput = context.getResources().openRawResource(R.raw.cert_keystore);
KeyStore keyStore = KeyStore.getInstance("bks");
keyStore.load(keystoreInput, "pass".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, "pass".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(keyStore);
SSLContext context= SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
URL url = new URL("https://ip/address");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
InputStream in = urlConnection.getInputStream();
但这会产生相同的错误。
我还确定证书有效,因为我可以按以下方式发布请求:
curl -v -s -k --key mykey.key--cert mycert.crt -X GET "https://ip/path"
注意:我不想使用任何库,包括OkHttp
,Apache
等。
最后找到了解决方案,这是主要部分:
InputStream keystoreInput = context.getResources().openRawResource(R.raw.cert_keystore);
KeyStore keyStore = KeyStore.getInstance("bks");
keyStore.load(keystoreInput, "pass".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keyStore, "pass".toCharArray());
SSLContext context= SSLContext.getInstance("TLS");
context.init(kmf.getKeyManagers(), new TrustManager[] {new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, null);
URL url = new URL("https://ip/address");
HttpsURLConnection urlConnection =
(HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(context.getSocketFactory());
// skip hostname security check
urlConnection.setHostnameVerifier(new AllowAllHostnameVerifier());
InputStream in = urlConnection.getInputStream();
但是我不确定是否存在使用自签名证书实现HTTPS连接的更安全方法。
“ TrustManager
是系统用来验证来自服务器的证书的系统,并且通过从具有一个或多个CA的KeyStore
创建一个证书,这些将是该{{ 1}}。” doc
因此,我们需要实现TrustManager
,但是我们应该怎么做呢?
任何建议都会有所帮助。
谢谢
答案 0 :(得分:0)
在WebServiceHelper类中,请使用以下方法:
public class WebServiceHelper {
public static final String ENDPOINT = "your address";
private static WebServiceHelper instance;
private Services service;
private Context mContext;
private OkHttpClient getMyOkHttpClient(String serverUrl) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
builder.interceptors().add((new HttpLoggingInterceptor()).setLevel(HttpLoggingInterceptor.Level.BODY));
} else {
builder.interceptors().add((new HttpLoggingInterceptor()).setLevel(HttpLoggingInterceptor.Level.NONE));
}
builder.cookieJar(new JavaNetCookieJar(new CookieManager()))
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS);
if ((serverUrl.toLowerCase()).contains("https")) initUnSafeSSL(builder);
return builder.build();
}
private void initUnSafeSSL(OkHttpClient.Builder builder1) {
try {
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
builder1.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
builder1.hostnameVerifier((hostname, session) -> true);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private WebServiceHelper(Context context) {
mContext = context;
Retrofit retrofit = createAdapter(ENDPOINT).build();
service = retrofit.create(Services.class);
}
public static WebServiceHelper getInstance(Context context) {
if (instance == null) {
instance = new WebServiceHelper(context);
}
return instance;
}
private Retrofit.Builder createAdapter(String serverChoice) {
return new Retrofit.Builder()
.baseUrl(serverChoice)
.client(getMyOkHttpClient(serverChoice))
.addConverterFactory(GsonConverterFactory.create());
}