/
Sunwave Health Digest Authentication

Sunwave Health Digest Authentication

Usage

Customer Generation and Usage

Generate Digest Example Code

/* * Copyright 2023 Sunwave Health * * 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 * * http://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.sunwave; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Base64; import java.util.TimeZone; import org.apache.commons.codec.digest.DigestUtils; public class DigestCreator { /** * userId is the email assigned to the user from sw_user_clinic.user_email. * If the combination of userId and clinic_id are not in sw_user_clinic * table the validation will fail. */ private final String userId; /** * clientId is the realm assigned to the user from * sw_user_clinic.clinic_id. If the combination of userId and clinic_id * are not in sw_user_clinic table the validation will fail. */ private final String clientId; /** * clinicId is the realm the user has permission to run rest calls against. */ private final String clinicId; /** * clientSecret is the secret key that was generated by the user's id and * clinic id. This comes from the sw_external_application table and is * used to calculate the HMAC. The HMAC the DigesterCreator calculates and * SunwaveEMR's DigestValidator must match. */ private final String clientSecret; /** * transactionId is a user chosen string that must be unique for the * whole clinic. This value is validated against the sw_api_transaction * table by transaction id and clinic id, the row is returned the * validation fails. */ private final String transactionId; /** * payload is only used on POST and PUT HTTP requests and is used to * calculate MD5 digest hash. */ private final String payload; /** * dateTime holds the current time in the following format * "Mon, 6 Feb 2023 16:35:45 +0000" as Base64 encoded string. This is * generated by getDateTimeBase64. ({@link #getDateTimeBase64()}) */ private final String dateTime; /** * PURPOSE: To calculate the MD5 digest of the payload and return the * lower case version of numeric digest as base 64 value. * @return Base64 value of the MD5 hash. * (@see <a href="https://en.wikipedia.org/wiki/MD5">MD5 Digest</a>) */ private String createMd5Digest() { byte[] digest = DigestUtils.md5Hex(payload.getBytes()).getBytes(); return Base64.getEncoder().encodeToString(digest); } /** * Purpose - To create the seed to be used with the user's private key to * generate the HMAC hash. In the case of GET HTTP requests the seed * comprises user id, client id, current time as Base64 string * {@link #dateTime}, clinic id and transaction id. In the case * of HTTP POST and PUT requests the MD5 digest of the payload is added * to the seed. * @return - Returns seed as clear text. */ private String createSeed() { String seed; if (payload == null) { seed = userId + ":" + clientId + ":" + dateTime + ":" + clinicId + ":" + transactionId; } else { seed = userId + ":" + clientId + ":" + dateTime + ":" + clinicId + ":" + transactionId + ":" + createMd5Digest(); } return seed; } /** * PURPOSE: To generate Base64 of the current timestamp. The format is * "Mon, 6 Feb 2023 16:42:14 +0000" Note it is using GMT * the local +0000 to generate the timestamp. * @return - Current time as Base64 string. */ private String getDateTimeBase64() { SimpleDateFormat df = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); df.setTimeZone(TimeZone.getTimeZone("GMT")); java.sql.Timestamp now = new java.sql.Timestamp(new java.util.Date().getTime()); String currentTime = df.format(now); byte[] encodedDate = Base64.getEncoder().encode(currentTime.getBytes()); return new String(encodedDate, StandardCharsets.UTF_8); } /** * Purpose: Create the digest token that can be used in Authorization Head * key with value of Digest concatenated with token: * "Digest sunwave-admin1:vQl91X514m11dTHHGYQPQ ...". The token consists of * user id, client id, current date time, clinic id, transaction id * HMAC code in the case of HTTP Get Request. For HTTP PUT and POST the * token consists of user id, client id, current date time, clinic id, * transaction id, MD5 digest of the payload and HMAC code. * (@see <a href="https://en.wikipedia.org/wiki/Digest_access_authentication">Digest Authentication</a>) * @return - digest token * @throws NoSuchAlgorithmException - Handles the message signature of * {@link #createHMAC(String)} * @throws InvalidKeyException - Handles the message signature of * {@link #createHMAC(String)} */ private String createToken() throws NoSuchAlgorithmException, InvalidKeyException { if (payload == null) { return userId + ":" + clientId + ":" + dateTime + ":" + clinicId + ":" + transactionId + ":" + createHMAC(createSeed()); } else { return userId + ":" + clientId + ":" + dateTime + ":" + clinicId + ":" + transactionId + ":" + createMd5Digest() + ":" + createHMAC(createSeed()); } } /** * Purpose: Create Base 64 encoded HMAC hash-based message authentication * code (@see <a href="https://en.wikipedia.org/wiki/HMAC">HMAC</a>). * Sunwave uses the SHA-512 Secure Hash Algorithm 2 * (@see <a href="https://en.wikipedia.org/wiki/SHA-2">SHAR-512</a>). * @param seed - The seed string to generate the HMAC on. * @return - HMAC Code as BAse 64 encoded String. * @throws NoSuchAlgorithmException - When Mac class does not support * the give hashing algorithm. Note it support HmacSHA512 so no problem. * @throws InvalidKeyException - {@link #clientSecret} key can not be * used by the SecretKeySpec class. */ private String createHMAC(final String seed) throws NoSuchAlgorithmException, InvalidKeyException { byte[] byteKey = clientSecret.getBytes(StandardCharsets.UTF_8); final String hmacSha512 = "HmacSHA512"; Mac sha512Hmac = Mac.getInstance(hmacSha512); SecretKeySpec keySpec = new SecretKeySpec(byteKey, hmacSha512); sha512Hmac.init(keySpec); byte[] macData = sha512Hmac. doFinal(seed.getBytes(StandardCharsets.UTF_8)); return Base64.getUrlEncoder().encodeToString(macData); } /** * Purpose: Create DigestCreator object populated with all the information * to generate digest authentication token. * @param userId - {@link #userId} * @param clientId - {@link #clientId} * @param clientSecret - {@link #clientSecret} * @param clinicId - {@link #clinicId} * @param transactionId - {@link #transactionId} * @param payload - {@link #payload} */ public DigestCreator(final String userId, final String clientId, final String clientSecret, final String clinicId, final String transactionId, final String payload) { this.userId = userId; this.clientId = clientId; this.clientSecret = clientSecret; this.clinicId = clinicId; this.transactionId = transactionId; this.payload = payload; this.dateTime = getDateTimeBase64(); } /** * Purpose: Provides command line interface to generate digest * authentication token. * @param args - Command line parameters. */ public static void main(final String[] args) { if ((args.length < 5) || (args.length > 6)) { System.out.println("java -cp ./target/DigestCreator-1.0-SNAPSHOT.jar com.sunwave.DigestCreator [user_id] [clinic_id] [client_id] [client_secret] [transaction_id] <payload>"); System.out.println("java -cp ./target/DigestCreator-1.0-SNAPSHOT.jar com.sunwave.DigestCreator sunwave-admin1 131 vQl91X514m11dTHHGYQPQkxJqNPxgbdJ C0iGincSREijXqeuB3P9sDdj1ZU6UwqVaUc6VLwhpcx2sBQmB85k8zWuIKSc6gkCAcnXm4JTk2YBFpH5fFDEPH0JyKg4SgchallGmNDc9fNkO1ojZxyKaZ5murQZFDvSW7iJl1CM5JESube8P0cdlqtiLoHb7BP4293S6FqG557TbIPS61ACp0lfAOu9fNXD6L2LD24j7QMRZpM8GE6GQOnY5nTaHGn42eBMjB8iMS9gx4P7iStJirC0vjq2miSC 0000002"); 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); try { System.out.println("Token: " + dc.createToken()); } catch (Exception e) { System.err.println("Unable to create authentication token."); e.printStackTrace(System.err); System.exit(-2); } 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

 

Data Dictionary

Term

Definition

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. It must have an 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 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 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

Related content

Rest API Security
Rest API Security
Read with this
User Permissions
User Permissions
More like this
Sunwave API - Attaching forms (attach forms)
Sunwave API - Attaching forms (attach forms)
Read with this
Sunwave 6.12.0 [ODD]
Sunwave 6.12.0 [ODD]
More like this
Sunwave 7.11
More like this