Skip to content

Instantly share code, notes, and snippets.

@vitorpamplona
Created July 5, 2022 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vitorpamplona/e880b30767302d5e4bfa9742a51b3315 to your computer and use it in GitHub Desktop.
Save vitorpamplona/e880b30767302d5e4bfa9742a51b3315 to your computer and use it in GitHub Desktop.
Parsing ICAO MasterList (RFC 5652 - Cryptographic Message Syntax) with Kotlin/Android
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.bouncycastle.asn1.ASN1Integer
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.ASN1Set
import org.bouncycastle.asn1.x509.Certificate
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cms.CMSSignedData
import org.bouncycastle.cms.SignerInformation
import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.InputStream
import java.security.cert.CertificateFactory
/**
* Download certificates from: https://www.icao.int/Security/FAL/PKD/Pages/icao-master-list.aspx
* The Health Master list signing certificates ("ICAOHealthMLSigner1.pem.crt") are also available in the website.
*/
@RunWith(AndroidJUnit4::class)
class ICAOMasterListTest {
private val cf = CertificateFactory.getInstance("X.509")!!;
private fun inputStream(assetName: String): InputStream? {
return javaClass.classLoader?.getResourceAsStream(assetName)
}
private fun loadMasterList(cmsSignedData: CMSSignedData): MutableList<java.security.cert.Certificate> {
val cscaCerts = mutableListOf<java.security.cert.Certificate>()
val idMasterList: String = cmsSignedData.signedContentTypeOID
var octets: ByteArray? = null
if (idMasterList == "2.23.136.1.1.2") {
val bout = ByteArrayOutputStream()
cmsSignedData.signedContent.write(bout)
octets = bout.toByteArray()
}
val derObjects = ASN1Sequence.getInstance(octets).objects
while (derObjects.hasMoreElements()) {
val version = derObjects.nextElement() as ASN1Integer //Should be 0
val certSet = ASN1Set.getInstance(derObjects.nextElement())
val certs = certSet.objects;
while (certs.hasMoreElements()) {
val certAsASN1Object = Certificate.getInstance(certs.nextElement())
cscaCerts.add(cf.generateCertificate(ByteArrayInputStream(certAsASN1Object.encoded)))
}
}
return cscaCerts
}
private fun loadSignerList(cmsSignedData: CMSSignedData): MutableList<java.security.cert.Certificate> {
val signerCertificates = mutableListOf<java.security.cert.Certificate>()
val certStore = cmsSignedData.certificates
val converter = JcaX509CertificateConverter()
val certificateHolders = certStore.getMatches(null)
for (holder in certificateHolders) {
signerCertificates.add(converter.getCertificate(holder))
}
return signerCertificates
}
private fun verifySignerInfo(signerInfo: SignerInformation, certs: MutableCollection<out java.security.cert.Certificate>): Boolean {
return signerInfo
.verify(JcaSimpleSignerInfoVerifierBuilder().setProvider("BC")
.build(certs.first().publicKey))
}
@Test
fun testICAOHealthList() {
val signingCerts = cf.generateCertificates(inputStream("ICAOHealthMLSigner1.pem.crt"))
val healthList = CMSSignedData(inputStream("ICAOHealthML27May2022.ml"))
val signerInfo = healthList.signerInfos.signers.first()
val cscaCerts = loadMasterList(healthList)
Assert.assertTrue(verifySignerInfo(signerInfo, signingCerts))
Assert.assertEquals(9, cscaCerts.size)
}
@Test
fun testICAOMasterList() {
val masterList = CMSSignedData(inputStream("ICAOMLJune2022.ml"))
val signerInfo = masterList.signerInfos.signers.first()
val cscaCerts = loadMasterList(masterList)
val signerCertificates = loadSignerList(masterList)
Assert.assertTrue(verifySignerInfo(signerInfo, signerCertificates))
Assert.assertEquals(346, cscaCerts.size)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment