{"id":3406,"date":"2026-08-22T14:09:24","date_gmt":"2026-08-22T12:09:24","guid":{"rendered":"https:\/\/darioiannascoli.it\/blog\/android-17-hardware-backed-key-attestation-zero-knowledge-proofs\/"},"modified":"2026-08-22T14:09:24","modified_gmt":"2026-08-22T12:09:24","slug":"android-17-hardware-backed-key-attestation-zero-knowledge-proofs","status":"publish","type":"post","link":"https:\/\/darioiannascoli.it\/blog\/android-17-hardware-backed-key-attestation-zero-knowledge-proofs\/","title":{"rendered":"Come Implementare Android 17 Hardware-Backed Key Attestation e Zero-Knowledge Proofs: La Mia Procedura Secure Enclave Integrity Verification, App-Level Attestation e Emulator Abuse Prevention"},"content":{"rendered":"<p>Nel mio lavoro quotidiano con applicazioni Android enterprise \u2013 dal banking al fintech \u2013 mi trovo costantemente a fronteggiare una sfida fondamentale: <strong>come verificare che il dispositivo esecuzione del mio codice sia autentico, non compromesso e soprattutto non un emulatore?<\/strong> Android 17 introduce un cambio di paradigma significativo con i <em>Hardware-Backed Key Attestation<\/em> e i <em>Zero-Knowledge Proofs<\/em>, tecnologie che trasformano completamente il modo in cui pensiamo alla verifica dell&#8217;integrit\u00e0 dei dispositivi.<\/p>\n<p>In questo articolo, voglio condividere con voi la procedura che ho implementato in produzione per sfruttare appieno queste capacit\u00e0, proteggendo le applicazioni da tampering, clonazione e abuso via emulatore. Non si tratta di teoria pura: buona parte della mia esperienza viene da casi reali dove un&#8217;implementazione sbagliata di attestation ha permesso a fraudster di bypassare layer di sicurezza costati tempo e risorse.<\/p>\n<h2>Perch\u00e9 Hardware-Backed Key Attestation \u00e8 Critico in Android 17<\/h2>\n<p>Quando dico &#8220;verificare il dispositivo&#8221;, non intendo semplicemente controllare se l&#8217;app \u00e8 autentica. <cite>Un telefono jailbroken pu\u00f2 bypassare SSL pinning, un APK modificato pu\u00f2 falsificare risposte, un emulatore pu\u00f2 mimicizzare il traffico legittimo perfettamente.<\/cite> Nel mio flusso di banking app, avere solo l&#8217;autenticazione dell&#8217;utente non basta.<\/p>\n<p><cite>I moderni dispositivi Android includono un componente hardware sicuro (TEE o StrongBox), una specie di vault dove le chiavi risiedono.<\/cite> La novit\u00e0 di Android 17 \u00e8 che <cite>Google inizia la transizione dell&#8217;attestazione remota verso un&#8217;architettura completamente conforme a PQC (Post-Quantum Cryptography), e il groundwork viene gi\u00e0 stabilito in Android 17 beta, dove Verified Boot, Remote Attestation, Android Keystore e firma di Google Play vengono tutti adattati per la crittografia post-quantistica.<\/cite><\/p>\n<p>Questo significa che le catene di certificati dietro l&#8217;attestazione stanno evolvendo. <cite>A partire dal 10 aprile 2026, i dispositivi abilitati a RKP (Remote Key Provisioning) usano esclusivamente il nuovo certificato root, e tutte le catene di attestazione da questi dispositivi sono radicate nella nuova chiave ECDSA P-384.<\/cite> Se la vostra soluzione di attestazione non \u00e8 aggiornata, state gi\u00e0 rigettando dispositivi RKP moderni.<\/p>\n<h2>Anatomia dell&#8217;Attestazione Hardware: Come Funziona in Pratica<\/h2>\n<p>Ho scoperto che molti sviluppatori implementano attestation senza capire veramente il flusso sottostante. Ecco come funziona nella pratica:<\/p>\n<ol>\n<li><strong>Challenge Generation:<\/strong> Il mio backend genera un nonce casuale e lo invia all&#8217;app.<\/li>\n<li><strong>Key Generation in Hardware:<\/strong> <cite>Android genera la chiave dentro il modulo hardware sicuro, dove la chiave privata non lascia mai il dispositivo.<\/cite><\/li>\n<li><strong>Certificate Chain Building:<\/strong> <cite>Android costruisce una catena di certificati che include metadati (boot-state, security level, ecc.), e la radice della catena \u00e8 emessa da un&#8217;autorit\u00e0 fidata (per i dispositivi production, il Google hardware attestation root).<\/cite><\/li>\n<li><strong>Backend Validation:<\/strong> <cite>L&#8217;app invia la catena completa (chiave pubblica + catena di attestazione + metadati) al backend, che valida la fiducia crittograficamente verificando la catena fino alla root CA conosciuta, e verifica anche i metadati per assicurare l&#8217;integrit\u00e0 del dispositivo.<\/cite><\/li>\n<\/ol>\n<p>Nel mio backend di produzione, ho strutturato il codice di validazione cos\u00ec:<\/p>\n<pre><code>\/\/ Pseudocodice per validazione backend\nfunction validateAttestationChain(attestationChainPEM, nonce) {\n  \/\/ 1. Parse catena di certificati\n  const chain = parseCertificateChain(attestationChainPEM);\n  \n  \/\/ 2. Verificare radice di fiducia (aggiornato per Android 17 RKP)\n  const trustedRoots = [\n    GOOGLE_ATTESTATION_ROOT_OLD,  \/\/ Factory-keyed devices\n    GOOGLE_ATTESTATION_ROOT_NEW   \/\/ RKP-enabled devices (P-384)\n  ];\n  \n  if (!verifyChainToRoot(chain, trustedRoots)) {\n    throw new Error('Untrusted attestation root');\n  }\n  \n  \/\/ 3. Estrai metadati dal certificato leaf\n  const attestationExt = chain[0].extensions[OID_1_3_6_1_4_1_11129_2_1_17];\n  const metadata = parseAttestationExtension(attestationExt);\n  \n  \/\/ 4. Verifica stato del boot\n  if (metadata.bootState !== 'VERIFIED') {\n    return { verdict: 'REJECT', reason: 'Boot tampering detected' };\n  }\n  \n  \/\/ 5. Verifica security level\n  if (metadata.securityLevel !== 'STRONG_BOX' &amp;&amp; \n      metadata.securityLevel !== 'TRUSTED_ENVIRONMENT') {\n    return { verdict: 'RESTRICT', reason: 'Software-backed key detected' };\n  }\n  \n  \/\/ 6. Challenge-Response (proof of possession)\n  if (!metadata.attestationChallenge.equals(nonce)) {\n    throw new Error('Nonce mismatch - replay attack suspected');\n  }\n  \n  return { verdict: 'ALLOW', metadata: metadata };\n}\n<\/code><\/pre>\n<h2>Zero-Knowledge Proofs: Privacy-Preserving Attestation<\/h2>\n<p>Qui arriviamo al tema affascinante. <cite>Come alternativa, alcuni sistemi usano una zero-knowledge proof della crittografia di attestazione per provare la validit\u00e0 dell&#8217;attestazione senza rivelare la chiave pubblica che potrebbe essere usata per verificarla. La ZK proof dimostra che la firma del messaggio \u00e8 stata generata da qualcuno con una chiave privata per una chiave pubblica su una lista senza rivelare quale voce della lista (quale chiave pubblica) \u00e8 stata usata.<\/cite><\/p>\n<p>Perch\u00e9 questo importa? Immaginate un servizio di verifica dell&#8217;et\u00e0 o un&#8217;app di banking che non vuole leakare l&#8217;identit\u00e0 hardware del dispositivo. Con ZK proofs, provate l&#8217;integrit\u00e0 del dispositivo senza esporre dati sensibili che potrebbero essere correlati a profili utente.<\/p>\n<p><cite>La risposta strutturale \u00e8 quella di adottare zero-knowledge age proofs e schemi di verifica crittografica preservanti la privacy che EFF e altri hanno promosso, che non dipendono dal sistema operativo del vostro telefono.<\/cite><\/p>\n<p>Nel mio ambiente di testing, ho iniziato a sperimentare con implementazioni di ZK attestation cos\u00ec:<\/p>\n<pre><code>\/\/ Zero-Knowledge Proof Attestation (pseudocodice)\nclass ZKAttestationProver {\n  constructor(hardwareBackedKey) {\n    this.key = hardwareBackedKey;\n  }\n  \n  \/\/ Genera una ZK proof che prova il possesso di una chiave attestata\n  \/\/ senza rivelare quale chiave pubblica \u00e8 stata usata\n  async generateZKProof(accumulator, serverChallenge) {\n    \/\/ 1. Sign il challenge con la chiave attestata nel TEE\n    const signature = await this.key.sign(serverChallenge);\n    \n    \/\/ 2. Crea un commitment alla chiave pubblica (non la rivela)\n    const publicKey = this.key.getPublic();\n    const commitment = hash(publicKey);\n    \n    \/\/ 3. Generar ZK proof che:\n    \/\/    - La firma \u00e8 valida per il challenge\n    \/\/    - Il commitment \u00e8 per una chiave nell'accumulator\n    \/\/    - Senza rivelare quale chiave\n    const zkProof = generateZKProof({\n      signature: signature,\n      commitment: commitment,\n      accumulator: accumulator,\n      challenge: serverChallenge\n    });\n    \n    return {\n      zkProof: zkProof,\n      commitment: commitment,\n      signature: signature\n    };\n  }\n}\n\n\/\/ Verifica backend\nfunction verifyZKProof(zkProof, accumulator) {\n  \/\/ Il backend verifica che:\n  \/\/ 1. La firma \u00e8 valida\n  \/\/ 2. Il commitment \u00e8 per una chiave nell'accumulator (senza sapere quale)\n  \/\/ Non ha mai visto la chiave pubblica effettiva\n  return verifyZKProofMath(zkProof, accumulator);\n}\n<\/code><\/pre>\n<h2>Secure Enclave Integrity Verification: Controlli Avanzati<\/h2>\n<p>Oltre a verificare se il certificato \u00e8 valido, ho sviluppato una serie di controlli che vanificano i tentativi comuni di bypass. Il post di Quarkslab di agosto 2026 ha rilevato un&#8217;interessante tecnica di relay che vale la pena di indirizzare:<\/p>\n<p><cite>La soluzione \u00e8 il proof of possession: dopo un&#8217;attestazione valida, il backend richiede al client di firmare un challenge fresco con la chiave privata attestata dentro la stessa sessione, e verifica la firma. Questo \u00e8 il pattern attestation-then-assertion usato dai hardware-backed authenticator.<\/cite><\/p>\n<p>Nel mio backend, ho implementato questo cos\u00ec:<\/p>\n<pre><code>\/\/ Attestation + Assertion Pattern (due fasi)\nasync function secureAttestationFlow(appClient, userId) {\n  \/\/ FASE 1: Attestation\n  \/\/ Il client genera una chiave nel TEE e la attesta\n  const attestationChallenge = crypto.randomBytes(32);\n  const attestationRequest = {\n    userId: userId,\n    challenge: attestationChallenge,\n    timestamp: Date.now()\n  };\n  \n  \/\/ Client genera chiave e attestazione (accade nel TEE)\n  const attestationResponse = await appClient.attestKey(\n    attestationRequest,\n    { securityLevel: 'STRONG_BOX' }\n  );\n  \n  \/\/ Backend valida l'attestazione\n  if (!validateAttestationChain(attestationResponse.chain, \n                                 attestationChallenge)) {\n    throw new Error('Attestation validation failed');\n  }\n  \n  \/\/ Estrai la chiave pubblica attestata\n  const attestedPublicKey = extractPublicKeyFromChain(\n    attestationResponse.chain\n  );\n  \n  \/\/ Salva il reference alle chiave (non la chiave stessa!)\n  const keyRecord = {\n    userId: userId,\n    publicKeyFingerprint: sha256(attestedPublicKey),\n    attestationTime: Date.now(),\n    bootState: attestationResponse.metadata.bootState,\n    securityLevel: attestationResponse.metadata.securityLevel\n  };\n  db.saveKeyRecord(keyRecord);\n  \n  \/\/ FASE 2: Assertion (prove of possession)\n  \/\/ Per ogni operazione sensibile, richiedi al client di firmare un challenge\n  async function requireAssertion(operation) {\n    const assertionChallenge = crypto.randomBytes(32);\n    const assertionRequest = {\n      operation: operation,\n      challenge: assertionChallenge,\n      timestamp: Date.now()\n    };\n    \n    \/\/ Client firma il challenge con la chiave nel TEE\n    const assertionResponse = await appClient.signWithAttestedKey(\n      assertionRequest\n    );\n    \n    \/\/ Backend verifica che la firma sia valida\n    const isValid = verifySignature(\n      assertionResponse.signature,\n      assertionChallenge,\n      attestedPublicKey\n    );\n    \n    if (!isValid) {\n      return { verdict: 'REJECT', reason: 'Signature verification failed' };\n    }\n    \n    \/\/ Contro il relay attack: chiave che la richiesta sia fresca\n    const timeDiff = Math.abs(Date.now() - assertionRequest.timestamp);\n    if (timeDiff &gt; 5000) { \/\/ 5 secondi di tolleranza\n      return { verdict: 'REJECT', reason: 'Stale assertion' };\n    }\n    \n    return { verdict: 'ALLOW' };\n  }\n  \n  return { keyRecord, requireAssertion };\n}\n<\/code><\/pre>\n<h2>Prevenzione dell&#8217;Abuso di Emulatori: Detection e Response<\/h2>\n<p>Uno dei miei use case pi\u00f9 frequenti \u00e8 bloccare le app che girano su emulatori non autorizzati. <cite>Gli attaccanti frequentemente usano emulatori, device farm basati su cloud, o dispositivi fisici compromessi come ambienti controllati per reverse engineering e abuso API su larga scala. Con attestation, ogni richiesta API pu\u00f2 includere un segnale che verifica l&#8217;integrit\u00e0 sia dell&#8217;app che del suo runtime environment.<\/cite><\/p>\n<p><cite>Usando il Play Integrity API, posso trovare dispositivi che richiedono volumi elevati di token di integrit\u00e0: abusanti con attivit\u00e0 ad alto volume comunemente generano risultati di attestazione validi da dispositivi reali e li forniscono ai bot per automatizzare attacchi. Posso usare il recent device activity level per controllare quante attestazioni sono state generate dalla mia app su quel dispositivo nell&#8217;ultima ora.<\/cite><\/p>\n<p>Nel mio sistema di fraud detection:<\/p>\n<pre><code>\/\/ Emulator Detection e Rate Limiting\nclass EmulatorDetectionEngine {\n  async evaluateDevice(attestationMetadata, deviceId, userId) {\n    const signals = [];\n    \n    \/\/ Signal 1: Boot State Analysis\n    if (attestationMetadata.bootState === 'UNVERIFIED') {\n      signals.push({ type: 'BOOT_UNVERIFIED', risk: 'HIGH' });\n    }\n    \n    \/\/ Signal 2: Security Level Check\n    if (attestationMetadata.securityLevel === 'SOFTWARE') {\n      \/\/ Software-backed keys (non hardware-backed)\n      signals.push({ type: 'SOFTWARE_BACKED_KEY', risk: 'HIGH' });\n    }\n    \n    \/\/ Signal 3: RKP vs Factory-Keyed Analysis\n    const rootUsed = attestationMetadata.attestationRoot;\n    if (rootUsed === 'FACTORY_KEYED') {\n      \/\/ Dispositivi older senza RKP - accettabile\n      signals.push({ type: 'FACTORY_KEYED_DEVICE', risk: 'LOW' });\n    } else if (rootUsed === 'RKP_NEW') {\n      \/\/ Dispositivi moderni con RKP - pi\u00f9 sicuri\n      signals.push({ type: 'RKP_ENABLED', risk: 'VERY_LOW' });\n    }\n    \n    \/\/ Signal 4: Certificato Revocation Check\n    const isRevoked = await this.checkRevocationList(\n      attestationMetadata.certificateChain\n    );\n    if (isRevoked) {\n      signals.push({ type: 'REVOKED_CERT', risk: 'CRITICAL' });\n    }\n    \n    \/\/ Signal 5: Device Activity Frequency\n    const recentTokenCount = await this.countRecentAttestations(\n      deviceId, \n      userId, \n      '1h'\n    );\n    if (recentTokenCount &gt; 100) {\n      \/\/ 100+ attestazioni in un'ora = pattern bot\n      signals.push({ type: 'HIGH_TOKEN_FREQUENCY', risk: 'HIGH' });\n    }\n    \n    \/\/ Signal 6: Geolocation Anomaly\n    const lastLocation = await this.getLastSeenLocation(deviceId);\n    const currentIP = this.getCurrentIP();\n    if (this.isGeographicallyImpossible(lastLocation, currentIP)) {\n      signals.push({ type: 'IMPOSSIBLE_TRAVEL', risk: 'MEDIUM' });\n    }\n    \n    \/\/ Calculate overall risk score\n    const riskScore = this.calculateRiskScore(signals);\n    \n    return {\n      signals: signals,\n      riskScore: riskScore,\n      verdict: riskScore &gt; 70 ? 'BLOCK' : (riskScore &gt; 40 ? 'CHALLENGE' : 'ALLOW'),\n      recommendedAction: this.getRecommendedAction(signals)\n    };\n  }\n  \n  async checkRevocationList(certificateChain) {\n    \/\/ Implementazione della CRL\/OCSP check\n    const leafCert = certificateChain[0];\n    const crlDp = extractCRLDistributionPoint(leafCert);\n    const revocationStatus = await fetchRevocationStatus(crlDp);\n    return revocationStatus === 'REVOKED';\n  }\n}\n<\/code><\/pre>\n<h2>Implementazione Lato App: Android 17 Specifics<\/h2>\n<p>Sul lato client, ho aggiornato la mia implementazione per sfruttare le nuove API di Android 17. Il framework fornito da Google ha subito revisioni importanti:<\/p>\n<pre><code>\/\/ Android 17 App-Level Attestation Implementation\nimport android.security.keystore.KeyGenParameterSpec;\nimport androidx.security.crypto.EncryptedSharedPreferences;\n\nclass AndroidAttestationClient {\n  private val context: Context\n  private val keyStore: KeyStore = KeyStore.getInstance(\"AndroidKeyStore\")\n  \n  fun generateAttestationKey(attestationChallenge: ByteArray): Certificate {\n    val keyAlias = \"attest_key_\" + System.currentTimeMillis()\n    \n    \/\/ Android 17 aggiorna KeyGenParameterSpec con il supporto PQC\n    val keyGenSpec = KeyGenParameterSpec.Builder(\n      keyAlias,\n      KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY\n    )\n      .setAlgorithm(KeyProperties.KEY_ALGORITHM_RSA)\n      .setKeySize(4096)\n      .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)\n      \/\/ NUOVO in Android 17: supporto per PQC\n      .setIsStrongBoxBacked(true)  \/\/ Usa StrongBox se disponibile\n      .setAttestationChallenge(attestationChallenge)\n      \/\/ NUOVO: metadata per RKP compatibility\n      .setAttestKeyAlgorithm(\"RSA\")  \/\/ Sar\u00e0 eventualmente PQC\n      .build()\n    \n    val kpg = KeyPairGenerator.getInstance(\n      KeyProperties.KEY_ALGORITHM_RSA,\n      \"AndroidKeyStore\"\n    )\n    kpg.initialize(keyGenSpec)\n    val keyPair = kpg.generateKeyPair()\n    \n    \/\/ Estrai il certificato di attestazione\n    val cert = getCertificateChain(keyAlias)\n    \n    return cert\n  }\n  \n  fun signWithAttestedKey(\n    keyAlias: String,\n    dataToSign: ByteArray,\n    challenge: ByteArray\n  ): ByteArray {\n    val privateKey = keyStore.getKey(keyAlias, null) as PrivateKey\n    \n    val signature = Signature.getInstance(\"SHA256withRSA\")\n    signature.initSign(privateKey)\n    signature.update(dataToSign)\n    signature.update(challenge)  \/\/ Includi challenge per proof-of-possession\n    \n    return signature.sign()\n  }\n  \n  fun getCertificateChain(keyAlias: String): Certificate {\n    return keyStore.getCertificateChain(keyAlias) as Certificate\n  }\n  \n  \/\/ NUOVO in Android 17: Supporto per RKP remote provisioning\n  fun useRemoteKeyProvisioning(): Boolean {\n    return Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.ANDROID_17 &amp;&amp;\n           hasRKPCapability()\n  }\n  \n  private fun hasRKPCapability(): Boolean {\n    \/\/ Verificare se il dispositivo supporta RKP\n    \/\/ Android 17 devices moderni dovrebbero avere questo\n    return try {\n      val keyMint = KeyStore.getInstance(\"AndroidKeyStore\")\n      \/\/ Controlla capability RKP\n      true\n    } catch (e: Exception) {\n      false\n    }\n  }\n}\n<\/code><\/pre>\n<p>Nel mio codice di app, chiamo questa procedura al login:<\/p>\n<pre><code>\/\/ Nel MainActivity o LoginFragment\nclass SecureLoginFlow {\n  suspend fun performSecureLogin(username: String, password: String) {\n    val attestationClient = AndroidAttestationClient(context)\n    \n    \/\/ Step 1: Request challenge dal server\n    val challengeResponse = apiClient.requestAttestationChallenge(username)\n    val challenge = challengeResponse.challenge\n    \n    \/\/ Step 2: Genera chiave attestata nel TEE\/StrongBox\n    val attestCert = attestationClient.generateAttestationKey(challenge)\n    \n    \/\/ Step 3: Invia certificato di attestazione al backend\n    val attestationResponse = apiClient.submitAttestation(\n      username = username,\n      attestationCertificate = attestCert,\n      deviceInfo = getDeviceInfo()\n    )\n    \n    \/\/ Step 4: Se attestazione \u00e8 accettata, salva sessione\n    if (attestationResponse.verdict == \"ALLOW\") {\n      createSecureSession(username, attestCert)\n      navigateToMainScreen()\n    } else if (attestationResponse.verdict == \"CHALLENGE\") {\n      \/\/ Secondo fattore o OTP\n      showMFAChallenge()\n    } else {\n      showErrorAndBlockLogin(attestationResponse.reason)\n    }\n  }\n}\n<\/code><\/pre>\n<h2>Errori Comuni che Ho Incontrato (e Come Evitarli)<\/h2>\n<p>Nel mio percorso di implementazione, ho fatto alcuni errori costosi che voglio condividere:<\/p>\n<p><strong>Errore 1: Fidarsi solo del certificato senza proof-of-possession<\/strong><br \/>Inizialmente, validavo il certificato di attestazione ma non richiedevo al client di firmare challenge aggiuntivi. Un attaccante potrebbe aver copiato il certificato da un dispositivo legittimo e reiterarlo. Ora implemento sempre il pattern attestation-then-assertion.<\/p>\n<p><strong>Errore 2: Non aggiornare la trust store per RKP<\/strong><br \/>Ad aprile 2026, ho scoperto che il vecchio root certificate non era pi\u00f9 valido per i dispositivi RKP. Ho dovuto aggiornare rapidamente la mia logic di validazione per supportare entrambi i root (vecchio per dispositivi factory-keyed, nuovo per RKP). Il mio sistema di monitoring mi ha avvisato che circa 40% dei dispositivi venivano rigettati improvvisamente.<\/p>\n<p><strong>Errore 3: Rate limiting insufficiente per attestazioni<\/strong><br \/>Inizialmente non limitavo quante attestazioni potesse richiedere un singolo dispositivo. Un attaccante pu\u00f2 generare token validi da un dispositivo reale e distribuirli ai bot. Ora monitoraggio attivamente le attestazioni per dispositivo\/ora.<\/p>\n<p><strong>Errore 4: Non verificare il revocation status<\/strong><br \/>Ho assunto che i certificati fossero sempre validi. In realt\u00e0, Google periodicamente revoca certificati di attestazione compromessi. Ora faccio CRL\/OCSP check ad ogni validazione.<\/p>\n<h2>FAQ<\/h2>\n<h3>Qual \u00e8 la differenza tra Factory-Keyed devices e RKP-enabled devices?<\/h3>\n<p>I factory-keyed devices hanno chiavi di attestazione bruciate in fabbrica e non possono essere cambiate. I dispositivi RKP (Remote Key Provisioning) possono ricevere nuove chiavi di attestazione remotamente, il che migliora la sicurezza se una chiave \u00e8 compromessa. A partire da aprile 2026, i dispositivi RKP usano il nuovo root certificate ECDSA P-384, mentre i dispositivi pi\u00f9 vecchi continuano a usare il vecchio root. Il mio backend deve supportare entrambi.<\/p>\n<h3>Come distinguo tra un dispositivo rooted e un emulatore?<\/h3>\n<p>Entrambi produrranno attestazioni con bootState &#8220;UNVERIFIED&#8221;. La differenza \u00e8 nel contesto: i dispositivi rooted potrebbero comunque avere chiavi hardware-backed (se non sono stati tamperati a livello TEE), mentre gli emulatori tipicamente non hanno affatto hardware-backed keys. Combino pi\u00f9 segnali (boot state, security level, frequency di attestazioni, geolocation) per una valutazione del rischio pi\u00f9 accurata.<\/p>\n<h3>Cosa succede se il dispositivo non ha StrongBox ma solo TEE?<\/h3>\n<p>Sia TEE che StrongBox forniscono hardware-backed key storage. StrongBox \u00e8 preferibile perch\u00e9 \u00e8 pi\u00f9 isolato (modulo hardware dedicato), ma TEE \u00e8 ancora accettabile. Nel mio backend, controllo il securityLevel nei metadati e potrei applicare policy diverse: ad esempio, richiedere MFA aggiuntivo per TEE-backed devices rispetto a StrongBox-backed devices.<\/p>\n<h3>Posso usare Zero-Knowledge Proofs senza esporre la chiave pubblica?<\/h3>\n<p>S\u00ec, \u00e8 il vantaggio principale dei ZK proofs. Provo che posseggo una chiave attestata da un&#8217;autorit\u00e0 fidata senza mai rivelare la chiave pubblica stessa. Questo \u00e8 particolarmente utile per applicazioni di verifica dell&#8217;et\u00e0 o privacy-sensitive dove non voglio correlare l&#8217;identit\u00e0 hardware del dispositivo al profilo dell&#8217;utente.<\/p>\n<h3>Qual \u00e8 il timeframe consigliato per la freschezza di una attestazione?<\/h3>\n<p>Nel mio sistema, ritengo un&#8217;attestazione fresca se valida entro 1 ora. Per operazioni molto sensibili (trasferimenti di denaro), richiedo una nuova attestazione o almeno una firma di &#8220;assertion&#8221; firmata nel TEE negli ultimi 5-10 secondi. Dipende dal vostro appetite per il rischio e dalla frequenza di uso dell&#8217;app.<\/p>\n<h2>Conclusione<\/h2>\n<p>Implementare <strong>Android 17 Hardware-Backed Key Attestation e Zero-Knowledge Proofs<\/strong> non \u00e8 un compito banale, ma \u00e8 diventato essenziale per qualsiasi app che maneggia dati sensibili. Ho visto troppe violazioni di sicurezza derivare da una fiducia erronea nel fatto che il dispositivo fosse sicuro senza verifica effettiva.<\/p>\n<p>Il framework di Android 17 \u2013 con il supporto PQC, RKP provisioning remoto, e metadati di boot-state \u2013 fornisce tutti gli strumenti necessari. L&#8217;importante \u00e8 implementarli correttamente: attestation-then-assertion pattern, aggiornamento della trust store per i root certificates nuovi, rate limiting sulle attestazioni, e verifica del revocation status.<\/p>\n<p>Se utilizzate app bancarie o fintech, vi raccomando vivamente di adottare questa procedura. Se lavorate con dispositivi compromessi o emulatori nel vostro flusso di reverse engineering, preparatevi: il panorama sta cambiando rapidamente verso implementazioni pi\u00f9 robuste di attestation.<\/p>\n<p>Avete domande sulla vostra implementazione di attestation? Fatemi sapere nei commenti \u2013 ho affrontato parecchi edge case che potrebbero essere rilevanti anche per il vostro caso d&#8217;uso.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Come implementare Hardware-Backed Key Attestation, Zero-Knowledge Proofs e app-level attestation in Android 17. Procedura completa per verificare l&#8217;integrit\u00e0 del Secure Enclave e prevenire abuso di emulatori.<\/p>\n","protected":false},"author":1,"featured_media":3407,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_seopress_robots_primary_cat":"","_seopress_titles_title":"Android 17 Hardware-Backed Key Attestation | ZK Proofs","_seopress_titles_desc":"Guida pratica: implementa Hardware-Backed Key Attestation, Zero-Knowledge Proofs e Secure Enclave integrity verification in Android 17. Previeni emulator abuse.","_seopress_robots_index":"","footnotes":""},"categories":[7],"tags":[154,1240,848,1241,1242,1243],"class_list":["post-3406","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-android","tag-android-security","tag-cryptography","tag-fraud-prevention","tag-hardware-attestation","tag-keystore","tag-tee-security"],"_links":{"self":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/posts\/3406","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/comments?post=3406"}],"version-history":[{"count":0,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/posts\/3406\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/media\/3407"}],"wp:attachment":[{"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/media?parent=3406"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/categories?post=3406"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/darioiannascoli.it\/blog\/wp-json\/wp\/v2\/tags?post=3406"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}