class VespaQueryClient {

  static final BouncyCastleProvider bcProvider = new BouncyCastleProvider();

  public static VespaQueryApi createInstance(String baseUrl, Path certificate, Path privateKey) {
    try {
      KeyStore keystore = KeyStore.getInstance("PKCS12");
      keystore.load(null);
      keystore.setKeyEntry("cert", privateKey(privateKey), new char[0], certificates(certificate));
      // Protocol version must be equal to TlsContext.SSL_CONTEXT_VERSION or higher
      SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
      sslContext.init(createKeyManagers(keystore), null, /*Default secure random algorithm*/null);

      TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
        TrustManagerFactory.getDefaultAlgorithm()
      );
      trustManagerFactory.init(keystore);

      OkHttpClient client = new OkHttpClient.Builder()
        .sslSocketFactory(sslContext.getSocketFactory(), (X509TrustManager) trustManagerFactory.getTrustManagers()[0])
        .addInterceptor(chain -> {
          // trick to format the query URL exactly how Vespa expects it (search/?query),
          // see https://docs.vespa.ai/en/reference/query-language-reference.html
          Request request = chain.request();
          HttpUrl url = request
            .url()
            .newBuilder()
            .removePathSegment(1)
            .addPathSegment("")
            .encodedQuery(request.url().encodedPathSegments().get(1))
            .build();
          request = request.newBuilder().url(url).build();
          return chain.proceed(request);
        })
        .build();

      Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(baseUrl)
        .client(client)
        .addConverterFactory(GsonConverterFactory.create(new GsonBuilder().create()))
        .build();

      return retrofit.create(VespaQueryApi.class);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private static KeyManager[] createKeyManagers(KeyStore keystore) throws GeneralSecurityException {
    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmf.init(keystore, new char[0]);
    return kmf.getKeyManagers();
  }

  private static Certificate[] certificates(Path file) throws IOException, GeneralSecurityException {
    try (PEMParser parser = new PEMParser(Files.newBufferedReader(file))) {
      List<X509Certificate> result = new ArrayList<>();
      Object pemObject;
      while ((pemObject = parser.readObject()) != null) {
        result.add(toX509Certificate(pemObject));
      }
      if (result.isEmpty()) throw new IOException("File contains no PEM encoded certificates: " + file);
      return result.toArray(new Certificate[0]);
    }
  }

  private static PrivateKey privateKey(Path file) throws IOException, GeneralSecurityException {
    try (PEMParser parser = new PEMParser(Files.newBufferedReader(file))) {
      Object pemObject;
      while ((pemObject = parser.readObject()) != null) {
        if (pemObject instanceof PrivateKeyInfo) {
          PrivateKeyInfo keyInfo = (PrivateKeyInfo) pemObject;
          PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyInfo.getEncoded());
          return createKeyFactory(keyInfo).generatePrivate(keySpec);
        } else if (pemObject instanceof PEMKeyPair) {
          PEMKeyPair pemKeypair = (PEMKeyPair) pemObject;
          PrivateKeyInfo keyInfo = pemKeypair.getPrivateKeyInfo();
          return createKeyFactory(keyInfo).generatePrivate(new PKCS8EncodedKeySpec(keyInfo.getEncoded()));
        }
      }
      throw new IOException("Could not find private key in PEM file");
    }
  }

  private static X509Certificate toX509Certificate(Object pemObject) throws IOException, GeneralSecurityException {<FILL_FUNCTION_BODY>}

  private static KeyFactory createKeyFactory(PrivateKeyInfo info) throws IOException, GeneralSecurityException {
    ASN1ObjectIdentifier algorithm = info.getPrivateKeyAlgorithm().getAlgorithm();
    if (X9ObjectIdentifiers.id_ecPublicKey.equals(algorithm)) {
      return KeyFactory.getInstance("EC", bcProvider);
    } else if (PKCSObjectIdentifiers.rsaEncryption.equals(algorithm)) {
      return KeyFactory.getInstance("RSA", bcProvider);
    } else {
      throw new IOException("Unknown key algorithm: " + algorithm);
    }
  }
}

    if (pemObject instanceof X509Certificate) return (X509Certificate) pemObject;
    if (pemObject instanceof X509CertificateHolder) {
      return new JcaX509CertificateConverter()
        .setProvider(bcProvider)
        .getCertificate((X509CertificateHolder) pemObject);
    }
    throw new IOException("Invalid type of PEM object: " + pemObject);
  
<no_super_class>