Android 使用改装2访问安全Web服务时获取CertPathValidator异常
我有一个Android应用程序,可以毫无问题地访问安全的Web服务。它使用我自己的类,它扩展了ApacheDefaultClientHttp 别名,证书和私钥是从KeyChain获得的(我不想将证书作为原始文件合并到应用程序中)。该应用程序工作正常,但现在我想重构Http客户端并使用2。这是我使用OkHttpClient的新类Android 使用改装2访问安全Web服务时获取CertPathValidator异常,android,web-services,security,ssl,retrofit2,Android,Web Services,Security,Ssl,Retrofit2,我有一个Android应用程序,可以毫无问题地访问安全的Web服务。它使用我自己的类,它扩展了ApacheDefaultClientHttp 别名,证书和私钥是从KeyChain获得的(我不想将证书作为原始文件合并到应用程序中)。该应用程序工作正常,但现在我想重构Http客户端并使用2。这是我使用OkHttpClient的新类 public class SslOkHttpClient { private static final String LOGGER = SslOkHttpClient.
public class SslOkHttpClient {
private static final String LOGGER = SslOkHttpClient.class.getName();
private static String alias;
private static Key privateKey;
private static X509Certificate[] certificates;
private static String KEYSTORE_PASS = "password";
public static OkHttpClient getOkHttpClient(Context context) throws GeneralSecurityException, IOException, IllegalStateException {
if (alias == null)
alias = HttpsUtils.getAlias(context);
if (privateKey == null)
privateKey = HttpsUtils.getPrivateKey(context, alias);
if (certificates == null)
certificates = HttpsUtils.getCertificateChain(context, alias);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.tlsVersions(TlsVersion.TLS_1_2)
.build();
KeyStore ks = getKeystore();
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getSslSocketFactory(ks), (X509TrustManager) getTrustManager(ks)[0])
.connectionSpecs(Collections.singletonList(spec))
.addInterceptor(interceptor)
.build();
return client;
}
private static SSLSocketFactory getSslSocketFactory(KeyStore keystore) throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException, IllegalStateException, NoSuchProviderException {
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, getTrustManager(keystore), null);
return sslContext.getSocketFactory();
}
private static TrustManager[] getTrustManager(KeyStore keystore) throws NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException, IllegalStateException, NoSuchProviderException {
TrustManager[] trustManagers;
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keystore);
trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
return trustManagers;
}
private static KeyStore getKeystore() throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, NoSuchProviderException {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, null);
if (alias != null) {
keystore.setKeyEntry(alias, privateKey, KEYSTORE_PASS.toCharArray(), certificates);
}
return keystore;
}
}
我使用新的安全客户端,如下所示:
private static ServiceData service;
public static ServiceAtestado getInstance(Context context) throws GeneralSecurityException, IOException, IllegalStateException {
if (service == null) {
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(Constants.URL);
Retrofit retrofit = builder
.addConverterFactory(GsonConverterFactory.create())
.client(SslOkHttpClient.getOkHttpClient(context))
.build();
service = retrofit.create(ServiceData.class);
}
return service;
}
尽管私有证书和CA证书与我当前使用的证书相同,但每次尝试访问Web服务中的资源时,我都会遇到以下异常:
java.security.cert.CertPathValidator异常:找不到证书路径的信任锚。
我认为问题与证书本身无关,因为我可以使用它从设备的浏览器访问Web服务,所以我认为问题在于2的配置。我已经阅读了很多关于使用SSLSocketFactory配置它的教程和文章,所以我认为我做得对,但显然我没有
我做错了什么?我非常感谢任何帮助。经过几天的努力,我终于意识到旧代码(有效的代码)与我在改型2中尝试编写的代码并不完全相同。它不使用自定义的信任管理器,而是使用系统的默认值。 对于可能感兴趣的人(包括未来的我;-),这里是最后一个简化的工人阶级
public class SslOkHttpClient {
private static final String LOGGER = SslOkHttpClient.class.getName();
private static String alias;
private static Key privateKey;
private static X509Certificate[] certificates;
private static String KEYSTORE_PASS = "secret";
public static OkHttpClient getOkHttpClient(Context context) throws GeneralSecurityException, IOException, IllegalStateException {
if (alias == null)
alias = HttpsUtils.getAlias(context);
if (privateKey == null)
privateKey = HttpsUtils.getPrivateKey(context, alias);
if (certificates == null)
certificates = HttpsUtils.getCertificateChain(context, alias);
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getSslSocketFactory(), getAllTrustManager())
.readTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.addInterceptor(interceptor)
.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
})
.build();
return client;
}
private static SSLSocketFactory getSslSocketFactory() throws CertificateException, NoSuchAlgorithmException, IOException, KeyStoreException, KeyManagementException, IllegalStateException, NoSuchProviderException, UnrecoverableKeyException {
SSLContext sslContext;
sslContext = SSLContext.getInstance("TLSv1.2");
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(null, null);
if (alias != null) {
keystore.setKeyEntry(alias, privateKey, KEYSTORE_PASS.toCharArray(), certificates);
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(keystore, KEYSTORE_PASS.toCharArray());
sslContext.init(kmf.getKeyManagers(), null, null);
return sslContext.getSocketFactory();
}
private static X509TrustManager getAllTrustManager() throws KeyStoreException, NoSuchAlgorithmException {
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
}
HttpUtils只是一个实用类
public class HttpsUtils {
private static final String KEYCHAIN_PREF_ALIAS = "alias";
private static final String KEYCHAIN_PREF = "keychain";
private static final int MODE_PRIVATE = 0;
private static final String DEFAULT_ALIAS = "";
private static final String LOGGER = HttpsUtils.class.getName();
private HttpsUtils() {
}
/**
* This method returns the alias of the key chain from the application
* preference
*
* @return The alias of the key chain
*/
public static String getAlias(Context context) {
SharedPreferences pref = context.getSharedPreferences(KEYCHAIN_PREF, MODE_PRIVATE);
return pref.getString(KEYCHAIN_PREF_ALIAS, DEFAULT_ALIAS);
}
/**
* This method sets the alias of the key chain to the application preference
*/
public static void setAlias(Context context, String alias) {
SharedPreferences pref = context.getSharedPreferences(KEYCHAIN_PREF, MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString(KEYCHAIN_PREF_ALIAS, alias);
editor.apply();
}
public static X509Certificate[] getCertificateChain(Context context, String alias) {
try {
return KeyChain.getCertificateChain(context, alias);
} catch (KeyChainException | InterruptedException ex) {
Log.e(LOGGER, ex.getMessage() == null ? context.getResources().getString(R.string.err_no_especificado) : ex.getMessage());
}
return null;
}
public static Key getPrivateKey(Context context, String alias) {
try {
return KeyChain.getPrivateKey(context, alias);
} catch (KeyChainException | InterruptedException ex) {
Log.e(LOGGER, ex.getMessage() == null ? context.getResources().getString(R.string.err_no_especificado) : ex.getMessage());
}
return null;
}
}
public class HttpsUtils {
private static final String KEYCHAIN_PREF_ALIAS = "alias";
private static final String KEYCHAIN_PREF = "keychain";
private static final int MODE_PRIVATE = 0;
private static final String DEFAULT_ALIAS = "";
private static final String LOGGER = HttpsUtils.class.getName();
private HttpsUtils() {
}
/**
* This method returns the alias of the key chain from the application
* preference
*
* @return The alias of the key chain
*/
public static String getAlias(Context context) {
SharedPreferences pref = context.getSharedPreferences(KEYCHAIN_PREF, MODE_PRIVATE);
return pref.getString(KEYCHAIN_PREF_ALIAS, DEFAULT_ALIAS);
}
/**
* This method sets the alias of the key chain to the application preference
*/
public static void setAlias(Context context, String alias) {
SharedPreferences pref = context.getSharedPreferences(KEYCHAIN_PREF, MODE_PRIVATE);
SharedPreferences.Editor editor = pref.edit();
editor.putString(KEYCHAIN_PREF_ALIAS, alias);
editor.apply();
}
public static X509Certificate[] getCertificateChain(Context context, String alias) {
try {
return KeyChain.getCertificateChain(context, alias);
} catch (KeyChainException | InterruptedException ex) {
Log.e(LOGGER, ex.getMessage() == null ? context.getResources().getString(R.string.err_no_especificado) : ex.getMessage());
}
return null;
}
public static Key getPrivateKey(Context context, String alias) {
try {
return KeyChain.getPrivateKey(context, alias);
} catch (KeyChainException | InterruptedException ex) {
Log.e(LOGGER, ex.getMessage() == null ? context.getResources().getString(R.string.err_no_especificado) : ex.getMessage());
}
return null;
}
}