ssl 证书路径验证器异常:找不到证书路径的信任锚- Retrofit Android

vcirk6k6  于 2023-03-30  发布在  Android
关注(0)|答案(9)|浏览(420)

我正在创建一个android应用程序,它使用https与服务器通信。我使用retrofitOkHttp进行请求。这些对于标准的http请求很好。以下是我遵循的步骤。

**第一步:**使用命令从服务器获取证书文件

echo -n | openssl s_client -connect api.****.tk:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > gtux.cert

**步骤2:**使用以下命令将证书转换为BKS格式

keytool -importcert -v -trustcacerts -file "gtux.cert" -alias imeto_alias -keystore "my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS

它要求我输入密码,文件成功创建。

步骤3:

创建一个OkHttpClient并使用它发出https请求

public class MySSLTrust {
public static OkHttpClient trustcert(Context context){
    OkHttpClient okHttpClient = new OkHttpClient();
    try {
        KeyStore ksTrust = KeyStore.getInstance("BKS");
        InputStream instream = context.getResources().openRawResource(R.raw.my_keystore);
        ksTrust.load(instream, "secret".toCharArray());
        // TrustManager decides which certificate authorities to use.
        TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ksTrust);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, tmf.getTrustManagers(), null);
        okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());
    } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) {
        e.printStackTrace();
    }
    return okHttpClient;
}
}

步骤4:

必须创建RestAdapter

RestAdapter.Builder()
.setRequestInterceptor(intercept)
.setEndpoint("https://api.****.tk")
.setClient(new OkClient(this))
.setLogLevel(RestAdapter.LogLevel.FULL)
.setLog(new AndroidLog("RETROFIT"))
.build();

但最后当运行应用程序时,它扔给我CertPathValidatorException : Trust anchor for certificate path not found。请帮助我解决这个问题。谢谢。

**其他失败尝试:**尝试在我的Xperia Z2中安装证书,它说文件已安装,但当我运行应用程序时,会抛出相同的异常。
错误日志这里是我执行时得到的错误日志...

Error Log
贴在那里,以便阅读。

f1tvaqid

f1tvaqid1#

免责声明:此答案来自2015年7月,并使用当时的RetrofitOkHttp

查看this link以了解有关Retrofit v2的更多信息,并查看此版本以了解当前的OkHttp方法。
好吧,我得到了它的工作使用Android开发人员指南.
就像OP一样,我尝试使用RetrofitOkHttp连接到自签名的SSL服务器。
下面是让事情工作的代码(我已经删除了try/catch块):

public static RestAdapter createAdapter(Context context) {
  // loading CAs from an InputStream
  CertificateFactory cf = CertificateFactory.getInstance("X.509");
  InputStream cert = context.getResources().openRawResource(R.raw.my_cert);
  Certificate ca;
  try {
    ca = cf.generateCertificate(cert);
  } finally { cert.close(); }

  // creating a KeyStore containing our trusted CAs
  String keyStoreType = KeyStore.getDefaultType();
  KeyStore keyStore = KeyStore.getInstance(keyStoreType);
  keyStore.load(null, null);
  keyStore.setCertificateEntry("ca", ca);

  // creating a TrustManager that trusts the CAs in our KeyStore
  String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
  TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
  tmf.init(keyStore);

  // creating an SSLSocketFactory that uses our TrustManager
  SSLContext sslContext = SSLContext.getInstance("TLS");
  sslContext.init(null, tmf.getTrustManagers(), null);

  // creating an OkHttpClient that uses our SSLSocketFactory
  OkHttpClient okHttpClient = new OkHttpClient();
  okHttpClient.setSslSocketFactory(sslContext.getSocketFactory());

  // creating a RestAdapter that uses this custom client
  return new RestAdapter.Builder()
              .setEndpoint(UrlRepository.API_BASE)
              .setClient(new OkClient(okHttpClient))
              .build();
}

为了帮助调试,我还将.setLogLevel(RestAdapter.LogLevel.FULL)添加到RestAdapter创建命令中,我可以看到它连接并从服务器获取响应。
只需要我原来保存在main/res/raw中的**.crt文件。.crt文件,也就是证书,是使用openssl创建证书时创建的两个文件之一。一般情况下,它是.crt或.cert文件,而另一个是.key文件。
当然,.crt文件是您的
公钥**,.key文件是您的私钥。
正如我所看到的,您已经有了一个**.cert**文件,这是相同的,所以请尝试使用它。

