Skip to end of metadata
Go to start of metadata

You are viewing an old version of this content. View the current version.

Compare with Current View Version History

« Previous Version 20 Next »

Usage

Customer Generation and Usage

Generate Digest Example Code

package com.sunwave;


import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.TimeZone;
import java.util.Date;

import org.apache.commons.codec.digest.DigestUtils;

public class DigestCreator {
    private String userId;
    private String clientId;
    private String clientSecret; // Realm Private Key "select client_secret from sw_external_application where client_id = ? and clinic_id = ?
    private String clinicId;

    private String transactionId; //"select id from sw_api_transaction where transaction_id = ? and clinic_id = ?"

    private String payload;


    private String createMd5Digest() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        return Base64.getEncoder().encodeToString(DigestUtils.md5Hex(payload.getBytes()).getBytes());
    }
    private String createSeed() throws NoSuchAlgorithmException, UnsupportedEncodingException {
        String seed = null;
        if (payload == null) {
            seed = userId + ":" + clientId + ":" + getDateTimeBase64() + ":" + clinicId + ":" + transactionId;
        } else {
            seed = userId + ":" + clientId + ":" + getDateTimeBase64() + ":" + clinicId + ":" + transactionId + ":" + createMd5Digest();
        }
        return seed;
    }

    private String getDateTimeBase64() throws UnsupportedEncodingException {
        SimpleDateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z");
        df.setTimeZone(TimeZone.getTimeZone("GMT"));
        Date d = new Date();
        java.sql.Timestamp now = new java.sql.Timestamp(new java.util.Date().getTime());
        String dateTime = df.format(now);
        byte[] encodedDate = Base64.getEncoder().encode(dateTime.getBytes());
        return new String(encodedDate, "UTF8");
    }

    private String createTransactionId() {
        return null;
    }

    private String createToken() throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        if (payload == null)
            return userId + ":" + clientId + ":" + getDateTimeBase64() + ":" + clinicId + ":" + transactionId + ":" + createHMAC(createSeed());
        else
            return userId + ":" + clientId + ":" + getDateTimeBase64() + ":" + clinicId + ":" + transactionId + ":" + createMd5Digest() + ":" + createHMAC(createSeed());
    }

    private String createHMAC(String message) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        byte[] byteKey = clientSecret.getBytes("UTF-8");
        final String HMAC_SHA512 = "HmacSHA512";
        Mac sha512_HMAC = Mac.getInstance(HMAC_SHA512);
        SecretKeySpec keySpec = new SecretKeySpec(byteKey, HMAC_SHA512);
        sha512_HMAC.init(keySpec);
        byte[] mac_data = sha512_HMAC.
            doFinal(message.getBytes("UTF-8"));
        return Base64.getUrlEncoder().encodeToString(mac_data);
    }


    public DigestCreator(String userId, String clientId, String clientSecret, String clinic_id, String transactionId, String payload) {
        this.userId = userId;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.clinicId = clinic_id;
        this.transactionId = transactionId;
        this.payload = payload;
    }

    public static void main( String args[] ) throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
        if ((args.length < 5) || (args.length > 6)) {
            System.out.println("usage: java -cp . org.baudekin.DigestCreator user_id clinic_id client_id, client_secret, transaction_id, <pay load>");
            System.exit(-1);
        }
        String payload = null;
        // We are doing post
        if (args.length == 6) {
            payload = args[5];
        }

        DigestCreator dc = new DigestCreator(args[0], args[2], args[3], args[1], args[4], payload);
        System.out.println("Token: " + dc.createToken());
        System.out.println("md5 digest: " + dc.createMd5Digest());
        System.exit(0);
    }


}
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>org.example</groupId>
  <artifactId>DigestCreater</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>jar</packaging>

  <dependencies>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.15</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-assembly-plugin</artifactId>
        <version>3.4.2</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>single</goal>
            </goals>
            <configuration>
              <archive>
                <manifest>
                  <mainClass>
                    com.sunwave.DigestCreator
                  </mainClass>
                </manifest>
              </archive>
              <descriptorRefs>
                <descriptorRef>jar-with-dependencies</descriptorRef>
              </descriptorRefs>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>8</source>
          <target>8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Generate GET Request Digest

