All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alextherapeutics.diga.implementation.DigaOkHttpClient Maven / Gradle / Ivy

/*
 * Copyright 2021-2021 Alex Therapeutics AB and individual contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *
 */

package com.alextherapeutics.diga.implementation;

import com.alextherapeutics.diga.DigaHttpClient;
import com.alextherapeutics.diga.DigaHttpClientException;
import com.alextherapeutics.diga.DigaUtils;
import com.alextherapeutics.diga.model.DigaApiHttpRequest;
import com.alextherapeutics.diga.model.DigaApiHttpResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import lombok.Builder;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import nl.altindag.ssl.SSLFactory;
import okhttp3.*;

/**
 * Default HTTP client using OkHttp configured to trust the insurance company certificates and
 * provide yuor own certificate with each request.
 */
@Slf4j
public class DigaOkHttpClient implements DigaHttpClient {
  private final byte[] keyStoreFileContent;
  private final String keyStorePassword;
  private final byte[] certificatesFileContent;
  private final String certificatesPassword;

  private OkHttpClient client;

  @Builder
  public DigaOkHttpClient(
      @NonNull byte[] keyStoreFileContent,
      @NonNull String keyStorePassword,
      @NonNull byte[] certificatesFileContent,
      @NonNull String certificatesPassword)
      throws DigaHttpClientException {
    this.keyStoreFileContent = keyStoreFileContent;
    this.keyStorePassword = keyStorePassword;
    this.certificatesFileContent = certificatesFileContent;
    this.certificatesPassword = certificatesPassword;
    init();
  }

  @Override
  public DigaApiHttpResponse post(DigaApiHttpRequest request) throws DigaHttpClientException {
    try {
      var httpResponse = client.newCall(toOkHttpRequest(request)).execute();

      if (httpResponse.code() != 200) {
        throw new DigaHttpClientException(
            new Exception(
                String.format("Request returned with http status code %d", httpResponse.code())));
      }
      return parseResponse(httpResponse);
    } catch (IOException e) {
      log.error("Http request failed", e);
      throw new DigaHttpClientException(e);
    }
  }

  private DigaApiHttpResponse parseResponse(Response okHttpResponse) throws IOException {
    var responseBuilder = DigaApiHttpResponse.builder().statusCode(okHttpResponse.code());
    if (okHttpResponse.body() == null) {
      throw new IOException("Request returned with an empty body");
    }
    var multiPartReader = new MultipartReader(okHttpResponse.body());
    MultipartReader.Part nextPart = null;
    do {
      nextPart = multiPartReader.nextPart();
      responseBuilder = parsePart(nextPart, responseBuilder);
    } while (nextPart != null);
    multiPartReader.close();
    return responseBuilder.build();
  }

  private Request toOkHttpRequest(DigaApiHttpRequest digaApiHttpRequest) {
    var body =
        new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
            .addFormDataPart(
                "iksender", DigaUtils.ikNumberWithoutPrefix(digaApiHttpRequest.getSenderIK()))
            .addFormDataPart(
                "ikempfaenger",
                DigaUtils.ikNumberWithoutPrefix(digaApiHttpRequest.getRecipientIK()))
            .addFormDataPart("verfahren", digaApiHttpRequest.getProcessCode().getCode())
            .addFormDataPart(
                "nutzdaten",
                "anfrage.cms",
                RequestBody.create(
                    digaApiHttpRequest.getEncryptedContent(),
                    MediaType.parse("application/octet-stream")))
            .build();
    return new Request.Builder().url(digaApiHttpRequest.getUrl()).post(body).build();
  }

  private void init() throws DigaHttpClientException {
    try {
      var krankenKasseTrustStore = KeyStore.getInstance("PKCS12");
      krankenKasseTrustStore.load(
          new ByteArrayInputStream(certificatesFileContent), certificatesPassword.toCharArray());
      var clientStore = KeyStore.getInstance("PKCS12");
      clientStore.load(
          new ByteArrayInputStream(keyStoreFileContent), keyStorePassword.toCharArray());

      var sslFactory =
          SSLFactory.builder()
              .withDefaultTrustMaterial()
              .withSystemTrustMaterial()
              .withTrustMaterial(krankenKasseTrustStore)
              .withIdentityMaterial(clientStore, keyStorePassword.toCharArray())
              .build();

      client =
          new OkHttpClient.Builder()
              .sslSocketFactory(
                  sslFactory.getSslSocketFactory(), sslFactory.getTrustManager().orElseThrow())
              .hostnameVerifier(sslFactory.getHostnameVerifier())
              .build();
    } catch (IOException | CertificateException | NoSuchAlgorithmException | KeyStoreException e) {
      log.error("Failed to instantiate OkHttpClient", e);
      throw new DigaHttpClientException(e);
    }
  }

  private boolean headerContainsFormDataName(Headers headers, String name) {
    var it = headers.iterator();
    var result = false;
    while (it.hasNext()) {
      var next = it.next();
      if (next.getSecond().contains(name)) {
        result = true;
        break;
      }
    }
    return result;
  }

  private DigaApiHttpResponse.DigaApiHttpResponseBuilder parsePart(
      MultipartReader.Part part, DigaApiHttpResponse.DigaApiHttpResponseBuilder builder)
      throws IOException {
    if (part == null) {
      return builder;
    }
    var headers = part.headers();
    if (headerContainsFormDataName(headers, "iksender")) { // TODO make enum
      return builder.senderIK(part.body().readString(StandardCharsets.UTF_8));
    } else if (headerContainsFormDataName(headers, "ikempfaenger")) {
      return builder.recipientIK(part.body().readString(StandardCharsets.UTF_8));
    } else if (headerContainsFormDataName(headers, "verfahren")) {
      return builder.verfahren(part.body().readString(StandardCharsets.UTF_8));
    } else if (headerContainsFormDataName(headers, "nutzdaten")) {
      return builder.encryptedBody(part.body().readByteArray());
    }
    return builder;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy