From 83b2db833260c03d250af18b6d2ad79e7d2230b8 Mon Sep 17 00:00:00 2001 From: Yuri Schimke Date: Fri, 11 Aug 2023 13:48:52 +0100 Subject: [PATCH] Handle certificate corruption --- okhttp/src/jvmMain/kotlin/okhttp3/Cache.kt | 3 +- .../src/jvmTest/java/okhttp3/CacheTest.java | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/okhttp/src/jvmMain/kotlin/okhttp3/Cache.kt b/okhttp/src/jvmMain/kotlin/okhttp3/Cache.kt index 2459ac88bf4c..38dc2cb68494 100644 --- a/okhttp/src/jvmMain/kotlin/okhttp3/Cache.kt +++ b/okhttp/src/jvmMain/kotlin/okhttp3/Cache.kt @@ -629,7 +629,8 @@ class Cache internal constructor( for (i in 0 until length) { val line = source.readUtf8LineStrict() val bytes = Buffer() - bytes.write(line.decodeBase64()!!) + val certificateBytes = line.decodeBase64() ?: throw IOException("Corrupt certificate in cache entry") + bytes.write(certificateBytes) result.add(certificateFactory.generateCertificate(bytes.inputStream())) } return result diff --git a/okhttp/src/jvmTest/java/okhttp3/CacheTest.java b/okhttp/src/jvmTest/java/okhttp3/CacheTest.java index 4a564cdfb250..9fa2bffffc44 100644 --- a/okhttp/src/jvmTest/java/okhttp3/CacheTest.java +++ b/okhttp/src/jvmTest/java/okhttp3/CacheTest.java @@ -338,6 +338,49 @@ private void testResponseCaching(TransferKind transferKind) throws IOException { assertThat(response2.handshake().localPrincipal()).isEqualTo(localPrincipal); } + @Test public void secureResponseCachingWithCorruption() throws IOException { + server.useHttps(handshakeCertificates.sslSocketFactory()); + server.enqueue(new MockResponse.Builder() + .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS)) + .addHeader("Expires: " + formatDate(1, TimeUnit.HOURS)) + .body("ABC") + .build()); + server.enqueue(new MockResponse.Builder() + .addHeader("Last-Modified: " + formatDate(-5, TimeUnit.MINUTES)) + .addHeader("Expires: " + formatDate(2, TimeUnit.HOURS)) + .body("DEF") + .build()); + + client = client.newBuilder() + .sslSocketFactory( + handshakeCertificates.sslSocketFactory(), handshakeCertificates.trustManager()) + .hostnameVerifier(NULL_HOSTNAME_VERIFIER) + .build(); + + Request request = new Request.Builder().url(server.url("/")).build(); + Response response1 = client.newCall(request).execute(); + assertThat(response1.body().string()).isEqualTo("ABC"); + + Path cacheEntry = fileSystem.allPaths().stream() + .filter((e) -> e.name().endsWith(".0")) + .findFirst() + .orElseThrow(); + corruptCertificate(cacheEntry); + + Response response2 = client.newCall(request).execute(); // Not Cached! + assertThat(response2.body().string()).isEqualTo("DEF"); + + assertThat(cache.requestCount()).isEqualTo(2); + assertThat(cache.networkCount()).isEqualTo(2); + assertThat(cache.hitCount()).isEqualTo(0); + } + + private void corruptCertificate(Path cacheEntry) throws IOException { + String content = Okio.buffer(fileSystem.source(cacheEntry)).readUtf8(); + content = content.replace("MII", "!!!"); + Okio.buffer(fileSystem.sink(cacheEntry)).writeUtf8(content).close(); + } + @Test public void responseCachingAndRedirects() throws Exception { server.enqueue(new MockResponse.Builder() .addHeader("Last-Modified: " + formatDate(-1, TimeUnit.HOURS))