add certificate pinning support for Android
This commit is contained in:
parent
8baac45187
commit
b0cb2b8933
@ -4,9 +4,6 @@
|
|||||||
>
|
>
|
||||||
> This repository contains a proof-of-concept React Native plugin and an example mobile application intended solely for technical evaluation.
|
> This repository contains a proof-of-concept React Native plugin and an example mobile application intended solely for technical evaluation.
|
||||||
>
|
>
|
||||||
> Only the iOS integration has been verified to work at this time.
|
|
||||||
> The Android bridge and Android example setup exist but have not been tested and are expected to require additional adjustments before they can be considered functional.
|
|
||||||
>
|
|
||||||
> This repository is **experimental**, **not formally approved**, **not officially released**, **not published as an npm package**, and **not endorsed by WebID for
|
> This repository is **experimental**, **not formally approved**, **not officially released**, **not published as an npm package**, and **not endorsed by WebID for
|
||||||
> production use**.
|
> production use**.
|
||||||
>
|
>
|
||||||
@ -38,12 +35,14 @@ cp .env.example .env
|
|||||||
URL=https://test.webid-solutions.de
|
URL=https://test.webid-solutions.de
|
||||||
USERNAME=your_username
|
USERNAME=your_username
|
||||||
API_KEY=your_api_key
|
API_KEY=your_api_key
|
||||||
CERT_BASE64=On ANdroid it need to be SHA-PINS
|
CERT_BASE64=
|
||||||
# SHA_PINS=sha256/AAAA...,sha256/BBBB...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit the .env file and add your username and API key.
|
Edit the .env file and add your username and API key.
|
||||||
|
|
||||||
|
ℹ️ The provided CERT_BASE64 value is preconfigured for the test system and is valid until 2026-02-21.
|
||||||
|
In most cases, this value does not need to be modified if you intend to test against the test environment.
|
||||||
|
|
||||||
### Install Dependencies
|
### Install Dependencies
|
||||||
|
|
||||||
From the repository root:
|
From the repository root:
|
||||||
|
|||||||
@ -24,6 +24,11 @@ import java.net.URI
|
|||||||
import com.facebook.react.bridge.UiThreadUtil
|
import com.facebook.react.bridge.UiThreadUtil
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import java.security.cert.X509Certificate
|
||||||
|
import android.util.Base64
|
||||||
|
|
||||||
private const val TAG = "WebIdMetaPlugin"
|
private const val TAG = "WebIdMetaPlugin"
|
||||||
|
|
||||||
@ -53,6 +58,53 @@ class WebIdMetaPluginModule(
|
|||||||
listenerCount = (listenerCount - count).coerceAtLeast(0)
|
listenerCount = (listenerCount - count).coerceAtLeast(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun toPinningShaKey(inputRaw: String): String {
|
||||||
|
val input = inputRaw.trim()
|
||||||
|
|
||||||
|
if (input.startsWith("sha256/") || input.startsWith("sha1/")) return input
|
||||||
|
|
||||||
|
val justValue = input.substringAfter("=", input).trim()
|
||||||
|
|
||||||
|
val base64Cert = justValue
|
||||||
|
.replace("-----BEGIN CERTIFICATE-----", "")
|
||||||
|
.replace("-----END CERTIFICATE-----", "")
|
||||||
|
.replace("\\s+".toRegex(), "")
|
||||||
|
|
||||||
|
require(base64Cert.isNotEmpty()) { "Certificate input is empty after normalization" }
|
||||||
|
|
||||||
|
val certBytes = try {
|
||||||
|
Base64.decode(base64Cert, Base64.DEFAULT)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
throw IllegalArgumentException("Certificate is not valid Base64 (DER/PEM).", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val x509 = try {
|
||||||
|
val cf = CertificateFactory.getInstance("X.509")
|
||||||
|
cf.generateCertificate(ByteArrayInputStream(certBytes)) as X509Certificate
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw IllegalArgumentException("Could not parse X.509 certificate bytes.", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val spkiBytes = x509.publicKey.encoded
|
||||||
|
val digest = MessageDigest.getInstance("SHA-256").digest(spkiBytes)
|
||||||
|
val pinB64 = Base64.encodeToString(digest, Base64.NO_WRAP)
|
||||||
|
|
||||||
|
return "sha256/$pinB64"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun toPinningShaKeys(inputs: List<String>): Array<String> =
|
||||||
|
inputs
|
||||||
|
.map { it.trim() }
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
.map { toPinningShaKey(it) }
|
||||||
|
.toTypedArray()
|
||||||
|
|
||||||
|
private fun shortPreview(s: String): String {
|
||||||
|
val t = s.trim()
|
||||||
|
if (t.length <= 12) return t
|
||||||
|
return "${t.take(6)}...${t.takeLast(6)}"
|
||||||
|
}
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
fun createMetaPlugin(
|
fun createMetaPlugin(
|
||||||
uri: String,
|
uri: String,
|
||||||
@ -69,10 +121,13 @@ class WebIdMetaPluginModule(
|
|||||||
val shaPinsList = shaPins.toArrayList().map { it.toString() }
|
val shaPinsList = shaPins.toArrayList().map { it.toString() }
|
||||||
val pluginsList = plugins.toArrayList().map { it.toString() }
|
val pluginsList = plugins.toArrayList().map { it.toString() }
|
||||||
|
|
||||||
|
val pinningShaKeys = toPinningShaKeys(shaPinsList)
|
||||||
|
Log.i(TAG, "createMetaPlugin pins(mapped) len=${pinningShaKeys.size} values=${pinningShaKeys.map { shortPreview(it) }}")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val environment = WebIdSdkEnvironment(
|
val environment = WebIdSdkEnvironment(
|
||||||
URI.create(cleanUri),
|
URI.create(cleanUri),
|
||||||
*shaPinsList.toTypedArray()
|
*pinningShaKeys
|
||||||
)
|
)
|
||||||
|
|
||||||
val selectedPlugins = ArrayList<IProductPluginWebId>()
|
val selectedPlugins = ArrayList<IProductPluginWebId>()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user