Android 使用改装2访问安全Web服务时获取CertPathValidator异常

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.

我有一个Android应用程序,可以毫无问题地访问安全的Web服务。它使用我自己的类,它扩展了ApacheDefaultClientHttp

别名证书私钥是从KeyChain获得的(我不想将证书作为原始文件合并到应用程序中)。该应用程序工作正常,但现在我想重构Http客户端并使用2。这是我使用OkHttpClient的新类

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;
}
}