在 Android 中使用自签名证书时出现 SSLPeerUnverifiedException
SSLPeerUnverifiedException when using a self-signed certificate in Android
我正尝试在我的应用中进行 SSL Pinning,并已按照 https://developer.android.com/training/articles/security-ssl as well as generating my own certificate with https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-in-ubuntu-16-04 on my server at https://coursescraper.tookmund.com/
上的说明进行操作
但是,当我尝试连接到我的服务器时出现异常:
javax.net.ssl.SSLPeerUnverifiedException: Hostname coursescraper.tookmund.com not verified:
certificate: sha1/Uax3JSsdPAGgCg5XyWPR8mhrjRU=
DN: CN=coursescraper.tookmund.com
subjectAltNames: []
然后我按照 https://developer.android.com/training/articles/security-config
的指定向我的应用程序添加了网络安全配置
这允许我删除所有 SSLContext 内容,但仍然会产生完全相同的错误。
我试图关闭主机名验证器只是为了看看会发生什么,但我得到了同样的错误,但没有在日志中看到 VERIFY,所以甚至没有涉及主机名验证器:
// TODO: FIX THIS!!! THIS IS BAD!!!
connection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
Log.v("VERIFY", s);
return true;
}
});
当我将应用程序中的证书文件切换到另一个证书时,我反而得到了 SSLHandshakeException:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
固定似乎有效,只是我的证书出了点问题。
Common Name是域名,从报错中可以看出,所以我不确定我做错了什么。
使用 curl 连接工作正常:
$ curl -v --cacert school/mobilesecCSCI420/nginx-selfsigned.crt https://coursescraper.tookmund.com
* Expire in 0 ms for 6 (transfer 0x558aada3ef50)
* Expire in 1 ms for 1 (transfer 0x558aada3ef50)
...
* Expire in 50 ms for 1 (transfer 0x558aada3ef50)
* Trying 54.165.223.53...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x558aada3ef50)
* Connected to coursescraper.tookmund.com (54.165.223.53) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: school/mobilesecCSCI420/nginx-selfsigned.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=coursescraper.tookmund.com
* start date: Feb 13 05:51:45 2020 GMT
* expire date: Feb 12 05:51:45 2021 GMT
* common name: coursescraper.tookmund.com (matched)
* issuer: CN=coursescraper.tookmund.com
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x558aada3ef50)
> GET / HTTP/2
> Host: coursescraper.tookmund.com
> User-Agent: curl/7.64.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: nginx/1.10.3
< date: Thu, 13 Feb 2020 16:35:28 GMT
< content-type: text/html
< content-length: 691
< last-modified: Sun, 09 Feb 2020 21:13:06 GMT
< etag: "5e4075e2-2b3"
< accept-ranges: bytes
<
<!DOCTYPE html>
<html>
...
我终于明白了。证书缺少 DNS 主题备用名称。
一旦我生成了一个新的证书,它就完美地工作了。
对于那些可能需要它的人,这是我的 ssl.cnf
RANDFILE = /dev/urandom
[ req ]
default_bits = 4096
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
prompt = no
policy = policy_anything
req_extensions = req_ext
x509_extensions = req_ext
[ req_distinguished_name ]
commonName = coursescraper.tookmund.com
[ req_ext ]
basicConstraints = CA:TRUE
subjectAltName = @alt_names
[alt_names]
DNS.1 = coursescraper.tookmund.com
将主机名替换为您自己的,然后 运行
sudo openssl req -x509 -nodes -days 365 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -config ssl.cnf
生成您的新证书
我正尝试在我的应用中进行 SSL Pinning,并已按照 https://developer.android.com/training/articles/security-ssl as well as generating my own certificate with https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-nginx-in-ubuntu-16-04 on my server at https://coursescraper.tookmund.com/
上的说明进行操作但是,当我尝试连接到我的服务器时出现异常:
javax.net.ssl.SSLPeerUnverifiedException: Hostname coursescraper.tookmund.com not verified:
certificate: sha1/Uax3JSsdPAGgCg5XyWPR8mhrjRU=
DN: CN=coursescraper.tookmund.com
subjectAltNames: []
然后我按照 https://developer.android.com/training/articles/security-config
的指定向我的应用程序添加了网络安全配置这允许我删除所有 SSLContext 内容,但仍然会产生完全相同的错误。
我试图关闭主机名验证器只是为了看看会发生什么,但我得到了同样的错误,但没有在日志中看到 VERIFY,所以甚至没有涉及主机名验证器:
// TODO: FIX THIS!!! THIS IS BAD!!!
connection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String s, SSLSession sslSession) {
Log.v("VERIFY", s);
return true;
}
});
当我将应用程序中的证书文件切换到另一个证书时,我反而得到了 SSLHandshakeException:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
固定似乎有效,只是我的证书出了点问题。
Common Name是域名,从报错中可以看出,所以我不确定我做错了什么。
使用 curl 连接工作正常:
$ curl -v --cacert school/mobilesecCSCI420/nginx-selfsigned.crt https://coursescraper.tookmund.com
* Expire in 0 ms for 6 (transfer 0x558aada3ef50)
* Expire in 1 ms for 1 (transfer 0x558aada3ef50)
...
* Expire in 50 ms for 1 (transfer 0x558aada3ef50)
* Trying 54.165.223.53...
* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x558aada3ef50)
* Connected to coursescraper.tookmund.com (54.165.223.53) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: school/mobilesecCSCI420/nginx-selfsigned.crt
CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=coursescraper.tookmund.com
* start date: Feb 13 05:51:45 2020 GMT
* expire date: Feb 12 05:51:45 2021 GMT
* common name: coursescraper.tookmund.com (matched)
* issuer: CN=coursescraper.tookmund.com
* SSL certificate verify ok.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x558aada3ef50)
> GET / HTTP/2
> Host: coursescraper.tookmund.com
> User-Agent: curl/7.64.0
> Accept: */*
>
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< server: nginx/1.10.3
< date: Thu, 13 Feb 2020 16:35:28 GMT
< content-type: text/html
< content-length: 691
< last-modified: Sun, 09 Feb 2020 21:13:06 GMT
< etag: "5e4075e2-2b3"
< accept-ranges: bytes
<
<!DOCTYPE html>
<html>
...
我终于明白了。证书缺少 DNS 主题备用名称。
一旦我生成了一个新的证书,它就完美地工作了。
对于那些可能需要它的人,这是我的 ssl.cnf
RANDFILE = /dev/urandom
[ req ]
default_bits = 4096
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
prompt = no
policy = policy_anything
req_extensions = req_ext
x509_extensions = req_ext
[ req_distinguished_name ]
commonName = coursescraper.tookmund.com
[ req_ext ]
basicConstraints = CA:TRUE
subjectAltName = @alt_names
[alt_names]
DNS.1 = coursescraper.tookmund.com
将主机名替换为您自己的,然后 运行
sudo openssl req -x509 -nodes -days 365 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -config ssl.cnf
生成您的新证书