mirror of
https://github.com/szkolny-eu/szkolny-android.git
synced 2025-02-21 14:04:45 +01:00
[APIv2/Vulcan] Add faster request signing.
This commit is contained in:
parent
3540b09623
commit
b8f58328cb
@ -156,7 +156,8 @@ dependencies {
|
|||||||
debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
|
debugImplementation "com.github.ChuckerTeam.Chucker:library:3.0.1"
|
||||||
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1"
|
releaseImplementation "com.github.ChuckerTeam.Chucker:library-no-op:3.0.1"
|
||||||
|
|
||||||
implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT'
|
//implementation 'com.github.wulkanowy:uonet-request-signer:master-SNAPSHOT'
|
||||||
|
//implementation 'com.github.kuba2k2.uonet-request-signer:android:master-63f094b14a-1'
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -52,6 +52,7 @@ const val VULCAN_API_USER_AGENT = "MobileUserAgent"
|
|||||||
const val VULCAN_API_APP_NAME = "VULCAN-Android-ModulUcznia"
|
const val VULCAN_API_APP_NAME = "VULCAN-Android-ModulUcznia"
|
||||||
const val VULCAN_API_APP_VERSION = "19.4.1.436"
|
const val VULCAN_API_APP_VERSION = "19.4.1.436"
|
||||||
const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06"
|
const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06"
|
||||||
|
const val VULCAN_API_PASSWORD_FAKELOG = "012345678901234567890123456789AB"
|
||||||
val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}"
|
val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}"
|
||||||
|
|
||||||
const val VULCAN_API_ENDPOINT_CERTIFICATE = "mobile-api/Uczen.v3.UczenStart/Certyfikat"
|
const val VULCAN_API_ENDPOINT_CERTIFICATE = "mobile-api/Uczen.v3.UczenStart/Certyfikat"
|
||||||
|
@ -16,7 +16,7 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
|||||||
|
|
||||||
fun isApiLoginValid() = apiCertificateExpiryTime-30 > currentTimeUnix()
|
fun isApiLoginValid() = apiCertificateExpiryTime-30 > currentTimeUnix()
|
||||||
&& apiCertificateKey.isNotNullNorEmpty()
|
&& apiCertificateKey.isNotNullNorEmpty()
|
||||||
&& apiCertificatePfx.isNotNullNorEmpty()
|
&& apiCertificatePrivate.isNotNullNorEmpty()
|
||||||
&& symbol.isNotNullNorEmpty()
|
&& symbol.isNotNullNorEmpty()
|
||||||
|
|
||||||
override fun satisfyLoginMethods() {
|
override fun satisfyLoginMethods() {
|
||||||
@ -145,6 +145,11 @@ class DataVulcan(app: App, profile: Profile?, loginStore: LoginStore) : Data(app
|
|||||||
get() { mApiCertificatePfx = mApiCertificatePfx ?: loginStore.getLoginData("certificatePfx", null); return mApiCertificatePfx }
|
get() { mApiCertificatePfx = mApiCertificatePfx ?: loginStore.getLoginData("certificatePfx", null); return mApiCertificatePfx }
|
||||||
set(value) { loginStore.putLoginData("certificatePfx", value); mApiCertificatePfx = value }
|
set(value) { loginStore.putLoginData("certificatePfx", value); mApiCertificatePfx = value }
|
||||||
|
|
||||||
|
private var mApiCertificatePrivate: String? = null
|
||||||
|
var apiCertificatePrivate: String?
|
||||||
|
get() { mApiCertificatePrivate = mApiCertificatePrivate ?: loginStore.getLoginData("certificatePrivate", null); return mApiCertificatePrivate }
|
||||||
|
set(value) { loginStore.putLoginData("certificatePrivate", value); mApiCertificatePrivate = value }
|
||||||
|
|
||||||
private var mApiCertificateExpiryTime: Int? = null
|
private var mApiCertificateExpiryTime: Int? = null
|
||||||
var apiCertificateExpiryTime: Int
|
var apiCertificateExpiryTime: Int
|
||||||
get() { mApiCertificateExpiryTime = mApiCertificateExpiryTime ?: loginStore.getLoginData("certificateExpiryTime", 0); return mApiCertificateExpiryTime ?: 0 }
|
get() { mApiCertificateExpiryTime = mApiCertificateExpiryTime ?: loginStore.getLoginData("certificateExpiryTime", 0); return mApiCertificateExpiryTime ?: 0 }
|
||||||
|
@ -4,12 +4,10 @@
|
|||||||
|
|
||||||
package pl.szczodrzynski.edziennik.api.v2.vulcan.data
|
package pl.szczodrzynski.edziennik.api.v2.vulcan.data
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import im.wangchao.mhttp.Request
|
import im.wangchao.mhttp.Request
|
||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||||
import io.github.wulkanowy.signer.signContent
|
|
||||||
import pl.szczodrzynski.edziennik.api.v2.*
|
import pl.szczodrzynski.edziennik.api.v2.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||||
@ -17,7 +15,6 @@ import pl.szczodrzynski.edziennik.data.db.modules.teams.Team
|
|||||||
import pl.szczodrzynski.edziennik.utils.Utils
|
import pl.szczodrzynski.edziennik.utils.Utils
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||||
import java.io.ByteArrayInputStream
|
|
||||||
import java.net.HttpURLConnection
|
import java.net.HttpURLConnection
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
@ -114,9 +111,12 @@ open class VulcanApi(open val data: DataVulcan) {
|
|||||||
.userAgent(VULCAN_API_USER_AGENT)
|
.userAgent(VULCAN_API_USER_AGENT)
|
||||||
.addHeader("RequestCertificateKey", data.apiCertificateKey)
|
.addHeader("RequestCertificateKey", data.apiCertificateKey)
|
||||||
.addHeader("RequestSignatureValue",
|
.addHeader("RequestSignatureValue",
|
||||||
Utils.VulcanRequestEncryptionUtils.signContent(
|
try {
|
||||||
finalPayload.toString().toByteArray(),
|
signContent(
|
||||||
ByteArrayInputStream(Base64.decode(data.apiCertificatePfx, Base64.DEFAULT))))
|
data.apiCertificatePrivate ?: "",
|
||||||
|
finalPayload.toString()
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {e.printStackTrace();""})
|
||||||
.apply {
|
.apply {
|
||||||
when (method) {
|
when (method) {
|
||||||
GET -> get()
|
GET -> get()
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Wulkanowy kiedyś tam.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package pl.szczodrzynski.edziennik.api.v2.vulcan.data
|
||||||
|
|
||||||
|
import android.util.Base64
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.security.KeyFactory
|
||||||
|
import java.security.KeyStore
|
||||||
|
import java.security.PrivateKey
|
||||||
|
import java.security.Signature
|
||||||
|
import java.security.spec.PKCS8EncodedKeySpec
|
||||||
|
|
||||||
|
fun signContent(password: String, certificate: String?, content: String): String {
|
||||||
|
val keystore = KeyStore.getInstance("pkcs12").apply {
|
||||||
|
load(ByteArrayInputStream(Base64.decode(certificate, Base64.DEFAULT)), password.toCharArray())
|
||||||
|
}
|
||||||
|
val signature = Signature.getInstance("SHA1WithRSA").apply {
|
||||||
|
initSign(keystore.getKey("LoginCert", password.toCharArray()) as PrivateKey)
|
||||||
|
update(content.toByteArray())
|
||||||
|
}
|
||||||
|
return Base64.encodeToString(signature.sign(), Base64.NO_WRAP)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun signContent(privateKey: String, content: String): String {
|
||||||
|
val key = PKCS8EncodedKeySpec(Base64.decode(privateKey, Base64.DEFAULT)).let {
|
||||||
|
KeyFactory.getInstance("RSA").generatePrivate(it)
|
||||||
|
}
|
||||||
|
val signature = Signature.getInstance("SHA1WithRSA").apply {
|
||||||
|
initSign(key)
|
||||||
|
update(content.toByteArray())
|
||||||
|
}
|
||||||
|
return Base64.encodeToString(signature.sign(), Base64.NO_WRAP)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPrivateKeyFromCert(password: String, certificate: String): String {
|
||||||
|
val keystore = KeyStore.getInstance("pkcs12").apply {
|
||||||
|
load(ByteArrayInputStream(Base64.decode(certificate, Base64.DEFAULT)), password.toCharArray())
|
||||||
|
}
|
||||||
|
val keyFactory = KeyFactory.getInstance("RSA")
|
||||||
|
val keySpec = keyFactory.getKeySpec(
|
||||||
|
keystore.getKey("LoginCert", password.toCharArray()) as PrivateKey,
|
||||||
|
PKCS8EncodedKeySpec::class.java
|
||||||
|
)
|
||||||
|
return Base64.encodeToString(keySpec.encoded, Base64.NO_WRAP)
|
||||||
|
}
|
@ -9,10 +9,14 @@ import com.google.gson.JsonObject
|
|||||||
import im.wangchao.mhttp.Request
|
import im.wangchao.mhttp.Request
|
||||||
import im.wangchao.mhttp.Response
|
import im.wangchao.mhttp.Response
|
||||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||||
import pl.szczodrzynski.edziennik.*
|
|
||||||
import pl.szczodrzynski.edziennik.api.v2.*
|
import pl.szczodrzynski.edziennik.api.v2.*
|
||||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||||
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
import pl.szczodrzynski.edziennik.api.v2.vulcan.DataVulcan
|
||||||
|
import pl.szczodrzynski.edziennik.api.v2.vulcan.data.getPrivateKeyFromCert
|
||||||
|
import pl.szczodrzynski.edziennik.currentTimeUnix
|
||||||
|
import pl.szczodrzynski.edziennik.getJsonObject
|
||||||
|
import pl.szczodrzynski.edziennik.getString
|
||||||
|
import pl.szczodrzynski.edziennik.isNotNullNorEmpty
|
||||||
import pl.szczodrzynski.edziennik.utils.Utils.d
|
import pl.szczodrzynski.edziennik.utils.Utils.d
|
||||||
import java.net.HttpURLConnection.HTTP_BAD_REQUEST
|
import java.net.HttpURLConnection.HTTP_BAD_REQUEST
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -28,6 +32,18 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
|
|||||||
onSuccess()
|
onSuccess()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (data.apiCertificatePfx.isNotNullNorEmpty()) {
|
||||||
|
try {
|
||||||
|
data.apiCertificatePrivate = getPrivateKeyFromCert(
|
||||||
|
if (data.apiToken?.get(0) == 'F') VULCAN_API_PASSWORD_FAKELOG else VULCAN_API_PASSWORD,
|
||||||
|
data.apiCertificatePfx ?: ""
|
||||||
|
)
|
||||||
|
onSuccess()
|
||||||
|
return@run
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {
|
if (data.symbol.isNotNullNorEmpty() && data.apiToken.isNotNullNorEmpty() && data.apiPin.isNotNullNorEmpty()) {
|
||||||
loginWithToken()
|
loginWithToken()
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user