14

I try to enable SSL on Square's MockWebServer to mock all webservice calls in my Android App under Test. I want to enable SSL to get the same errors like under real conditions. I don't want to disable SSL for the tests or implement a SSL-ignoring HTTPClient.

On every request I get this javax.net.ssl.SSLPeerUnverifiedExceptionecxeption:

org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://localhost:42451/api/blabla": No peer certificate; nested exception is javax.net.ssl.SSLPeerUnverifiedException: No peer certificate

It seems I need to set a certificate for the MockWebServer. But I don't know how...

This is how I tried to enable SSL:

SSLContext sslContext = new SslContextBuilder(InetAddress.getLocalHost().getHostName()).build();
server.useHttps(sslContext.getSocketFactory(), true);

Or:

SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, null, null);
server.useHttps(sslContext.getSocketFactory(), false);

Can anyone help?

StefanTo
  • 971
  • 1
  • 10
  • 28

4 Answers4

7

You will need to create your own keystore:

keytool -genkey -v -keystore mystore.keystore.jks -alias alias_name -keyalg RSA -keysize 2048 -validity 10000

or see : https://developer.android.com/studio/publish/app-signing.html

After creating the keystore, load it in your code and use it in the following way:

        FileInputStream stream = new FileInputStream("mystore.keystore.jks");
        char[] serverKeyStorePassword = "yourcode".toCharArray();
        KeyStore serverKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        serverKeyStore.load(stream, serverKeyStorePassword);

        String kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlgorithm);
        kmf.init(serverKeyStore, serverKeyStorePassword);

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(kmfAlgorithm);
        trustManagerFactory.init(serverKeyStore);

        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(kmf.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        SSLSocketFactory sf = sslContext.getSocketFactory();
        mockWebServer.useHttps(sf, false);
Teng-pao Yu
  • 1,313
  • 15
  • 30
  • 1
    An important detail for this to work: you must also provide the correct options to the `keytool` command, see https://stackoverflow.com/a/11118072/2291104 – David Ferrand Mar 18 '19 at 15:56
2

You can use the helpers from okhttp-tls to generate a localhost certificate and use it for both your MockWebServer and OkHttpClient:

Approachable APIs for using TLS.

A HeldCertificate is a certificate and its private key. Use the builder to create a self-signed certificate that a test server can use for HTTPS:

String localhost = InetAddress.getByName("localhost").getCanonicalHostName();
HeldCertificate localhostCertificate = new HeldCertificate.Builder()
   .addSubjectAlternativeName(localhost)
   .build();

HandshakeCertificates keeps the certificates for a TLS handshake. Use its builder to define which certificates the HTTPS server returns to its clients. The returned instance can create an SSLSocketFactory that implements this policy:

HandshakeCertificates serverCertificates = new HandshakeCertificates.Builder()
    .heldCertificate(localhostCertificate)
    .build();
MockWebServer server = new MockWebServer();
server.useHttps(serverCertificates.sslSocketFactory(), false);
server.start();

HandshakeCertificates also works for clients where its job is to define which root certificates to trust. In this simplified example we trust the server's self-signed certificate:

andshakeCertificates clientCertificates = new HandshakeCertificates.Builder()
    .addTrustedCertificate(localhostCertificate.certificate())
    .build();
OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(clientCertificates.sslSocketFactory(), clientCertificates.trustManager())
    .build();
marcelosalloum
  • 3,481
  • 4
  • 39
  • 63
0

One way to achieve this as stated here is to do the following:

First create a self signed CERTIFICATE and PRIVATE KEY and add them to the project. One file should contain both the CERTIFICATE and PRIVATE KEY, this is to be used with MockWebServer. The other contains just the CERTIFICATE, this is to be used in the network security config.

val localhost = InetAddress.getByName("localhost").getCanonicalHostName()
val localhostCertificate = HeldCertificate.Builder()
    .addSubjectAlternativeName(localhost)
    .duration(10 * 365, TimeUnit.DAYS)
    .build()
// Print public key
println(localhostCertificate.certificatePem())
// Print private key
println(localhostCertificate.privateKeyPkcs8Pem())

Add the cert to the network security config

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <debug-overrides>
        <trust-anchors>
            <certificates src="@raw/debug_cert"/>
        </trust-anchors>
    </debug-overrides>
</network-security-config>

Then use the cert with MockWebServer

    val localhostCertificate =
        HeldCertificate.decode("instrumentation_cert.pem".loadString())

    val serverCertificates = HandshakeCertificates.Builder()
        .heldCertificate(localhostCertificate)
        .build()

    server.useHttps(
        serverCertificates.sslSocketFactory(),
        tunnelProxy = false,
    )
LethalMaus
  • 798
  • 1
  • 8
  • 20
-1

This is how i do it:

 MockWebServer serverForHttps = new MockWebServer();
 serverForHttps .useHttps(SslContextBuilder.localhost().getSocketFactory(), false);
 serverForHttps .start()

It works for me .

april
  • 11
  • I always get this error: `javax.net.ssl.SSLPeerUnverifiedException: No peer certificate` – StefanTo Jan 14 '16 at 13:06
  • 7
    For future readers: `SslContextBuilder` is a class, that dynamically generates in-memory certificate during test execution using BouncyCastle. That class used to be present in okhttp source code in the past, but no longer seems to be there. With a bit of effort you can find it's older versions, such as [this one](https://android.googlesource.com/platform/external/okhttp/+/5f7fde35d881e7e9f8850daeac4de52265635656/src/test/java/com/squareup/okhttp/internal/SslContextBuilder.java). – user1643723 Jul 20 '17 at 01:52
  • 1
    I get: java.lang.RuntimeException: java.security.NoSuchAlgorithmException: The BC provider no longer provides an implementation for KeyPairGenerator.RSA. – bashan Oct 07 '18 at 09:03