Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
<module>saic-java-client</module>
<module>saic-java-api-gateway</module>
<module>saic-java-api-cli</module>
<module>saic-java-rest-api</module>
<module>saic-java-rest-client</module>
<module>saic-java-mqtt-gateway</module>
</modules>
<scm>
Expand Down
15 changes: 15 additions & 0 deletions saic-java-rest-api/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.saic-ismart-api</groupId>
<artifactId>saic-ismart-api-parent</artifactId>
<version>0.0.0-SNAPSHOT</version>
</parent>

<artifactId>saic-java-rest-api</artifactId>
<name>SAIC Java REST API</name>
<description>Implementation of the SAIC Rest API in Java</description>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package net.heberling.ismart.java.rest;

import java.nio.charset.StandardCharsets;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AESUtils {

public static final String AES = "AES";

public static String decrypt(String cipherText, String hexKey, String hexIV) {
if (StringUtils.isEmpty(cipherText)
|| StringUtils.isEmpty(hexKey)
|| StringUtils.isEmpty(hexIV)) {
return null;
}
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(HexUtils.hexToBytes(hexKey), AES);
IvParameterSpec ivParameterSpec = new IvParameterSpec(HexUtils.hexToBytes(hexIV));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(2, secretKeySpec, ivParameterSpec);
return new String(cipher.doFinal(HexUtils.hexToBytes(cipherText)), StandardCharsets.UTF_8);
} catch (Exception e2) {
throw new RuntimeException(e2);
}
}

