Я видел почти все вопросы, связанные с этой темой, о переполнении стека, но не смог найти решение.
Согласно документации , мы можем использовать самозаверяющий сертификат следующим образом:
// 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"
Как правильно реализовать соединение https с самозаверяющим сертификатом в Android (без дополнительных библиотек, просто используя HttpsUrlConnection)?
Примечание. Я не хочу использовать какую-либо библиотеку, включая 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-соединения с самозаверяющим сертификатом.
"A TrustManager
- это то, что система использует для проверки сертификатов с сервера и - путем создания одного из KeyStore
с одним или несколькими CA - это будут единственные CA, которым доверяет TrustManager
«. doc
Следовательно, нам нужно реализовать TrustManager
, но как мы должны это сделать?Любое предложение поможет.
Спасибо