|
@@ -0,0 +1,255 @@
|
|
|
|
+package io.nexilis
|
|
|
|
+
|
|
|
|
+import okhttp3.*
|
|
|
|
+import okhttp3.RequestBody.Companion.toRequestBody
|
|
|
|
+import okio.Buffer
|
|
|
|
+import org.json.JSONObject
|
|
|
|
+import java.io.IOException
|
|
|
|
+import java.net.Socket
|
|
|
|
+import java.nio.file.Files
|
|
|
|
+import java.nio.file.Paths
|
|
|
|
+import java.security.*
|
|
|
|
+import java.security.cert.CertificateException
|
|
|
|
+import java.security.cert.X509Certificate
|
|
|
|
+import java.security.spec.InvalidKeySpecException
|
|
|
|
+import java.security.spec.PKCS8EncodedKeySpec
|
|
|
|
+import java.text.SimpleDateFormat
|
|
|
|
+import java.util.*
|
|
|
|
+import java.util.concurrent.TimeUnit
|
|
|
|
+import javax.crypto.Mac
|
|
|
|
+import javax.crypto.spec.SecretKeySpec
|
|
|
|
+import javax.net.ssl.*
|
|
|
|
+
|
|
|
|
+fun main(args: Array<String>) {
|
|
|
|
+ try {
|
|
|
|
+ val bytes = Files.readAllBytes(Paths.get(args[0]))
|
|
|
|
+ val json = JSONObject(String(bytes))
|
|
|
|
+ val host = json.getString("host")
|
|
|
|
+ val path = json.getString("path")
|
|
|
|
+ val credential = json.getJSONObject("credential")
|
|
|
|
+ val body = json.getJSONObject("body")
|
|
|
|
+ val clientKey = credential.getString("client_key")
|
|
|
|
+ val clientSecret = credential.getString("client_secret")
|
|
|
|
+ val privateKey = credential.getString("private_key")
|
|
|
|
+ val partnerId = credential.getString("partner_id")
|
|
|
|
+ println("CLIENT-KEY = $clientKey")
|
|
|
|
+ println("CLIENT-SECRET = $clientSecret")
|
|
|
|
+ println("PRIVATE-KEY = $privateKey")
|
|
|
|
+ println("PARTNER-ID = $partnerId")
|
|
|
|
+ val timeStamp: String = getXTimestamp()
|
|
|
|
+ println("TIMESTAMP = $timeStamp")
|
|
|
|
+ println("===========================================================")
|
|
|
|
+ var signature: String = createAuthSignature(clientKey, privateKey, timeStamp)
|
|
|
|
+ val client: OkHttpClient = getUnsafeOkHttpClient()
|
|
|
|
+ val auth = "{\"additionalInfo\":{},\"grantType\":\"client_credentials\"}"
|
|
|
|
+ var requestBody: RequestBody = auth.toByteArray().toRequestBody(null, 0, auth.length)
|
|
|
|
+ var request: Request = Request.Builder()
|
|
|
|
+ .addHeader("Host", "service.synxchro.co.id")
|
|
|
|
+ .addHeader("Content-Type", "application/json")
|
|
|
|
+ .addHeader("x-timestamp", timeStamp)
|
|
|
|
+ .addHeader("x-client-key", clientKey)
|
|
|
|
+ .addHeader("x-signature", signature)
|
|
|
|
+ .url("https://service.synxchro.co.id/fello_snap/v1/access/token/b2b")
|
|
|
|
+ .post(requestBody)
|
|
|
|
+ .build()
|
|
|
|
+ var accessToken: String
|
|
|
|
+ client.newCall(request).execute().use { response ->
|
|
|
|
+ val responseBody = response.body
|
|
|
|
+ val responseBodyString = responseBody?.string() ?: "{}"
|
|
|
|
+ println(responseBodyString)
|
|
|
|
+ val jsonObject1 = JSONObject(responseBodyString)
|
|
|
|
+ if (!jsonObject1.has("accessToken")) return
|
|
|
|
+ accessToken = jsonObject1.getString("accessToken")
|
|
|
|
+ }
|
|
|
|
+ println("===========================================================")
|
|
|
|
+ signature = createServiceSignature(clientSecret, path, accessToken, timeStamp, body.toString())
|
|
|
|
+ requestBody = body.toString().toByteArray().toRequestBody(null, 0, body.toString().length)
|
|
|
|
+ request = Request.Builder()
|
|
|
|
+ .addHeader("Host", "service.synxchro.co.id")
|
|
|
|
+ .addHeader("Content-Type", "application/json")
|
|
|
|
+ .addHeader("Authorization", "Bearer $accessToken")
|
|
|
|
+ .addHeader("x-timestamp", timeStamp)
|
|
|
|
+ .addHeader("x-client-key", clientKey)
|
|
|
|
+ .addHeader("x-signature", signature)
|
|
|
|
+ .addHeader("x-external-id", getRandomId())
|
|
|
|
+ .addHeader("x-partner-id", partnerId)
|
|
|
|
+ .addHeader("channel-id", "1")
|
|
|
|
+ .url("https://$host$path")
|
|
|
|
+ .post(requestBody)
|
|
|
|
+ .build()
|
|
|
|
+ client.newCall(request).execute().use { response ->
|
|
|
|
+ val responseBody = response.body
|
|
|
|
+ val responseBodyString = responseBody?.string() ?: "{}"
|
|
|
|
+ println(responseBodyString)
|
|
|
|
+ }
|
|
|
|
+ } catch (e: java.lang.Exception) {
|
|
|
|
+ e.printStackTrace()
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+//
|
|
|
|
+//
|
|
|
|
+
|
|
|
|
+@Throws(
|
|
|
|
+ NoSuchAlgorithmException::class,
|
|
|
|
+ InvalidKeySpecException::class,
|
|
|
|
+ InvalidKeyException::class,
|
|
|
|
+ SignatureException::class
|
|
|
|
+)
|
|
|
|
+fun createAuthSignature(clientKey: String, privateKey: String, timeStamp: String): String {
|
|
|
|
+ return Base64.getEncoder().encodeToString(signRsa(clientKey, privateKey, timeStamp))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@Throws(
|
|
|
|
+ NoSuchAlgorithmException::class,
|
|
|
|
+ InvalidKeySpecException::class,
|
|
|
|
+ InvalidKeyException::class,
|
|
|
|
+ SignatureException::class
|
|
|
|
+)
|
|
|
|
+fun signRsa(clientKey: String, privateKey: String, timeStamp: String): ByteArray {
|
|
|
|
+ val signature = Signature.getInstance("SHA256withRSA")
|
|
|
|
+ signature.initSign(generatePrivateKey(privateKey))
|
|
|
|
+ signature.update(("$clientKey|$timeStamp").toByteArray())
|
|
|
|
+ return signature.sign()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun getXTimestamp(): String {
|
|
|
|
+ return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").format(Date())
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun getXTimestamp(date: Date?): String {
|
|
|
|
+ return SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").format(date)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@Throws(NoSuchAlgorithmException::class, InvalidKeySpecException::class)
|
|
|
|
+fun generatePrivateKey(privateKey: String): PrivateKey {
|
|
|
|
+ val decodeBytes: ByteArray = Base64.getDecoder().decode(privateKey)
|
|
|
|
+ val kf = KeyFactory.getInstance("RSA")
|
|
|
|
+ return kf.generatePrivate(PKCS8EncodedKeySpec(decodeBytes))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@Throws(NoSuchAlgorithmException::class, InvalidKeyException::class)
|
|
|
|
+fun createServiceSignature(secret: String, path: String, accessToken: String, timeStamp: String, body: String): String {
|
|
|
|
+ val hex: String = bytesToHex(hashed256(body)).lowercase(Locale.getDefault())
|
|
|
|
+ val data = "post:$path:$accessToken:$hex:$timeStamp"
|
|
|
|
+ return Base64.getEncoder().encodeToString(calculateHMAC(secret, data))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+private const val HMAC_SHA512 = "HmacSHA512"
|
|
|
|
+
|
|
|
|
+@Throws(NoSuchAlgorithmException::class, InvalidKeyException::class)
|
|
|
|
+fun calculateHMAC(secret: String, data: String): ByteArray {
|
|
|
|
+ val secretKeySpec = SecretKeySpec(secret.toByteArray(), HMAC_SHA512)
|
|
|
|
+ val mac = Mac.getInstance(HMAC_SHA512)
|
|
|
|
+ mac.init(secretKeySpec)
|
|
|
|
+ return mac.doFinal(data.toByteArray())
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+@Throws(NoSuchAlgorithmException::class)
|
|
|
|
+fun hashed256(data: String): ByteArray {
|
|
|
|
+ val digest = MessageDigest.getInstance("SHA-256")
|
|
|
|
+ return digest.digest(data.toByteArray())
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun bytesToHex(bytes: ByteArray): String {
|
|
|
|
+ val sb = StringBuilder()
|
|
|
|
+ for (b in bytes) {
|
|
|
|
+ sb.append(String.format("%02x", b))
|
|
|
|
+ }
|
|
|
|
+ return sb.toString()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun getRandomId(): String {
|
|
|
|
+ return System.nanoTime().toString()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun getUnsafeOkHttpClient(): OkHttpClient {
|
|
|
|
+ try {
|
|
|
|
+ val trustAllCerts = arrayOf<TrustManager>(object : X509ExtendedTrustManager() {
|
|
|
|
+ @Throws(CertificateException::class)
|
|
|
|
+ override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String, socket: Socket) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Throws(CertificateException::class)
|
|
|
|
+ override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String, socket: Socket) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Throws(CertificateException::class)
|
|
|
|
+ override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String, engine: SSLEngine) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Throws(CertificateException::class)
|
|
|
|
+ override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String, engine: SSLEngine) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Throws(CertificateException::class)
|
|
|
|
+ override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Throws(CertificateException::class)
|
|
|
|
+ override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ override fun getAcceptedIssuers(): Array<X509Certificate> {
|
|
|
|
+ return arrayOf()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ val sslContext = SSLContext.getInstance("TLS")
|
|
|
|
+ sslContext.init(null, trustAllCerts, SecureRandom())
|
|
|
|
+ val sslSocketFactory = sslContext.socketFactory
|
|
|
|
+ val builder: OkHttpClient.Builder = OkHttpClient.Builder()
|
|
|
|
+ builder.addInterceptor(LoggingInterceptor())
|
|
|
|
+ builder.followRedirects(false)
|
|
|
|
+ builder.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
|
|
|
|
+ builder.hostnameVerifier { _: String?, _: SSLSession? -> true }
|
|
|
|
+ builder.readTimeout(30, TimeUnit.SECONDS)
|
|
|
|
+ return builder.build()
|
|
|
|
+ } catch (e: Exception) {
|
|
|
|
+ throw RuntimeException(e)
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+class LoggingInterceptor : Interceptor {
|
|
|
|
+ @Throws(IOException::class)
|
|
|
|
+ override fun intercept(chain: Interceptor.Chain): Response {
|
|
|
|
+ val request: Request = chain.request()
|
|
|
|
+
|
|
|
|
+ val t1 = System.nanoTime()
|
|
|
|
+ print(
|
|
|
|
+ "OkHttp", String.format(
|
|
|
|
+ "Sending request %s%n%s",
|
|
|
|
+ request.url, request.headers
|
|
|
|
+ )
|
|
|
|
+ )
|
|
|
|
+ print(bodyToString(request) + "\n")
|
|
|
|
+
|
|
|
|
+ val response: Response = chain.proceed(request)
|
|
|
|
+
|
|
|
|
+ val t2 = System.nanoTime()
|
|
|
|
+ print(
|
|
|
|
+ "OkHttp", String.format(
|
|
|
|
+ "Received response for %s in %.1fms%n%s",
|
|
|
|
+ response.request.url, (t2 - t1) / 1e6, response.headers
|
|
|
|
+ )
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ return response
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun print(vararg args: CharSequence?) {
|
|
|
|
+ println(java.lang.String.join(",", *args))
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+fun bodyToString(request: Request): String {
|
|
|
|
+ try {
|
|
|
|
+ val copy = request.newBuilder().build()
|
|
|
|
+ val buffer = Buffer()
|
|
|
|
+ copy.body?.writeTo(buffer)
|
|
|
|
+ return buffer.readUtf8()
|
|
|
|
+ } catch (e: IOException) {
|
|
|
|
+ return ""
|
|
|
|
+ }
|
|
|
|
+}
|