java -cp ./target/DigestCreater-1.0-SNAPSHOT.jar com.sunwave.DigestCreator sunwave-admin1 131 vQl91X514m11dTHHGYQPQkxJqNPxgbdJ C0iGincSREijXqeuB3P9sDdj1ZU6UwqVaUc6VLwhpcx2sBQmB85k8zWuIKSc6gkCAcnXm4JTk2YBFpH5fFDEPH0JyKg4SgchallGmNDc9fNkO1ojZxyKaZ5murQZFDvSW7iJl1CM5JESube8P0cdlqtiLoHb7BP4293S6FqG557TbIPS61ACp0lfAOu9fNXD6L2LD24j7QMRZpM8GE6GQOnY5nTaHGn42eBMjB8iMS9gx4P7iStJirC0vjq2miSC 0000002

Generate POST Request Digest

#!/usr/bin/env bash
TRANSACTION_ID=$1
#PAYLOAD="eyJjYWxsZXJfZmlyc3RfbmFtZSIgOiAiQXBpLTEwIiwgImNhbGxlcl9sYXN0X25hbWUiIDogIkFwaS0xMCIsICJwYXRpZW50X2ZpcnN0X25hbWUiIDogIkFwaS0xMCIsICJwYXRpZW50X2xhc3RfbmFtZSIgOiAiQXBpLTEwIiwgInBhdGllbnRfZGF0ZV9vZl9iaXJ0aCIgOiAiMTk4MC0wNC0wNCIsICJhY2NvdW50X2lkIjogIjQzMSIsICJzY2hlZHVsZWRfc2VydmljZV9mYWNpbGl0eV9pZCIgOiAiMTIzNCIsICJzY2hlZHVsZWRfYWRtaXNzaW9uX2RhdGUiIDogIjIwMjItMTAtMDgiLCAic2NoZWR1bGVkX2FkbWlzc2lvbl90aW1lIjogIjA5OjAwIiwgInNjaGVkdWxlZF9sZXZlbF9vZl9jYXJlX2lkIjogIjEiLCAic2NoZWR1bGVkX3Byb2dyYW0iOiAiMTA2NSIsICJwYXRpZW50X3Bob25lX21vYmlsZSI6ICI3ODY0Mzg4Nzg2IiwgInBhdGllbnRfcGhvbmVfd29yayI6ICI3ODY0Mzg4Nzg2IiwgInBhdGllbnRfcGhvbmVfaG9tZSI6ICI3ODY0Mzg4Nzg2IiwgInBhdGllbnRfc3NuIjogIjQ1NS02Ni02Njc3IiwgInBhdGllbnRfZW1haWwiOiAiYXBpLTZAZ21haWwuY29tIiwgInBhdGllbnRfYWRkcmVzcyI6ICIzMzc1IFcgNzZTdCIsICJwYXRpZW50X2NpdHkiOiAiTWlhbWkiLCAicGF0aWVudF9zdGF0ZSI6ICJGTCIsICJwYXRpZW50X3ppcCI6ICIzMzAxOSIsICJwYXRpZW50X2dlbmRlcl9jb2RlIjogIk0iLCAicmFjZSI6ICJJbmRpYW4iLCAibWFya2V0aW5nX3NvdXJjZV9jb250YWN0X2lkIjogIjM2IiwgImFkbWlzc2lvbl9yZXByZXNlbnRhdGl2ZSI6ICIyNzAxNiIsICJpbnN1cmFuY2VfcHJvdmlkZXIiOiAiOTc0IiwgIm1lbWJlcl9pZCI6ICIxMTIyIiwgImluc3VyYW5jZV9ncm91cF9udW1iZXIiOiAiMzAwMCIsICJzY2hlZHVsZWRfaW50YWtlX21lc3NhZ2UiOiAiTmVlZCB0byBwYXkifQo="
PAYLOAD='{"caller_first_name" : "Api-10", "caller_last_name" : "Api-10", "patient_first_name" : "Api-10", "patient_last_name" : "Api-10", "patient_date_of_birth" : "1980-04-04", "account_id": "431", "scheduled_service_facility_id" : "1234", "scheduled_admission_date" : "2022-10-08", "scheduled_admission_time": "09:00", "scheduled_level_of_care_id": "1", "scheduled_program": "1065", "patient_phone_mobile": "7864388786", "patient_phone_work": "7864388786", "patient_phone_home": "7864388786", "patient_ssn": "455-66-6677", "patient_email": "api-6@gmail.com", "patient_address": "3375 W 76St", "patient_city": "Miami", "patient_state": "FL", "patient_zip": "33019", "patient_gender_code": "M", "race": "Indian", "marketing_source_contact_id": "36", "admission_representative": "27016", "insurance_provider": "974", "member_id": "1122", "insurance_group_number": "3000", "scheduled_intake_message": "Need to pay"}'
# {
#       "caller_first_name" : "Api-10",
#       "caller_last_name" : "Api-10",
#       "patient_first_name" : "Api-10",
#       "patient_last_name" : "Api-10",
#       "patient_date_of_birth" : "1980-04-04",
#       "account_id": "431",
#       "scheduled_service_facility_id" : "1234",
#       "scheduled_admission_date" : "2022-10-08",
#       "scheduled_admission_time": "09:00",
#       "scheduled_level_of_care_id": "1",
#       "scheduled_program": "1065",
#       "patient_phone_mobile": "7864388786",
#       "patient_phone_work": "7864388786",
#       "patient_phone_home": "7864388786",
#       "patient_ssn": "455-66-6677",
#       "patient_email": "api-6@gmail.com",
#       "patient_address": "3375 W 76St",
#       "patient_city": "Miami",
#       "patient_state": "FL",
#       "patient_zip": "33019",
#       "patient_gender_code": "M",
#    "race": "Indian",
#       "marketing_source_contact_id": "36",
#       "admission_representative": "27016",
#       "insurance_provider": "974",
#       "member_id": "1122",
#       "insurance_group_number": "3000",
#       "scheduled_intake_message": "Need to pay"
#}
java -jar ./target/DigestCreater-1.0-SNAPSHOT-jar-with-dependencies.jar sunwave-admin1 131 vQl91X514m11dTHHGYQPQkxJqNPxgbdJ C0iGincSREijXqeuB3P9sDdj1ZU6UwqVaUc6VLwhpcx2sBQmB85k8zWuIKSc6gkCAcnXm4JTk2YBFpH5fFDEPH0JyKg4SgchallGmNDc9fNkO1ojZxyKaZ5murQZFDvSW7iJl1CM5JESube8P0cdlqtiLoHb7BP4293S6FqG557TbIPS61ACp0lfAOu9fNXD6L2LD24j7QMRZpM8GE6GQOnY5nTaHGn42eBMjB8iMS9gx4P7iStJirC0vjq2miSC "${TRANSACTION_ID}" "${PAYLOAD}"
source ./postgen.sh test00013

