Okhttp: Exception using MockWebServer with Https on Android P

Created on 27 Jul 2018  Â·  11Comments  Â·  Source: square/okhttp

Got the following exception when running Android P with https:

java.lang.VerifyError: Verifier rejected class okhttp3.internal.tls.HeldCertificate$Builder: okhttp3.internal.tls.HeldCertificate okhttp3.internal.tls.HeldCertificate$Builder.build() failed to verify: okhttp3.internal.tls.HeldCertificate okhttp3.internal.tls.HeldCertificate$Builder.build(): [0x9D] register v7 has type Reference: org.bouncycastle.asn1.ASN1ObjectIdentifier but expected Reference: org.bouncycastle.asn1.DERObjectIdentifier (declaration of 'okhttp3.internal.tls.HeldCertificate$Builder' a

Here is the test code to start the MockWebServer:

    MockWebServer mockWebServer = new MockWebServer();
    mockWebServer.useHttps(SslClient.localhost().socketFactory, false);
    mockWebServer.start();
bug

Most helpful comment

... or perhaps build our own certificate encoder and avoid the bouncycastle dependency altogether!

All 11 comments

Are you shipping an incompatible version of bouncycastle with your project? HeldCertificate depends on bouncycastle 1.50, though perhaps we should update the library to require a newer version.

... or perhaps build our own certificate encoder and avoid the bouncycastle dependency altogether!

@swankjesse does that mean that including implementation com.squareup.okhttp3:okhttp-tls:3.11.0 is enough since org.bouncycastle:bcprov-jdk15on:1.50 is a transitive dependency, right?

That's not the case in my project (testet on Android emulator with API 21 - 28) where I do the following to setup https for testing with MockWebServer:

val localhostCertificate = HeldCertificate.Builder()
    .addSubjectAlternativeName(InetAddress.getByName("localhost").canonicalHostName)
    .build()

If I don't add additional bouncy castle dependencies I get the following error:

java.lang.AssertionError: java.security.NoSuchAlgorithmException: no such algorithm: ECDSA for provider BC
at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:429)
at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353)
...

Nevertheless, I can confirm that you get this error

java.lang.VerifyError: Verifier rejected class okhttp3.internal.tls.HeldCertificate$Builder: 
okhttp3.internal.tls.HeldCertificate okhttp3.internal.tls.HeldCertificate$Builder.build() failed to verify: 
okhttp3.internal.tls.HeldCertificate okhttp3.internal.tls.HeldCertificate$Builder.build(): [0x9D] register v7 has type Reference: org.bouncycastle.asn1.ASN1ObjectIdentifier but expected Reference: 
org.bouncycastle.asn1.DERObjectIdentifier (declaration of 'okhttp3.internal.tls.HeldCertificate$Builder' a

if you use a different version of bouncy castle (like org.bouncycastle:bctls-jdk15on:1.60)

@sockeqwe gotcha. You must be pulling in yet another version somehow. If you explicitly specify 1.50 does it work?

In any case we should update our code to 1.60 for the next release.

  1. okhttp + bcprov-jdk15on
java.lang.AssertionError: java.security.NoSuchAlgorithmException: no such algorithm: ECDSA for provider BC
at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:429)
at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353)
  1. okhttp + bcprov-ext-jdk15on
implementation "com.squareup.okhttp3:okhttp-tls:3.11.0"
implementation "org.bouncycastle:bcprov-ext-jdk15on:1.50"

causes aapt2 error at compile time

AGPBI: {"kind":"error","text":"Program type already present: org.bouncycastle.LICENSE","sources":[{}],"tool":"D8"}
  1. okhttp (excluding bouncycastle) + bcprov-ext-jdk15on
implementation (testLibraries.okhttpTls){
  exclude group: 'org.bouncycastle'
}
implementation "org.bouncycastle:bcprov-ext-jdk15on:1.50"

causes

java.lang.AssertionError: java.security.NoSuchAlgorithmException: no such algorithm: ECDSA for provider BC
at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:429)
at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353)
  1. okhttp + bcpkix
implementation "com.squareup.okhttp3:okhttp-tls:3.11.0"
implementation "org.bouncycastle:bcpkix-jdk15on:1.50"

causes

java.lang.AssertionError: java.security.NoSuchAlgorithmException: no such algorithm: ECDSA for provider BC
at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:429)
at okhttp3.tls.HeldCertificate$Builder.build(HeldCertificate.java:353)

Is there any other bouncy castle dependency that I should use?

Btw. starting in bouncy castle 1.60 seems to have a dedicated artifact for tls: org.bouncycastle:bctls-jdk15on:1.60

Our tests pass with bcprov-jdk15on.

on my macbook during unit tests it works too, but not on an android
emulator.

Jesse Wilson notifications@github.com schrieb am So., 29. Juli 2018,
23:45:

Our tests pass with bcprov-jdk15on.

—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/square/okhttp/issues/4175#issuecomment-408708132, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAjnrlRbIUp3X_dmLvcdEv80MhpydJh0ks5uLizfgaJpZM4Vi8iR
.

Hm. Will investigate.

I created a new issue here: #4183

Instead of using SslClient.localhost().socketFactory, I create a cert and load to Android Keystore,

MockWebServer mockWebServer = new MockWebServer();
mockWebServer.useHttps(getSSLSocketFactory(), false);
private SSLSocketFactory getSSLSocketFactory() throws Exception {
        char[] password = "password".toCharArray();

        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
        keyPairGenerator.initialize(1024, new SecureRandom());
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        Calendar notAfter = Calendar.getInstance();
        notAfter.add(Calendar.DAY_OF_YEAR, 1);
        X509v3CertificateBuilder builder = new JcaX509v3CertificateBuilder(
                new X500Principal("CN=localhost"),
                new BigInteger("1"),
                new Date(),
                notAfter.getTime(),
                new X500Principal("CN=localhost"),
                keyPair.getPublic());

        ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSAEncryption")
                .build(keyPair.getPrivate());

        builder.build(contentSigner);

        X509Certificate certificate = new JcaX509CertificateConverter()
                .getCertificate(builder.build(contentSigner));

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        InputStream in = null;
        keyStore.load(in, password);
        Certificate[] certificateChain = {certificate};
        keyStore.setKeyEntry("private", keyPair.getPrivate(), password, certificateChain);
        keyStore.setCertificateEntry("cert", certificate);

        KeyManagerFactory keyManagerFactory =
                KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password);
        TrustManagerFactory trustManagerFactory =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(),
                new SecureRandom());
        return sslContext.getSocketFactory();
    }

Running it with our current code on Android P:

Caused by: java.security.NoSuchAlgorithmException: ECDSA KeyPairGenerator not available
        at java.security.KeyPairGenerator.getInstance(KeyPairGenerator.java:241)
        at okhttp3.tls.HeldCertificate$Builder.generateKeyPair(HeldCertificate.java:426)

This is fixed by replacing ECDSA with RSA2048

    HeldCertificate localhostCertificate = new HeldCertificate.Builder()
        .addSubjectAlternativeName(localhost)
        .rsa2048()
        .build();

But I’d still like to figure out how to get ECDSA working on Android!

Was this page helpful?
0 / 5 - 0 ratings