Skip to content

Commit 8f4034b

Browse files
authored
Java REST API implementation. (#147)
1 parent 807f201 commit 8f4034b

26 files changed

+1851
-0
lines changed

pom.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
<module>saic-java-client</module>
2828
<module>saic-java-api-gateway</module>
2929
<module>saic-java-api-cli</module>
30+
<module>saic-java-rest-api</module>
31+
<module>saic-java-rest-client</module>
3032
<module>saic-java-mqtt-gateway</module>
3133
</modules>
3234
<scm>

saic-java-rest-api/pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<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">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<parent>
6+
<groupId>io.github.saic-ismart-api</groupId>
7+
<artifactId>saic-ismart-api-parent</artifactId>
8+
<version>0.0.0-SNAPSHOT</version>
9+
</parent>
10+
11+
<artifactId>saic-java-rest-api</artifactId>
12+
<name>SAIC Java REST API</name>
13+
<description>Implementation of the SAIC Rest API in Java</description>
14+
15+
</project>
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import javax.crypto.Cipher;
5+
import javax.crypto.spec.IvParameterSpec;
6+
import javax.crypto.spec.SecretKeySpec;
7+
8+
public class AESUtils {
9+
10+
public static final String AES = "AES";
11+
12+
public static String decrypt(String cipherText, String hexKey, String hexIV) {
13+
if (StringUtils.isEmpty(cipherText)
14+
|| StringUtils.isEmpty(hexKey)
15+
|| StringUtils.isEmpty(hexIV)) {
16+
return null;
17+
}
18+
try {
19+
SecretKeySpec secretKeySpec = new SecretKeySpec(HexUtils.hexToBytes(hexKey), AES);
20+
IvParameterSpec ivParameterSpec = new IvParameterSpec(HexUtils.hexToBytes(hexIV));
21+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
22+
cipher.init(2, secretKeySpec, ivParameterSpec);
23+
return new String(cipher.doFinal(HexUtils.hexToBytes(cipherText)), StandardCharsets.UTF_8);
24+
} catch (Exception e2) {
25+
throw new RuntimeException(e2);
26+
}
27+
}
28+
29+
public static String encrypt(String plainText, String hexKey, String hexIV) {
30+
if (StringUtils.isEmpty(plainText)
31+
|| StringUtils.isEmpty(hexKey)
32+
|| StringUtils.isEmpty(hexIV)) {
33+
return null;
34+
}
35+
try {
36+
SecretKeySpec secretKeySpec = new SecretKeySpec(HexUtils.hexToBytes(hexKey), AES);
37+
IvParameterSpec ivParameterSpec = new IvParameterSpec(HexUtils.hexToBytes(hexIV));
38+
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
39+
cipher.init(1, secretKeySpec, ivParameterSpec);
40+
return HexUtils.bytesToHex(cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8)));
41+
} catch (Exception e2) {
42+
throw new RuntimeException(e2);
43+
}
44+
}
45+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
public final class APIConfig {
4+
public static final String CONTENT_ENCRYPTED = "1";
5+
public static final String PARAM_AUTHENTICATION = "Basic c3dvcmQ6c3dvcmRfc2VjcmV0";
6+
public static final String TENANT_ID = "459771";
7+
public static final String USER_TYPE = "app";
8+
}
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
public class EncryptionUtils {
4+
private EncryptionUtils() {}
5+
6+
public static String calculateRequestVerification(
7+
String resourcePath,
8+
long sendDate,
9+
String tenant,
10+
String contentType,
11+
String bodyEncrypted,
12+
String token) {
13+
String str9 = resourcePath + tenant + token + APIConfig.USER_TYPE;
14+
String a2 = HashUtils.md5(str9);
15+
String str10 = sendDate + APIConfig.CONTENT_ENCRYPTED + contentType;
16+
String a3 = HashUtils.md5(a2 + str10);
17+
String str11 =
18+
resourcePath
19+
+ tenant
20+
+ token
21+
+ APIConfig.USER_TYPE
22+
+ sendDate
23+
+ APIConfig.CONTENT_ENCRYPTED
24+
+ contentType
25+
+ bodyEncrypted;
26+
String a5 = HashUtils.md5(a3 + sendDate);
27+
if (!StringUtils.isEmpty(a5) && !StringUtils.isEmpty(str11)) {
28+
return MacUtils.hmacSha256(a5.getBytes(), str11);
29+
}
30+
return "";
31+
}
32+
33+
public static String decryptResponse(String timeStamp, String contentType, String cipherText) {
34+
String str4 = timeStamp + APIConfig.CONTENT_ENCRYPTED + contentType;
35+
String a2 = StringUtils.isEmpty(str4) ? "" : HashUtils.md5(str4);
36+
String hashedTimeStamp = HashUtils.md5(timeStamp);
37+
if (!StringUtils.isEmpty(cipherText)) {
38+
return AESUtils.decrypt(cipherText, a2, hashedTimeStamp);
39+
}
40+
return "";
41+
}
42+
43+
public static String calculateResponseVerification(String str, String str2, String str3) {
44+
String str4 = str + APIConfig.CONTENT_ENCRYPTED + str2;
45+
String a2 = StringUtils.isEmpty(str4) ? "" : HashUtils.md5(str4);
46+
String str5 = str + APIConfig.CONTENT_ENCRYPTED + str2 + str3;
47+
String a4 = HashUtils.md5(a2 + str);
48+
if (!StringUtils.isEmpty(a4) && !StringUtils.isEmpty(str5)) {
49+
50+
return MacUtils.hmacSha256(a4.getBytes(), str5);
51+
}
52+
53+
return "";
54+
}
55+
56+
public static final String BASE_URL_P = "https://gateway-mg-eu.soimt.com/api.app/v1/";
57+
58+
public static String encryptRequest(
59+
String url, long time, String tenant, String token, String body, String contentType) {
60+
61+
String sendDate = String.valueOf(time);
62+
// tenant
63+
String replace = !StringUtils.isEmpty(url) ? url.replace(BASE_URL_P, "/") : "";
64+
String encryptedBody = "";
65+
if (!StringUtils.isEmpty(body)) {
66+
String sb3 =
67+
HashUtils.md5(replace + tenant + token + APIConfig.USER_TYPE)
68+
+ sendDate
69+
+ APIConfig.CONTENT_ENCRYPTED
70+
+ contentType;
71+
String a2 = HashUtils.md5(sb3);
72+
String a3 = HashUtils.md5(sendDate);
73+
if (!StringUtils.isEmpty(body) && !StringUtils.isEmpty(a2) && !StringUtils.isEmpty(a3)) {
74+
encryptedBody = AESUtils.encrypt(body, a2, a3);
75+
}
76+
}
77+
78+
if (encryptedBody == null) {
79+
encryptedBody = "";
80+
}
81+
return encryptedBody;
82+
}
83+
84+
public static String decryptRequest(
85+
String url, long time, String tenant, String token, String body, String contentType) {
86+
87+
String timeStamp = String.valueOf(time);
88+
String resourcePath = !StringUtils.isEmpty(url) ? url.replace(BASE_URL_P, "/") : "";
89+
if (!StringUtils.isEmpty(body)) {
90+
String sb3 =
91+
HashUtils.md5(resourcePath + tenant + token + APIConfig.USER_TYPE)
92+
+ timeStamp
93+
+ APIConfig.CONTENT_ENCRYPTED
94+
+ contentType;
95+
String a2 = HashUtils.md5(sb3);
96+
String timeStampHash = HashUtils.md5(timeStamp);
97+
if (!StringUtils.isEmpty(body)
98+
&& !StringUtils.isEmpty(a2)
99+
&& !StringUtils.isEmpty(timeStampHash)) {
100+
return AESUtils.decrypt(body, a2, timeStampHash);
101+
}
102+
}
103+
return null;
104+
}
105+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.security.MessageDigest;
5+
import java.security.NoSuchAlgorithmException;
6+
7+
public class HashUtils {
8+
public static final String MD5 = "MD5";
9+
public static final String SHA_1 = "SHA1";
10+
public static final String SHA_256 = "SHA-256";
11+
12+
public static String md5(String str) {
13+
try {
14+
MessageDigest messageDigest = MessageDigest.getInstance(MD5);
15+
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
16+
byte[] digest = messageDigest.digest();
17+
return HexUtils.bytesToHex(digest);
18+
} catch (NoSuchAlgorithmException e2) {
19+
throw new RuntimeException(e2);
20+
}
21+
}
22+
23+
public static String sha1(String str) {
24+
try {
25+
MessageDigest messageDigest = MessageDigest.getInstance(SHA_1);
26+
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
27+
return HexUtils.bytesToHex(messageDigest.digest());
28+
} catch (NoSuchAlgorithmException e) {
29+
throw new RuntimeException(e);
30+
}
31+
}
32+
33+
public static String sha256(String str) {
34+
try {
35+
MessageDigest messageDigest = MessageDigest.getInstance(SHA_256);
36+
messageDigest.update(str.getBytes(StandardCharsets.UTF_8));
37+
return HexUtils.bytesToHex(messageDigest.digest());
38+
} catch (NoSuchAlgorithmException e) {
39+
throw new RuntimeException(e);
40+
}
41+
}
42+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
public class HexUtils {
4+
5+
public static String bytesToHex(byte[] byteArray) {
6+
StringBuilder hexStringBuffer = new StringBuilder();
7+
for (int i = 0; i < byteArray.length; i++) {
8+
hexStringBuffer.append(byteToHex(byteArray[i]));
9+
}
10+
return hexStringBuffer.toString();
11+
}
12+
13+
public static String byteToHex(byte num) {
14+
return String.format("%02x", num);
15+
}
16+
17+
public static byte[] hexToBytes(String hexString) {
18+
if (hexString.length() % 2 == 1) {
19+
throw new IllegalArgumentException("Invalid hexadecimal String supplied.");
20+
}
21+
22+
byte[] bytes = new byte[hexString.length() / 2];
23+
for (int i = 0; i < hexString.length(); i += 2) {
24+
bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
25+
}
26+
return bytes;
27+
}
28+
29+
public static byte hexToByte(String str) {
30+
return (byte) Integer.parseInt(str, 16);
31+
}
32+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
import java.nio.charset.StandardCharsets;
4+
import java.security.InvalidKeyException;
5+
import java.security.NoSuchAlgorithmException;
6+
import javax.crypto.Mac;
7+
import javax.crypto.spec.SecretKeySpec;
8+
9+
public class MacUtils {
10+
private static final String HMAC_SHA_256 = "HmacSHA256";
11+
12+
public static String hmacSha256(byte[] bArr, String str) {
13+
try {
14+
Mac mac = Mac.getInstance(HMAC_SHA_256);
15+
mac.init(new SecretKeySpec(bArr, HMAC_SHA_256));
16+
return HexUtils.bytesToHex(mac.doFinal(str.getBytes(StandardCharsets.UTF_8)));
17+
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
18+
throw new RuntimeException(e);
19+
}
20+
}
21+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package net.heberling.ismart.java.rest;
2+
3+
public class StringUtils {
4+
public static boolean isEmpty(String str2) {
5+
return str2 == null || str2.isEmpty();
6+
}
7+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package net.heberling.ismart.java.rest.api.v1;
2+
3+
/**
4+
* @author Doug Culnane
5+
*/
6+
public abstract class JsonResponseMessage {
7+
8+
public static final Integer CODE_SUCCESS = 0;
9+
10+
Integer code;
11+
String message;
12+
String eventId;
13+
14+
abstract Object getData();
15+
16+
public boolean hasData() {
17+
return getData() != null;
18+
}
19+
20+
public String getEventId() {
21+
return eventId;
22+
}
23+
24+
public void setEventId(String eventId) {
25+
this.eventId = eventId;
26+
}
27+
28+
public JsonResponseMessage() {}
29+
30+
public Integer getCode() {
31+
return code;
32+
}
33+
34+
public void setCode(Integer code) {
35+
this.code = code;
36+
}
37+
38+
public String getMessage() {
39+
return message;
40+
}
41+
42+
public void setMessage(String message) {
43+
this.message = message;
44+
}
45+
46+
@Override
47+
public String toString() {
48+
return "[" + code + "] " + message;
49+
}
50+
51+
public boolean isSuccess() {
52+
return code == CODE_SUCCESS;
53+
}
54+
}

0 commit comments

Comments
 (0)