PS:对于以后读到的,只有.pem文件的用户,根据this answer,只需要这个就可以进行转换:

openssl x509 -outform der -in your-cert.pem -out your-cert.crt

**PS²:**对于那些没有任何文件的用户,您可以使用以下命令(bash)从任何服务器提取公钥(又名证书):

echo -n | openssl s_client -connect your.server.com:443 | \
  sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt

只需替换your.server.com和端口(如果不是标准HTTPS),并为要创建的输出文件选择一个有效的路径。

y53ybaqx

y53ybaqx2#

要解决CertPathValidatorException问题,请使用从https://mobikul.com/android-retrofit-handling-sslhandshakeexception/复制的以下代码-

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(YOUR_BASE_URL)
        .client(getUnsafeOkHttpClient().build())
        .build();

  public static OkHttpClient.Builder getUnsafeOkHttpClient() {

    try {
        // Create a trust manager that does not validate certificate chains
        final TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String
authType) throws CertificateException {
                    }
 
                    @Override
                    public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String
authType) throws CertificateException {
                    }
 
                    @Override
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return new java.security.cert.X509Certificate[]{};
                    }
                }
        };
 
        // Install the all-trusting trust manager
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
 
        // Create an ssl socket factory with our all-trusting manager
        final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
 
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);
        builder.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return true;
            }
        });
        return builder;
    } catch (Exception e) {
        throw new RuntimeException(e);
    } }
guz6ccqo

guz6ccqo3#

这是Kotlin版本。Okhttp 4.9.0
Thank you:)

fun unSafeOkHttpClient() :OkHttpClient.Builder {
            val okHttpClient = OkHttpClient.Builder()
            try {
                // Create a trust manager that does not validate certificate chains
                val trustAllCerts:  Array<TrustManager> = arrayOf(object : X509TrustManager {
                    override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?){}
                    override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
                    override fun getAcceptedIssuers(): Array<X509Certificate>  = arrayOf()
                })

                // Install the all-trusting trust manager
                val  sslContext = SSLContext.getInstance("SSL")
                sslContext.init(null, trustAllCerts, SecureRandom())

                // Create an ssl socket factory with our all-trusting manager
                val sslSocketFactory = sslContext.socketFactory
                if (trustAllCerts.isNotEmpty() &&  trustAllCerts.first() is X509TrustManager) {
                    okHttpClient.sslSocketFactory(sslSocketFactory, trustAllCerts.first() as X509TrustManager)
                    okHttpClient.hostnameVerifier {HostnameVerifier { _, _ -> true } }
                }

                return okHttpClient
            } catch (e: Exception) {
                return okHttpClient
            }
        }
raogr8fs

raogr8fs4#

我不使用Retrofit,对于OkHttp,这里是对我有效的自签名证书的唯一解决方案:
1.像Gowtham的问题一样,从我们的网站获取证书,并将其放入项目的 res/raw 目录:

echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt

1.使用Paulo answer设置ssl工厂(现在使用 OkHttpClient.Builder()),但不创建RestAdapter
1.然后添加the following解决方案来修复:*SSLPeerUnverifiedException:主机名未验证 *
因此,Paulo的代码(在 sslContext 初始化之后)的结尾看起来像下面这样:

...
OkHttpClient.Builder builder = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory());
builder.hostnameVerifier(new HostnameVerifier() {
  @Override
  public boolean verify(String hostname, SSLSession session) {
    return "secure.elkews.com".equalsIgnoreCase(hostname);
});
OkHttpClient okHttpClient = builder.build();
xriantvc

xriantvc5#

改造2.3.0

// Load CAs from an InputStream
    CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

    InputStream inputStream = context.getResources().openRawResource(R.raw.ssl_certificate); //(.crt)
    Certificate certificate = certificateFactory.generateCertificate(inputStream);
    inputStream.close();

    // Create a KeyStore containing our trusted CAs
    String keyStoreType = KeyStore.getDefaultType();
    KeyStore keyStore = KeyStore.getInstance(keyStoreType);
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", certificate);

    // Create a TrustManager that trusts the CAs in our KeyStore.
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm);
    trustManagerFactory.init(keyStore);

    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
    X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];

    // Create an SSLSocketFactory that uses our TrustManager
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
    sslSocketFactory = sslContext.getSocketFactory();

    //create Okhttp client
    OkHttpClient client = new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory,x509TrustManager)
                .build();

    Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl(url)
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(client)
                    .build();
