forked from github/szkolny
[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"
|
||||
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 {
|
||||
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_VERSION = "19.4.1.436"
|
||||
const val VULCAN_API_PASSWORD = "CE75EA598C7743AD9B0B7328DED85B06"
|
||||
const val VULCAN_API_PASSWORD_FAKELOG = "012345678901234567890123456789AB"
|
||||
val VULCAN_API_DEVICE_NAME = "Szkolny.eu ${Build.MODEL}"
|
||||
|
||||
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()
|
||||
&& apiCertificateKey.isNotNullNorEmpty()
|
||||
&& apiCertificatePfx.isNotNullNorEmpty()
|
||||
&& apiCertificatePrivate.isNotNullNorEmpty()
|
||||
&& symbol.isNotNullNorEmpty()
|
||||
|
||||
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 }
|
||||
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
|
||||
var apiCertificateExpiryTime: Int
|
||||
get() { mApiCertificateExpiryTime = mApiCertificateExpiryTime ?: loginStore.getLoginData("certificateExpiryTime", 0); return mApiCertificateExpiryTime ?: 0 }
|
||||
|
@ -4,12 +4,10 @@
|
||||
|
||||
package pl.szczodrzynski.edziennik.api.v2.vulcan.data
|
||||
|
||||
import android.util.Base64
|
||||
import com.google.gson.JsonObject
|
||||
import im.wangchao.mhttp.Request
|
||||
import im.wangchao.mhttp.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import io.github.wulkanowy.signer.signContent
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
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.d
|
||||
import pl.szczodrzynski.edziennik.utils.models.Date
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.net.HttpURLConnection
|
||||
import java.util.*
|
||||
|
||||
@ -114,9 +111,12 @@ open class VulcanApi(open val data: DataVulcan) {
|
||||
.userAgent(VULCAN_API_USER_AGENT)
|
||||
.addHeader("RequestCertificateKey", data.apiCertificateKey)
|
||||
.addHeader("RequestSignatureValue",
|
||||
Utils.VulcanRequestEncryptionUtils.signContent(
|
||||
finalPayload.toString().toByteArray(),
|
||||
ByteArrayInputStream(Base64.decode(data.apiCertificatePfx, Base64.DEFAULT))))
|
||||
try {
|
||||
signContent(
|
||||
data.apiCertificatePrivate ?: "",
|
||||
finalPayload.toString()
|
||||
)
|
||||
} catch (e: Exception) {e.printStackTrace();""})
|
||||
.apply {
|
||||
when (method) {
|
||||
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.Response
|
||||
import im.wangchao.mhttp.callback.JsonCallbackHandler
|
||||
import pl.szczodrzynski.edziennik.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.*
|
||||
import pl.szczodrzynski.edziennik.api.v2.models.ApiError
|
||||
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 java.net.HttpURLConnection.HTTP_BAD_REQUEST
|
||||
import java.util.*
|
||||
@ -28,6 +32,18 @@ class VulcanLoginApi(val data: DataVulcan, val onSuccess: () -> Unit) {
|
||||
onSuccess()
|
||||
}
|
||||
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()) {
|
||||
loginWithToken()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user