Tookmund
Tookmund

Reputation: 103

SSLPeerUnverifiedException when using a self-signed certificate in Android

I'm trying to do SSL Pinning in my app and have followed the instructions on 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/

However, when I try to connect to my server I get an exception:

javax.net.ssl.SSLPeerUnverifiedException: Hostname coursescraper.tookmund.com not verified:
        certificate: sha1/Uax3JSsdPAGgCg5XyWPR8mhrjRU=
        DN: CN=coursescraper.tookmund.com
        subjectAltNames: []

I then added a network security config to my app as specified by https://developer.android.com/training/articles/security-config

This allows me to remove all the SSLContext stuff but still produces the exact same error.

I tried to switch out the hostname verifier just to see what happens but I get the same error without seeing VERIFY in the logs, so the hostname verifier is not even involved:

        // TODO: FIX THIS!!! THIS IS BAD!!!
    connection.setDefaultHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    Log.v("VERIFY", s);
                    return true;
                }
            });

When I switch out the certificate file in the app to another certificate I instead get a SSLHandshakeException:

javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.

Pinning thus appears to be working, there's just something wrong with my certificate.

The Common Name is the domain name, as can be seen from the error, so I'm not sure what I'm doing wrong.

Connecting with curl works fine:

$ 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>
...

Upvotes: 2

Views: 997

Answers (1)

Tookmund
Tookmund

Reputation: 103

I finally figured it out. The certificate is missing a DNS subject alternative name.

Once I generated a new certificate with that it worked perfectly.

For those who might need it, heres my 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

Replace the hostname with your own and then run


sudo openssl req -x509 -nodes -days 365 -keyout /etc/ssl/private/nginx-selfsigned.key -out /etc/ssl/certs/nginx-selfsigned.crt -config ssl.cnf

to generate your new certificate

Upvotes: 3

Related Questions