zsbz8rwp

zsbz8rwp6#

您正在将cert转换为BKS Keystore,为什么不直接使用.cert,从https://developer.android.com/training/articles/security-ssl.html

CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream instream = context.getResources().openRawResource(R.raw.gtux_cert);
Certificate ca;
try {
    ca = cf.generateCertificate(instream);
} finally {
    caInput.close();
}

KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType());
kStore.load(null, null);
kStore.setCertificateEntry("ca", ca);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(););
tmf.init(kStore);

SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);

okHttpClient.setSslSocketFactory(context.getSocketFactory());
nqwrtyyt

nqwrtyyt7#

在Kotlin中实现:改造2.3.0

private fun getUnsafeOkHttpClient(mContext: Context) : 
OkHttpClient.Builder? {

var mCertificateFactory : CertificateFactory = 
CertificateFactory.getInstance("X.509")
var mInputStream = mContext.resources.openRawResource(R.raw.cert)
            var mCertificate : Certificate = mCertificateFactory.generateCertificate(mInputStream)
        mInputStream.close()
val mKeyStoreType = KeyStore.getDefaultType()
val mKeyStore = KeyStore.getInstance(mKeyStoreType)
mKeyStore.load(null, null)
mKeyStore.setCertificateEntry("ca", mCertificate)

val mTmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm()
val mTrustManagerFactory = TrustManagerFactory.getInstance(mTmfAlgorithm)
mTrustManagerFactory.init(mKeyStore)

val mTrustManagers = mTrustManagerFactory.trustManagers

val mSslContext = SSLContext.getInstance("SSL")
mSslContext.init(null, mTrustManagers, null)
val mSslSocketFactory = mSslContext.socketFactory

val builder = OkHttpClient.Builder()
builder.sslSocketFactory(mSslSocketFactory, mTrustManagers[0] as X509TrustManager)
builder.hostnameVerifier { _, _ -> true }
return builder

}

ylamdve6

ylamdve68#

经过长时间的研究和挖掘太深,我发现在Android的证书钉的解决方案,是的,它不同于iOS,我们需要一个证书本身,但在Android中,我们只需要一个散列引脚,就是这样。

如何获取证书的hash pin?

最初只是使用一个错误的散列引脚和您的java类将抛出一个错误与正确的散列引脚或引脚链,只需复制和粘贴到您的代码,这就是它。
这个解决方案解决了我的问题:https://stackoverflow.com/a/45853669/3448003

t5zmwmid

t5zmwmid9#

根据哈尼的回答,它对我来说工作得很好。但是如果你得到编译错误,你需要修复一件事。
okHttpClient.hostnameVerifier {HostnameVerifier { _, _ -> true } }更改为okHttpClient.hostnameVerifier { _, _ -> true }
和函数

fun unSafeOkHttpClient() :OkHttpClient.Builder {
    val okHttpClient = OkHttpClient.Builder()
    try {
        // Create a trust manager that does not validate certificate chains
        val trustAllCerts:  Array<TrustManager> = arrayOf(object : X509TrustManager {
            override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?){}
            override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
            override fun getAcceptedIssuers(): Array<X509Certificate>  = arrayOf()
        })

        // Install the all-trusting trust manager
        val  sslContext = SSLContext.getInstance("SSL")
        sslContext.init(null, trustAllCerts, SecureRandom())

        // Create an ssl socket factory with our all-trusting manager
        val sslSocketFactory = sslContext.socketFactory
        if (trustAllCerts.isNotEmpty() &&  trustAllCerts.first() is X509TrustManager) {
            okHttpClient.sslSocketFactory(sslSocketFactory, trustAllCerts.first() as X509TrustManager)
            okHttpClient.hostnameVerifier { _, _ -> true } // change here
        }

        return okHttpClient
    } catch (e: Exception) {
        return okHttpClient
    }
}

相关问题