DEV Community

vmodal_ai
vmodal_ai

Posted on Originally published at v-modal.com

Android Robot SDK: Secure Pairing and Mutual Authentication for Robots

Android Robot SDK: Secure Pairing and Mutual Authentication for Robots

In safety-critical mobile robotics, unauthenticated command injection poses catastrophic risks. Ensuring that an Android device controls a designated robot requires cryptographic pairing, Mutual TLS (mTLS) with X.509 certificates, and Hardware-backed KeyStore protection on Android devices.

This tutorial guides you through engineering a zero-trust pairing and mTLS authentication module inside an Android Robot SDK.


1. Security Architecture Diagram

+-------------------------------------------------------------+
|                     Android KeyStore                        |
|        [EC Private Key (SecP256r1) in Hardware HSM]         |
+-------------------------------------------------------------+
                               |
                   Client Cert | Handshake (mTLS 1.3)
                               v
+-------------------------------------------------------------+
|                     Robot Embedded Core                     |
|            [X.509 Server Certificate Verification]           |
+-------------------------------------------------------------+
Enter fullscreen mode Exit fullscreen mode

2. Generating Device Keypair in Android KeyStore

Use Android's hardware-backed KeyStore to store private keys that cannot be extracted by root software or memory inspection.

package com.vmodal.sdk.security

import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.KeyStore

class RobotKeyStoreManager {

    companion object {
        private const val KEY_ALIAS = "vmodal_robot_client_cert_key"
        private const val ANDROID_KEYSTORE = "AndroidKeyStore"
    }

    fun generateHardwareKeyPair(): KeyPair {
        val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }

        if (keyStore.containsAlias(KEY_ALIAS)) {
            val privateKey = keyStore.getKey(KEY_ALIAS, null) as java.security.PrivateKey
            val publicKey = keyStore.getCertificate(KEY_ALIAS).publicKey
            return KeyPair(publicKey, privateKey)
        }

        val keyPairGenerator = KeyPairGenerator.getInstance(
            KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE
        )

        val spec = KeyGenParameterSpec.Builder(
            KEY_ALIAS,
            KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
        )
            .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
            .setAlgorithmParameterSpec(java.security.spec.ECGenParameterSpec("secp256r1"))
            .setUserAuthenticationRequired(false)
            .build()

        keyPairGenerator.initialize(spec)
        return keyPairGenerator.generateKeyPair()
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Creating Custom SSLSocketFactory for mTLS

To execute mutual TLS authentication, build an SSLSocketFactory backed by your client certificate and trusted Robot Root Certificate Authority (CA).

package com.vmodal.sdk.security.net

import java.security.KeyStore
import java.security.cert.X509Certificate
import javax.net.ssl.KeyManagerFactory
import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManagerFactory
import javax.net.ssl.SSLSocketFactory

class MtlsSecurityContextBuilder {

    fun buildMtlsSocketFactory(
        clientCertificate: X509Certificate,
        clientPrivateKey: java.security.PrivateKey,
        robotCaCertificate: X509Certificate
    ): SSLSocketFactory {

        // KeyStore containing Android Client identity
        val clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
            load(null, null)
            setKeyEntry("client_id", clientPrivateKey, null, arrayOf(clientCertificate))
        }

        val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()).apply {
            init(clientKeyStore, null)
        }

        // KeyStore containing Trusted Robot Root CA
        val trustKeyStore = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
            load(null, null)
            setCertificateEntry("robot_root_ca", robotCaCertificate)
        }

        val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()).apply {
            init(trustKeyStore)
        }

        val sslContext = SSLContext.getInstance("TLSv1.3").apply {
            init(kmf.keyManagers, tmf.trustManagers, null)
        }

        return sslContext.socketFactory
    }
}
Enter fullscreen mode Exit fullscreen mode

4. Implementing Handshake Pairing Protocol

Before full mTLS credentials are exchanged, an out-of-band PIN verification phase ensures the operator is physically near the machine.

package com.vmodal.sdk.security.pairing

import java.security.MessageDigest
import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

class PinChallengePairing {

    /**
     * Compute HMAC-SHA256 challenge response using pairing PIN shown on Robot's hardware screen.
     */
    fun computeChallengeResponse(pin: String, challengeNonce: ByteArray): ByteArray {
        val pinBytes = pin.toByteArray(Charsets.UTF_8)
        val mac = Mac.getInstance("HmacSHA256")
        val secretKey = SecretKeySpec(pinBytes, "HmacSHA256")
        mac.init(secretKey)
        return mac.doFinal(challengeNonce)
    }

    fun verifyNonceResponse(
        pin: String,
        challengeNonce: ByteArray,
        receivedDigest: ByteArray
    ): Boolean {
        val expected = computeChallengeResponse(pin, challengeNonce)
        return MessageDigest.isEqual(expected, receivedDigest)
    }
}
Enter fullscreen mode Exit fullscreen mode

5. Production Handshake Integration Example

import com.vmodal.sdk.security.RobotKeyStoreManager
import com.vmodal.sdk.security.pairing.PinChallengePairing
import java.security.SecureRandom

fun main() {
    println("Step 1: Initializing Hardware KeyStore KeyPair...")
    val keyStoreManager = RobotKeyStoreManager()
    val keyPair = keyStoreManager.generateHardwareKeyPair()
    println("Generated Key Public Algorithm: ${keyPair.public.algorithm}")

    println("Step 2: Performing PIN Challenge Verification...")
    val pairing = PinChallengePairing()
    val nonce = ByteArray(32).also { SecureRandom().nextBytes(it) }
    val displayPin = "849204" // Displayed on Robot OLED display

    val clientResponse = pairing.computeChallengeResponse(displayPin, nonce)

    val isValid = pairing.verifyNonceResponse(displayPin, nonce, clientResponse)
    if (isValid) {
        println("Pairing SUCCESSFUL! Secure channel established over mTLS 1.3.")
    } else {
        println("Pairing FAILED! Unauthorized client pairing attempt.")
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

Enforcing Mutual TLS (mTLS) backed by hardware keys in the Android KeyStore creates an end-to-end zero-trust baseline for robot command and control applications.


Useful Links

Top comments (0)