Data Dictionary

Term

Definition

Client Id

The unique identifier we assign to user of external API call. Random 32 character string generated using custom algorithm. https://github.com/sunwavehealth/SunwaveEMR/blob/d46b653451ce24623a0c6c72d0aa4e2313c5c0f9/src/main/java/com/sunwave/emr/server/security/ExternalApplicationProcessor.java#L96

Client Secret

Secret we assign to the user of the Rest API. It is 256 characters randomly generated by custom algorithm. https://github.com/sunwavehealth/SunwaveEMR/blob/d46b653451ce24623a0c6c72d0aa4e2313c5c0f9/src/main/java/com/sunwave/emr/server/security/ExternalApplicationProcessor.java#L96

User Id

The use’s account for Sunwave Health. I must have email to be used for this validation.

Clinic Id

Realm to the user id is to be validated against.

Transaction Id

A unique id the customer generates for each transaction. Can not be reused.

Payload

Only used for POST operations and is the base 64 encoded string representing the data to be put into Sunwave.

MD5 Digest

Message-digest algorithm for producing 128-bit hash values. See https://en.wikipedia.org/wiki/MD5 Sunwave uses it as a checksum to validate the request has not been modified.

Seed

For GET requests: User Id + ":" + Client Id + ":" + getDateTimeBase64() + ":" + Clinic Id + ":" + Transaction Id;For POST requests User Id + ":" + Client Id + ":" + getDateTimeBase64() + ":" + Clinic Id + ":" + Transaction Id + “:” + MD5 Digest

