Skip to content

Commit

Permalink
TrustManager etc., added copyright and documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
keesgeluk committed Nov 20, 2023
1 parent 5bafa43 commit 443518f
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.identity.trustmanagement

import org.bouncycastle.asn1.ASN1ObjectIdentifier
Expand All @@ -9,6 +24,9 @@ import javax.security.auth.x500.X500Principal
private const val DIGITAL_SIGNATURE = 0
private const val KEY_CERT_SIGN = 5

/**
* Determine whether the certificate has the key usage of a document signer
*/
fun X509Certificate.hasKeyUsageDocumentSigner(): Boolean {
if (this.keyUsage == null)
{
Expand All @@ -17,6 +35,9 @@ fun X509Certificate.hasKeyUsageDocumentSigner(): Boolean {
return this.keyUsage[DIGITAL_SIGNATURE]
}

/**
* Determine whether the certificate has the key usage of a certificate authority
*/
fun X509Certificate.hasKeyUsageCaCertificate(): Boolean {
if (this.keyUsage == null)
{
Expand All @@ -25,22 +46,37 @@ fun X509Certificate.hasKeyUsageCaCertificate(): Boolean {
return this.keyUsage[KEY_CERT_SIGN]
}

/**
* Extract the common name of a X500Principal (subject or issuer)
*/
fun X500Principal.getCommonName(defaultValue: String): String {
return readRdn(this.name, BCStyle.CN, defaultValue)
}

/**
* Extract the organisation of a X500Principal (subject or issuer)
*/
fun X500Principal.getOrganisation(defaultValue: String): String {
return readRdn(this.name, BCStyle.O, defaultValue)
}

/**
* Extract the organisational unit of a X500Principal (subject or issuer)
*/
fun X500Principal.organisationalUnit(defaultValue: String): String {
return readRdn(this.name, BCStyle.OU, defaultValue)
}

/**
* Extract the country code of a X500Principal (subject or issuer)
*/
fun X500Principal.countryCode(defaultValue: String): String {
return readRdn(this.name, BCStyle.C, defaultValue)
}

/**
* Read a relative distinguished name from a distinguished name
*/
private fun readRdn(name: String, field: ASN1ObjectIdentifier, defaultValue: String): String {
val x500name = X500Name(name)
for (rdn in x500name.getRDNs(field)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.identity.trustmanagement

import org.bouncycastle.asn1.x500.X500Name
Expand All @@ -9,6 +24,9 @@ import java.security.cert.CertificateFactory
import java.security.cert.PKIXCertPathChecker
import java.security.cert.X509Certificate

/**
* Object with validations on X509 certificates
*/
object CertificateValidations {

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.identity.trustmanagement

import java.security.cert.Certificate
import java.security.cert.CertificateException
import java.security.cert.PKIXCertPathChecker
import java.security.cert.X509Certificate

/**
* Class used to validate that the country code in the whole certificate chain is the same
*/
class CountryValidator : PKIXCertPathChecker() {
private var previousCountryCode: String = ""

/**
* There is no custom initialisation of this class
*/
override fun init(p0: Boolean) {
// intentionally left empty
}


/**
* Forward checking supported: the order of the certificate chain is not relevant for the check
* on country code.
*/
override fun isForwardCheckingSupported(): Boolean {
return true
}

/**
* Check the country code
*/
override fun check(certificate: Certificate?, state: MutableCollection<String>?) {
if (certificate is X509Certificate) {
val countryCode = certificate.subjectX500Principal.countryCode("")
Expand All @@ -29,6 +59,9 @@ class CountryValidator : PKIXCertPathChecker() {
}
}

/**
* Extensions are not validated on country code
*/
override fun getSupportedExtensions(): MutableSet<String> {
return mutableSetOf()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,29 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.identity.trustmanagement

import java.security.cert.PKIXCertPathChecker

/**
* Object used to obtain custom validators based on docType
*/
object CustomValidators {
fun getByDocType(mdocType: String): List<PKIXCertPathChecker>
fun getByDocType(docType: String): List<PKIXCertPathChecker>
{
when (mdocType){
when (docType){
"org.iso.18013.5.1.mDL" -> return listOf(CountryValidator())
else -> return emptyList()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
/*
* Copyright 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.identity.trustmanagement

import com.android.identity.storage.StorageEngine
Expand All @@ -8,26 +23,32 @@ import java.security.cert.PKIXCertPathChecker
import java.security.cert.X509Certificate

/**
* [TrustManager] class used for the verification of a certificate chain
* This class is used for the verification of a certificate chain
*/
class TrustManager(val certificateStorage: StorageEngine) {

private val certificates: MutableMap<X500Name, X509Certificate>

/**
* Class containing the result of the verification of a certificate chain
* Nested class containing the result of the verification of a certificate chain
*/
class TrustResult(
var isTrusted: Boolean,
var trustChain: List<X509Certificate> = emptyList(),
var error: String = ""
)

/**
* Initialize private variables
*/
init {
certificates = getAllCertificates().associateBy { X500Name(it.subjectX500Principal.name) }
.toMutableMap()
}

/**
* Save a certificate
*/
fun saveCertificate(certificateBytes: ByteArray) {
val certificate = parseCertificate(certificateBytes)
if (certificateExists(certificate)) {
Expand All @@ -38,17 +59,26 @@ class TrustManager(val certificateStorage: StorageEngine) {
certificates[name] = certificate
}

/**
* Check that a certificate exists
*/
fun certificateExists(certificate: X509Certificate): Boolean{
val name = X500Name(certificate.subjectX500Principal.name)
return certificateStorage.get(name.toString()) != null
}

/**
* Get all the certificates in the [TrustManager]
*/
fun getAllCertificates(): List<X509Certificate> {
return certificateStorage.enumerate().map {
parseCertificate(certificateStorage.get(it)!!)
}
}

/**
* Delete a certificate
*/
fun deleteCertificate(certificate: X509Certificate){
val name = X500Name(certificate.subjectX500Principal.name)
certificateStorage.delete(name.toString())
Expand Down Expand Up @@ -90,6 +120,9 @@ class TrustManager(val certificateStorage: StorageEngine) {
}
}

/**
* Find the trusted root of a certificate chain
*/
private fun findTrustedRoot(chain: List<X509Certificate>): X509Certificate? {
chain.forEach { cert ->
run {
Expand All @@ -102,6 +135,9 @@ class TrustManager(val certificateStorage: StorageEngine) {
return null
}

/**
* Validate the certificate trust path
*/
private fun validateCertificationTrustPath(
certificationTrustPath: List<X509Certificate>,
customValidators: List<PKIXCertPathChecker>
Expand All @@ -126,6 +162,9 @@ class TrustManager(val certificateStorage: StorageEngine) {
}


/**
* Parse a byte array an X509 certificate
*/
private fun parseCertificate(certificateBytes: ByteArray): X509Certificate {
return CertificateFactory.getInstance("X509")
.generateCertificate(ByteArrayInputStream(certificateBytes)) as X509Certificate
Expand Down

0 comments on commit 443518f

Please sign in to comment.