public static String encrypt(String plainText, String hexKey, String hexIV) {
if (StringUtils.isEmpty(plainText)
|| StringUtils.isEmpty(hexKey)
|| StringUtils.isEmpty(hexIV)) {
return null;
}
try {
SecretKeySpec secretKeySpec = new SecretKeySpec(HexUtils.hexToBytes(hexKey), AES);
IvParameterSpec ivParameterSpec = new IvParameterSpec(HexUtils.hexToBytes(hexIV));
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(1, secretKeySpec, ivParameterSpec);
return HexUtils.bytesToHex(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e2) {
throw new RuntimeException(e2);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package net.heberling.ismart.java.rest;

public final class APIConfig {
public static final String CONTENT_ENCRYPTED = "1";
public static final String PARAM_AUTHENTICATION = "Basic c3dvcmQ6c3dvcmRfc2VjcmV0";
public static final String TENANT_ID = "459771";
public static final String USER_TYPE = "app";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package net.heberling.ismart.java.rest;

public class EncryptionUtils {
private EncryptionUtils() {}

public static String calculateRequestVerification(
String resourcePath,
long sendDate,
String tenant,
String contentType,
String bodyEncrypted,
String token) {
String str9 = resourcePath + tenant + token + APIConfig.USER_TYPE;
String a2 = HashUtils.md5(str9);
String str10 = sendDate + APIConfig.CONTENT_ENCRYPTED + contentType;
String a3 = HashUtils.md5(a2 + str10);
String str11 =
resourcePath
+ tenant
+ token
+ APIConfig.USER_TYPE
+ sendDate
+ APIConfig.CONTENT_ENCRYPTED
+ contentType
+ bodyEncrypted;
String a5 = HashUtils.md5(a3 + sendDate);
if (!StringUtils.isEmpty(a5) && !StringUtils.isEmpty(str11)) {
return MacUtils.hmacSha256(a5.getBytes(), str11);
}
return "";
}

public static String decryptResponse(String timeStamp, String contentType, String cipherText) {
String str4 = timeStamp + APIConfig.CONTENT_ENCRYPTED + contentType;
String a2 = StringUtils.isEmpty(str4) ? "" : HashUtils.md5(str4);
String hashedTimeStamp = HashUtils.md5(timeStamp);
if (!StringUtils.isEmpty(cipherText)) {
return AESUtils.decrypt(cipherText, a2, hashedTimeStamp);
}
return "";
}

public static String calculateResponseVerification(String str, String str2, String str3) {
String str4 = str + APIConfig.CONTENT_ENCRYPTED + str2;
String a2 = StringUtils.isEmpty(str4) ? "" : HashUtils.md5(str4);
String str5 = str + APIConfig.CONTENT_ENCRYPTED + str2 + str3;
String a4 = HashUtils.md5(a2 + str);
if (!StringUtils.isEmpty(a4) && !StringUtils.isEmpty(str5)) {

return MacUtils.hmacSha256(a4.getBytes(), str5);
}

return "";
}

public static final String BASE_URL_P = "https://gateway-mg-eu.soimt.com/api.app/v1/";

public static String encryptRequest(
String url, long time, String tenant, String token, String body, String contentType) {

String sendDate = String.valueOf(time);
// tenant
String replace = !StringUtils.isEmpty(url) ? url.replace(BASE_URL_P, "/") : "";
String encryptedBody = "";
if (!StringUtils.isEmpty(body)) {
String sb3 =
HashUtils.md5(replace + tenant + token + APIConfig.USER_TYPE)
+ sendDate
+ APIConfig.CONTENT_ENCRYPTED
+ contentType;
String a2 = HashUtils.md5(sb3);
String a3 = HashUtils.md5(sendDate);
if (!StringUtils.isEmpty(body) && !StringUtils.isEmpty(a2) && !StringUtils.isEmpty(a3)) {
encryptedBody = AESUtils.encrypt(body, a2, a3);
}
}

if (encryptedBody == null) {
encryptedBody = "";
}
return encryptedBody;
}

public static String decryptRequest(
String url, long time, String tenant, String token, String body, String contentType) {

String timeStamp = String.valueOf(time);
String resourcePath = !StringUtils.isEmpty(url) ? url.replace(BASE_URL_P, "/") : "";
if (!StringUtils.isEmpty(body)) {
String sb3 =
HashUtils.md5(resourcePath + tenant + token + APIConfig.USER_TYPE)
+ timeStamp
+ APIConfig.CONTENT_ENCRYPTED
+ contentType;
String a2 = HashUtils.md5(sb3);
String timeStampHash = HashUtils.md5(timeStamp);
if (!StringUtils.isEmpty(body)
&& !StringUtils.isEmpty(a2)
&& !StringUtils.isEmpty(timeStampHash)) {
return AESUtils.decrypt(body, a2, timeStampHash);
}
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package net.heberling.ismart.java.rest;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class HashUtils {
public static final String MD5 = "MD5";
public static final String SHA_1 = "SHA1";
public static final String SHA_256 = "SHA-256";

public static String md5(String str) {
try {
MessageDigest messageDigest = MessageDigest.getInstance(MD5);
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
byte[] digest = messageDigest.digest();
return HexUtils.bytesToHex(digest);
} catch (NoSuchAlgorithmException e2) {
throw new RuntimeException(e2);
}
}

public static String sha1(String str) {
try {
MessageDigest messageDigest = MessageDigest.getInstance(SHA_1);
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
return HexUtils.bytesToHex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}

public static String sha256(String str) {
try {
MessageDigest messageDigest = MessageDigest.getInstance(SHA_256);
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
return HexUtils.bytesToHex(messageDigest.digest());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package net.heberling.ismart.java.rest;

public class HexUtils {

public static String bytesToHex(byte[] byteArray) {
StringBuilder hexStringBuffer = new StringBuilder();
for (int i = 0; i < byteArray.length; i++) {
hexStringBuffer.append(byteToHex(byteArray[i]));
}
return hexStringBuffer.toString();
}

public static String byteToHex(byte num) {
return String.format("%02x", num);
}

public static byte[] hexToBytes(String hexString) {
if (hexString.length() % 2 == 1) {
throw new IllegalArgumentException("Invalid hexadecimal String supplied.");
}

byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
}
return bytes;
}

public static byte hexToByte(String str) {
return (byte) Integer.parseInt(str, 16);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.heberling.ismart.java.rest;

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class MacUtils {
private static final String HMAC_SHA_256 = "HmacSHA256";

public static String hmacSha256(byte[] bArr, String str) {
try {
Mac mac = Mac.getInstance(HMAC_SHA_256);
mac.init(new SecretKeySpec(bArr, HMAC_SHA_256));
return HexUtils.bytesToHex(mac.doFinal(str.getBytes(StandardCharsets.UTF_8)));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package net.heberling.ismart.java.rest;

public class StringUtils {
public static boolean isEmpty(String str2) {
return str2 == null || str2.isEmpty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.heberling.ismart.java.rest.api.v1;

/**
* @author Doug Culnane
*/
public abstract class JsonResponseMessage {

public static final Integer CODE_SUCCESS = 0;

Integer code;
String message;
String eventId;

abstract Object getData();

public boolean hasData() {
return getData() != null;
}

public String getEventId() {
return eventId;
}

public void setEventId(String eventId) {
this.eventId = eventId;
}

public JsonResponseMessage() {}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

@Override
public String toString() {
return "[" + code + "] " + message;
}

public boolean isSuccess() {
return code == CODE_SUCCESS;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package net.heberling.ismart.java.rest.api.v1;

/**
* @author Doug Culnane
*/
public class MessageList extends JsonResponseMessage {

String data;

public String getData() {
return data;
}

public void setData(String data) {
this.data = data;
}
}
Loading