Skip to content

Commit e3e8644

Browse files
OID Checkup (#42)
* OID Services, Test Classes and Helper added. * Testcases modified, Unknown Certificatetype added to validation, overload added for validation. * Updated tests * Updated formatting * Updated tests * Update Co-authored-by: Oleksandr Sarapulov <[email protected]>
1 parent 0aeb3a5 commit e3e8644

File tree

9 files changed

+302
-144
lines changed

9 files changed

+302
-144
lines changed

decoder/src/main/java/dgca/verifier/app/decoder/cose/CryptoService.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
package dgca.verifier.app.decoder.cose
2424

25+
import dgca.verifier.app.decoder.model.CertificateType
2526
import dgca.verifier.app.decoder.model.VerificationResult
2627
import java.security.cert.Certificate
2728

@@ -31,4 +32,5 @@ import java.security.cert.Certificate
3132
interface CryptoService {
3233

3334
fun validate(cose: ByteArray, certificate: Certificate, verificationResult: VerificationResult)
35+
fun validate(cose: ByteArray, certificate: Certificate, verificationResult: VerificationResult, certificateType: CertificateType)
3436
}

decoder/src/main/java/dgca/verifier/app/decoder/cose/VerificationCryptoService.kt

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ import com.upokecenter.cbor.CBORObject
2626
import dgca.verifier.app.decoder.ECDSA_256
2727
import dgca.verifier.app.decoder.RSA_PSS_256
2828
import dgca.verifier.app.decoder.convertToDer
29+
import dgca.verifier.app.decoder.model.CertificateType
2930
import dgca.verifier.app.decoder.model.VerificationResult
31+
import dgca.verifier.app.decoder.services.X509
3032
import dgca.verifier.app.decoder.verify
3133
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
3234
import org.bouncycastle.jce.provider.BouncyCastleProvider
@@ -39,13 +41,32 @@ import java.security.spec.RSAPublicKeySpec
3941
/**
4042
* Verifies COSE signature
4143
*/
42-
class VerificationCryptoService : CryptoService {
44+
class VerificationCryptoService(private val x509: X509) : CryptoService {
4345

4446
init {
4547
Security.addProvider(BouncyCastleProvider()) // for SHA256withRSA/PSS
4648
}
4749

48-
override fun validate(cose: ByteArray, certificate: Certificate, verificationResult: VerificationResult) {
50+
override fun validate(
51+
cose: ByteArray,
52+
certificate: Certificate,
53+
verificationResult: VerificationResult,
54+
certificateType: CertificateType
55+
) {
56+
validate(cose, certificate, verificationResult)
57+
58+
verificationResult.coseVerified =
59+
verificationResult.coseVerified && (certificateType == CertificateType.UNKNOWN || x509.isSuitable(
60+
certificate.encoded,
61+
certificateType
62+
))
63+
}
64+
65+
override fun validate(
66+
cose: ByteArray,
67+
certificate: Certificate,
68+
verificationResult: VerificationResult
69+
) {
4970
val verificationKey = certificate.publicKey
5071
verificationResult.coseVerified = try {
5172
val messageObject = CBORObject.DecodeFromBytes(cose)
@@ -66,7 +87,8 @@ class VerificationCryptoService : CryptoService {
6687
)
6788
}
6889
RSA_PSS_256 -> {
69-
val bytes = SubjectPublicKeyInfo.getInstance(certificate.publicKey.encoded).publicKeyData.bytes
90+
val bytes =
91+
SubjectPublicKeyInfo.getInstance(certificate.publicKey.encoded).publicKeyData.bytes
7092
val rsaPublicKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(bytes)
7193
val spec = RSAPublicKeySpec(rsaPublicKey.modulus, rsaPublicKey.publicExponent)
7294
val key = KeyFactory.getInstance("RSA").generatePublic(spec)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package dgca.verifier.app.decoder.model
2+
3+
enum class CertificateType {
4+
UNKNOWN,VACCINATION,RECOVERY,TEST
5+
}

decoder/src/main/java/dgca/verifier/app/decoder/model/GreenCertificate.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,12 @@ data class GreenCertificate(
7474
} catch (ex: Exception) {
7575
""
7676
}.toLowerCase(Locale.ROOT)
77-
}
77+
78+
fun getType(): CertificateType {
79+
if (vaccinations?.isNotEmpty() == true)
80+
return CertificateType.VACCINATION
81+
if (tests?.isNotEmpty() == true) return CertificateType.TEST
82+
if (recoveryStatements?.isNotEmpty() == true) return CertificateType.RECOVERY
83+
return CertificateType.UNKNOWN
84+
}
85+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package dgca.verifier.app.decoder.services
2+
3+
import dgca.verifier.app.decoder.model.CertificateType
4+
import java.io.ByteArrayInputStream
5+
import java.security.cert.*
6+
7+
8+
class X509 {
9+
private val OID_TEST = "1.3.6.1.4.1.1847.2021.1.1"
10+
private val OID_ALT_TEST = "1.3.6.1.4.1.0.1847.2021.1.1"
11+
private val OID_VACCINATION = "1.3.6.1.4.1.1847.2021.1.2"
12+
private val OID_ALT_VACCINATION = "1.3.6.1.4.1.0.1847.2021.1.2"
13+
private val OID_RECOVERY = "1.3.6.1.4.1.1847.2021.1.3"
14+
private val OID_ALT_RECOVERY = "1.3.6.1.4.1.0.1847.2021.1.3"
15+
16+
fun checkIsSuitable(cert: String?, certType: CertificateType?): Boolean {
17+
val b64: ByteArray = org.bouncycastle.util.encoders.Base64.decode(cert)
18+
return isSuitable(b64, certType)
19+
}
20+
21+
fun isSuitable(data: ByteArray?, certificateType: CertificateType?): Boolean {
22+
try {
23+
val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
24+
val cert: Certificate = cf.generateCertificate(ByteArrayInputStream(data))
25+
if (isType(cert as X509Certificate)) {
26+
val extendedKeys = cert.extendedKeyUsage
27+
return when (certificateType) {
28+
CertificateType.TEST -> extendedKeys.contains(OID_TEST) || extendedKeys.contains(
29+
OID_ALT_TEST
30+
)
31+
CertificateType.VACCINATION -> extendedKeys.contains(OID_VACCINATION) || extendedKeys.contains(
32+
OID_ALT_VACCINATION
33+
)
34+
CertificateType.RECOVERY -> extendedKeys.contains(OID_RECOVERY) || extendedKeys.contains(
35+
OID_ALT_RECOVERY
36+
)
37+
CertificateType.UNKNOWN -> false
38+
else -> false
39+
}
40+
}
41+
} catch (e: CertificateException) {
42+
return false
43+
}
44+
return true
45+
}
46+
47+
private fun isType(certificate: X509Certificate): Boolean {
48+
return try {
49+
val extendedKeyUsage: List<String> = certificate.extendedKeyUsage ?: return false
50+
51+
extendedKeyUsage.contains(OID_TEST)
52+
|| extendedKeyUsage.contains(OID_ALT_TEST)
53+
|| extendedKeyUsage.contains(OID_RECOVERY)
54+
|| extendedKeyUsage.contains(OID_ALT_RECOVERY)
55+
|| extendedKeyUsage.contains(OID_VACCINATION)
56+
|| extendedKeyUsage.contains(OID_ALT_VACCINATION)
57+
} catch (e: CertificateParsingException) {
58+
false
59+
}
60+
}
61+
}

decoder/src/test/java/dgca/verifier/app/decoder/CertificateTestRunner.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import dgca.verifier.app.decoder.prefixvalidation.DefaultPrefixValidationService
1717
import dgca.verifier.app.decoder.prefixvalidation.PrefixValidationService
1818
import dgca.verifier.app.decoder.schema.DefaultSchemaValidator
1919
import dgca.verifier.app.decoder.schema.SchemaValidator
20+
import dgca.verifier.app.decoder.services.X509
2021
import org.hamcrest.CoreMatchers.equalTo
2122
import org.hamcrest.CoreMatchers.notNullValue
2223
import org.hamcrest.CoreMatchers.nullValue
@@ -45,7 +46,7 @@ class CertificateTestRunner {
4546
prefixValidationService = DefaultPrefixValidationService()
4647
base45Service = DefaultBase45Service()
4748
compressorService = DefaultCompressorService()
48-
cryptoService = VerificationCryptoService()
49+
cryptoService = VerificationCryptoService(X509())
4950
coseService = DefaultCoseService()
5051
schemaValidator = DefaultSchemaValidator()
5152
cborService = DefaultCborService()

decoder/src/test/java/dgca/verifier/app/decoder/QrCodeTests.java

Lines changed: 0 additions & 139 deletions
This file was deleted.

0 commit comments

Comments
 (0)