Skip to content

Commit

Permalink
TrustManager support in the Wallet App (#438)
Browse files Browse the repository at this point in the history
- Reader certificate chain in the Wallet app will be validated with the TrustManager
- UI support for CA certificates

Signed-off-by: Kees Geluk <[email protected]>
  • Loading branch information
keesgeluk authored Dec 20, 2023
1 parent 20861f4 commit 273ed93
Show file tree
Hide file tree
Showing 19 changed files with 881 additions and 260 deletions.
35 changes: 35 additions & 0 deletions appholder/src/main/java/com/android/identity/wallet/HolderApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,35 @@ import com.android.identity.credentialtype.knowntypes.VaccinationDocument
import com.android.identity.credentialtype.knowntypes.VehicleRegistration
import com.android.identity.securearea.SecureAreaRepository
import com.android.identity.securearea.SoftwareSecureArea
import com.android.identity.storage.GenericStorageEngine
import com.android.identity.storage.StorageEngine
import com.android.identity.trustmanagement.TrustManager
import com.android.identity.trustmanagement.TrustPoint
import com.android.identity.util.Logger
import com.android.identity.wallet.document.KeysAndCertificates
import com.android.identity.wallet.util.PeriodicKeysRefreshWorkRequest
import com.android.identity.wallet.util.PreferencesHelper
import com.google.android.material.color.DynamicColors
import org.bouncycastle.jce.provider.BouncyCastleProvider
import java.io.ByteArrayInputStream
import java.security.Security
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate

class HolderApp: Application() {

private val credentialTypeRepository by lazy {
CredentialTypeRepository()
}

private val trustManager by lazy {
TrustManager()
}

private val certificateStorageEngine by lazy {
GenericStorageEngine(getDir("Certificates", MODE_PRIVATE))
}

override fun onCreate() {
super.onCreate()
Logger.setLogPrinter(AndroidLogPrinter())
Expand All @@ -41,11 +57,22 @@ class HolderApp: Application() {
credentialTypeRepositoryInstance.addCredentialType(VehicleRegistration.getCredentialType())
credentialTypeRepositoryInstance.addCredentialType(VaccinationDocument.getCredentialType())
credentialTypeRepositoryInstance.addCredentialType(EUPersonalID.getCredentialType())
trustManagerInstance = trustManager
certificateStorageEngineInstance = certificateStorageEngine
certificateStorageEngineInstance.enumerate().forEach {
val certificate = parseCertificate(certificateStorageEngineInstance.get(it)!!)
trustManagerInstance.addTrustPoint(TrustPoint(certificate))
}
KeysAndCertificates.getTrustedReaderCertificates(this).forEach {
trustManagerInstance.addTrustPoint(TrustPoint(it))
}
}

companion object {

lateinit var credentialTypeRepositoryInstance: CredentialTypeRepository
lateinit var trustManagerInstance: TrustManager
lateinit var certificateStorageEngineInstance: StorageEngine
fun createCredentialStore(
context: Context,
secureAreaRepository: SecureAreaRepository
Expand All @@ -61,4 +88,12 @@ class HolderApp: Application() {
return CredentialStore(storageEngine, secureAreaRepository)
}
}

/**
* Parse a byte array as an X509 certificate
*/
private fun parseCertificate(certificateBytes: ByteArray): X509Certificate {
return CertificateFactory.getInstance("X509")
.generateCertificate(ByteArrayInputStream(certificateBytes)) as X509Certificate
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import com.android.identity.wallet.HolderApp
import com.android.identity.wallet.R
import com.android.identity.wallet.databinding.FragmentTransferDocumentBinding
import com.android.identity.wallet.document.DocumentInformation
import com.android.identity.wallet.document.KeysAndCertificates
import com.android.identity.wallet.readerauth.SimpleReaderTrustStore
import com.android.identity.wallet.transfer.TransferManager
import com.android.identity.wallet.trustmanagement.CustomValidators
import com.android.identity.wallet.trustmanagement.getCommonName
import com.android.identity.wallet.util.PreferencesHelper
import com.android.identity.wallet.util.TransferStatus
import com.android.identity.wallet.util.log
Expand Down Expand Up @@ -81,14 +82,10 @@ class TransferDocumentFragment : Fragment() {

private fun onTransferRequested() {
log("Request")

var commonName = ""
var trusted = false
try {
val trustStore = com.android.identity.wallet.readerauth.SimpleReaderTrustStore(
KeysAndCertificates.getTrustedReaderCertificates(requireContext())
)
val requestedDocuments = viewModel.getRequestedDocuments()
var readerCommonName = ""
var readerIsTrusted = false
requestedDocuments.forEach { reqDoc ->
val docs = viewModel.getDocuments().filter { reqDoc.docType == it.docType }
if (!viewModel.getSelectedDocuments().any { reqDoc.docType == it.docType }) {
Expand All @@ -104,39 +101,31 @@ class TransferDocumentFragment : Fragment() {
}
val doc = viewModel.getSelectedDocuments().first { reqDoc.docType == it.docType }
if (reqDoc.readerAuth != null && reqDoc.readerAuthenticated) {
val readerChain = reqDoc.readerCertificateChain
val trustPath =
trustStore.createCertificationTrustPath(readerChain.toList())
// Look for the common name in the root certificate if it is trusted or not
val certChain = if (trustPath?.isNotEmpty() == true) {
trustPath
} else {
readerChain
var certChain = reqDoc.readerCertificateChain
val customValidators = CustomValidators.getByDocType(doc.docType)
val result = HolderApp.trustManagerInstance.verify(
chain = certChain,
customValidators = customValidators
)
trusted = result.isTrusted
if (result.trustChain.any()){
certChain = result.trustChain
}

certChain.first().subjectX500Principal.name.split(",")
.forEach { line ->
val (key, value) = line.split("=", limit = 2)
if (key == "CN") {
readerCommonName = value
}
}
commonName = certChain.last().issuerX500Principal.getCommonName("")

// Add some information about the reader certificate used
if (trustStore.validateCertificationTrustPath(trustPath)) {
readerIsTrusted = true
binding.txtDocuments.append("- Trusted reader auth used: ($readerCommonName)\n")
if (result.isTrusted) {
binding.txtDocuments.append("- Trusted reader auth used: ($commonName)\n")
} else {
readerIsTrusted = false
binding.txtDocuments.append("- Not trusted reader auth used: ($readerCommonName)\n")
binding.txtDocuments.append("- Not trusted reader auth used: ($commonName)\n")
}
}
binding.txtDocuments.append("- ${doc.userVisibleName} (${doc.docType})\n")
}
if (viewModel.getSelectedDocuments().isNotEmpty()) {
viewModel.createSelectedItemsList()
val direction = TransferDocumentFragmentDirections
.navigateToConfirmation(readerCommonName, readerIsTrusted)
.navigateToConfirmation(commonName, trusted)
findNavController().navigate(direction)
} else {
// Send response with 0 documents
Expand Down

This file was deleted.

This file was deleted.

Loading

0 comments on commit 273ed93

Please sign in to comment.