1
            try {
                final SSLSocket sslSocket = (SSLSocket)SSLSocketFactory.getDefault().createSocket();
                Timber.e("RAWR: BEFORE CONNECT");
                sslSocket.connect(new InetSocketAddress("192.168.1.90", 8443));
                Timber.e("RAWR: AFTER CONNECT");
                final SSLSession session = sslSocket.getSession();
                Timber.e("RAWR: AFTER GET SESSION");
                for (Certificate certificate : session.getPeerCertificates()) {
                    Timber.e("RAWR found cert " + certificate);
                }
            } catch (SSLPeerUnverifiedException peer) {
                Timber.e("RAWR: UNVERIFIED PEER %S", peer.getMessage());
            } catch (IOException e) {
                Timber.e(e, "RAWR IOE %s", e.getMessage());
            }

I want to DOWNLOAD the certificates using the getPeerCertificates() but it throws SSLPeerUnverifiedException before it can get the certs.

The environments I'm dealing with have 100% self-signed certificates and they CANNOT be loaded on device before hand. The idea is customers do not have to load this themselves, and the app itself should download it and prompt the user whether to accept it or not.

Ray W
  • 11
  • 2
  • 1
    Frankly, addressing the use of self-signed certificates should be forefront. At the very least establish an internal CA and then you can distribute the CA certificate in the app for validation. – Sammitch May 13 '22 at 20:41
  • 1
    Such a seemingly poor security design makes me sad but anyway [this](https://stackoverflow.com/a/5671038/238704) shows you how to accept any/all certificates for a connection. After the connection is successful, retrieve the certs and close it immediately. Then allow the user to make the trust decision, create a new custom TrustManager to accepts only *that* certificate and reconnect. – President James K. Polk May 13 '22 at 21:14

1 Answers1

2

The easiest way is to let Java download the server certificate for you and then just grab and save it.

This can be done by using a custom X509TrustManager implementation as it it shown for example in this answer.

The part you need to modify is checkServerTrusted:

public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
    X509Certificate serverCert = certs[0]; // as far as I remember the server leaf certificate is always the first entry in this array
    // save the certificate to some field you can access from outside of the trust manager.
    throw new CertificateException(); // we don't want to establish the HTTPS connection so by throwing the exception we can abort the TLS handshake here
}

In the end you only have to call getEncoded() on the saved serverCert to get it's ASN.1 DER encoded representation as byte[] which you can save to a file like server.cer.

Robert
  • 39,162
  • 17
  • 99
  • 152