HMAC

Hash Based Method Authentication code see https://en.wikipedia.org/wiki/HMAC Used to verify both the data integrity and authenticity of the user’s request.

Token

This is the string to used as the Digest. User Id + ":" + Client Id + ":" + getDateTimeBase64() + ":" + Clinic Id + ":" + Transaction Id + “:” + HMAC of the seed

Software Design

GET Request Validation Trace

##### Begin GET Validation Trace
01 Feb 2023 10:21:01,666~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.DigestValidator::validateTransactionId line: 79
01 Feb 2023 10:21:01,671~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ select id from sw_api_transaction where transaction_id = '0000002' and clinic_id = '131'
01 Feb 2023 10:21:08,263~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.DigestValidator::validateTransactionId line: 84
01 Feb 2023 10:21:08,265~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ insert into sw_api_transaction (transaction_id, created_on,clinic_id) values ('0000002',str_to_date('2023-02-01 10:21:06','%Y-%m-%d %H:%i:%s'),'131')
01 Feb 2023 10:22:09,078~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.DigestValidator::validateGET line: 52
01 Feb 2023 10:22:09,081~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ select user_email from sw_user_clinic where sw_user_clinic.clinic_id = '131' and sw_user_clinic.user_email = 'sunwave-admin1'
01 Feb 2023 10:22:12,955~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.DigestValidator::getPrivateKey line: 73
01 Feb 2023 10:22:12,959~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ select client_secret from sw_external_application where client_id = 'vQl91X514m11dTHHGYQPQkxJqNPxgbdJ' and clinic_id = '131'
##### End GET Validation Trace
##### Begin GET Users Trace
01 Feb 2023 10:23:55,725~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.APISecurityFilter::validateAPICalls line: 198
01 Feb 2023 10:23:55,726~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ select api_call_limit_per_day from sw_clinic where clinic_id = '131'
01 Feb 2023 10:23:55,739~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.APISecurityFilter::validateAPICalls line: 200
01 Feb 2023 10:23:55,739~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ select call_log_count from sw_api_call_count_log where call_log_date = '2023-02-01' and clinic_id = '131'
01 Feb 2023 10:23:55,747~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.security.APISecurityFilter::validateAPICalls line: 207
01 Feb 2023 10:23:55,747~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ update sw_api_call_count_log set call_log_count = call_log_count + 1 where call_log_date = '2023-02-01' and clinic_id = '131'
01 Feb 2023 10:23:55,757~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.Processor::getParentClinicId line: 1535
01 Feb 2023 10:23:55,757~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ SELECT parent_clinic_id FROM sw_clinic where clinic_id='131'
01 Feb 2023 10:23:55,762~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.server.Processor::getTimezoneId line: 1584
01 Feb 2023 10:23:55,762~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ SELECT timezone_id, clinic_id FROM sw_clinic where id='131' 
01 Feb 2023 10:23:55,768~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:37 ~  ~ *********** com.sunwave.emr.dao.UserDao::getUsersByClinicExcludingSunwaveUsers line: 763
01 Feb 2023 10:23:55,768~ DEBUG ~ com.sunwave.emr.server.util.LoggerUtils:38 ~  ~ SELECT mb2_user.id, mb2_user.email, mb2_user.first_name, mb2_user.last_name, ifnull(mb2_user.created_on,sw_user_clinic.created_on) created_on, mb2_user.created_by,
 if(sw_user_clinic.last_login='2011-01-01 00:00:00', '', sw_user_clinic.last_login) last_login  
FROM mb2_user   inner join sw_user_clinic        on sw_user_clinic.user_email = mb2_user.email WHERE sw_user_clinic.clinic_id='131'        and ((mb2_user.is_sunwave != 'true' and mb2_user.is_sunwave_user != 'true') or            (mb2_user.is_sunwave is null and mb2_user.is_sunwave_user is null)) 
##### End GET Users Trace
  • No labels