我在Raspberry Pi上运行一个简单的Web API,该API位于同一PI上的Nginx服务器后面。我正在使用自签名的客户端证书来验证来自Android应用程序的调用。过去的工作完全很好,但是我最近在重建一些硬件后又回到了这个项目,当我尝试在Runny Android 8.1上使用它时,它给出了以下例外:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:219)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:268)
...
我根据以下方式生成了证书和键:http://nategood.com/client-side-certificate-authentication-in-ngi
用curl
进行测试正常。
我创建了用于以下应用程序的密钥库:
openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out client.p12
我遵循以下文章来设置Android中的客户端证书并连接到服务器:http://chariotsolutions.com/blog/post/https-withpps-with-client-certificates-on/
但我使用okhttp:
将其重写private const val SERVER = "https://my.server"
/**
* trustManagers is used to authorize the server's self-signed cert
*/
private val trustManagers by lazy {
val cert = CertificateFactory.getInstance("X.509")
.generateCertificate(appCtx.assets.open("ca.crt")) as X509Certificate
val trustStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
load(null, null)
setCertificateEntry(cert.subjectX500Principal.name, cert)
}
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
init(trustStore)
}.trustManagers
}
/**
* keyManagers is used to load the client-authentication cert
*/
private val keyManagers by lazy {
// assuming this can only be called after Application is created
val keyStore = KeyStore.getInstance("PKCS12").apply {
load(appCtx.assets.open("client.p12"), "".toCharArray())
}
KeyManagerFactory.getInstance("X509").apply {
init(keyStore, "".toCharArray())
}.keyManagers
}
/**
* sslContext for opening TLS connection to server
*/
private val sslContext by lazy {
SSLContext.getInstance("TLS").apply {
init(keyManagers, trustManagers, null)
}
}
/**
* pass an HTTPS request to server
*/
suspend fun request(url: String): ByteArray? {
return try {
val request = Request.Builder()
.url(url)
.build()
val client = OkHttpClient.Builder()
.sslSocketFactory(sslContext.socketFactory, trustManagers[0] as X509TrustManager)
.build()
client.newCall(request).await().body().byteStream().readBytes()
} catch (e: Exception) {
err(e) { "failed to send request" }
null
}
}
this 使用工作,但现在不使用。我花了一天半的时间寻找答案,并且我尝试了以下内容:
- 我尝试使用httpurlconnection而不是okhttp
- 我尝试重新创建从头开始的所有证书/键。
- 我尝试使用新的"网络安全配置":
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config>
<trust-anchors>
<!-- Additionally trust user added CAs -->
<certificates src="user" />
<certificates src="@raw/ca"/>
</trust-anchors>
</base-config>
</network-security-config>
我已经阅读了有关创建自定义信任经理的所有示例,而且它们几乎都是一样的,甚至https://developer.android.com/training/articles/security-security-ssl.html.html#unknownca
我尝试过的每个人都会产生相同的例外,我是否缺少某些东西?
这就是我能够在GoDaddy上使用自签名的证书在项目中工作的方式:
- 在您的项目中,在Android Studio中,创建一个名为"原始"的新目录(没有单引号(
- 在原始文件夹中创建一个新的文本文件,并使用任何名称和.crt扩展名(即GDCERT.CRT(
- 在安全性的godaddy cpanel上,单击" ssl/tls"。单击"证书"下的链接,然后将其显示您已经创建的证书。单击"编辑"链接。选择并复制证书的内容并将其粘贴到您在Android Studio" RAW"文件夹中创建的文本文件中并保存。
- 在Android Studio中,右键单击" RES"文件夹,new&gt;xml&gt;应用程序XML文件。命名此文件'network_security_config'(没有单个引号(
- 将以下内容添加到'network_security_config.xml'文件:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<debug-overrides>
<trust-anchors>
<certificates src="user"/>
<certificates src="@raw/gdcert"/>
</trust-anchors>
</debug-overrides>
</network-security-config>
- 在您的androidmanifest.xml文件中,在&lt;应用程序节点下添加以下内容:
android:networkSecurityConfig="@xml/network_security_config"
- 在您的解决方案中使用以下Java代码:
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory ssf = sc.getSocketFactory();
SSLSocket sslsocket = (SSLSocket) ssf.createSocket("yourwebsitename.com", 443);
接着是您要运行以连接到服务器的任何代码。
- 请注意,第二行调用了一种新方法,该方法应添加到您的课程中:
TrustManager[] trustAllCerts = new TrustManager[] {new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}};
保存您的项目并运行它。