diff --git a/jvm/src/main/kotlin/app/cash/trifle/SignedData.kt b/jvm/src/main/kotlin/app/cash/trifle/SignedData.kt index 4b2486d..c4a1a5d 100644 --- a/jvm/src/main/kotlin/app/cash/trifle/SignedData.kt +++ b/jvm/src/main/kotlin/app/cash/trifle/SignedData.kt @@ -22,11 +22,7 @@ data class SignedData internal constructor( ) { fun verify(certAnchor: Certificate): Boolean { val validator = CertChainValidatorFactory.get(certAnchor) - try { - if (!validator.validate(certificates)) return false - } catch (e: Exception) { - return false - } + if (!validator.validate(certificates)) return false val contentVerifier = JCAContentVerifierProvider(certificates.first()) .get(envelopedData.signingAlgorithm) diff --git a/jvm/src/main/kotlin/app/cash/trifle/TrifleErrors.kt b/jvm/src/main/kotlin/app/cash/trifle/TrifleErrors.kt new file mode 100644 index 0000000..b736aee --- /dev/null +++ b/jvm/src/main/kotlin/app/cash/trifle/TrifleErrors.kt @@ -0,0 +1,6 @@ +package app.cash.trifle + +class NoTrustAnchorException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) +class ExpiredCertificateException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) +class IncorrectSignatureException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) +class UnSpecifiedFailureException(message: String? = null, cause: Throwable? = null) : Exception(message, cause) diff --git a/jvm/src/main/kotlin/app/cash/trifle/internal/validators/X509CertChainValidator.kt b/jvm/src/main/kotlin/app/cash/trifle/internal/validators/X509CertChainValidator.kt index 48375b8..4eeec24 100644 --- a/jvm/src/main/kotlin/app/cash/trifle/internal/validators/X509CertChainValidator.kt +++ b/jvm/src/main/kotlin/app/cash/trifle/internal/validators/X509CertChainValidator.kt @@ -1,10 +1,11 @@ package app.cash.trifle.internal.validators -import app.cash.trifle.Certificate +import app.cash.trifle.* import java.security.cert.CertPathValidatorException import java.security.cert.CertPathValidatorException.BasicReason import java.security.cert.CertificateFactory import java.security.cert.PKIXParameters +import java.security.cert.PKIXReason import java.security.cert.TrustAnchor import java.security.cert.X509Certificate import java.util.Date @@ -43,7 +44,21 @@ internal class X509CertChainValidator(certAnchor: Certificate, date: Date? = nul return false } - PATH_VALIDATOR.validate(X509FACTORY.generateCertPath(x509Certs), pkixParams) + try { + PATH_VALIDATOR.validate(X509FACTORY.generateCertPath(x509Certs), pkixParams) + } catch (e: CertPathValidatorException) { + val reason = e.reason + // https://docs.oracle.com/javase/8/docs/api/java/security/cert/PKIXReason.html + // https://docs.oracle.com/javase/8/docs/api/java/security/cert/CertPathValidatorException.BasicReason.html + when (reason) { + BasicReason.EXPIRED -> throw ExpiredCertificateException("Expired Trifle certificate", e) + BasicReason.INVALID_SIGNATURE -> throw IncorrectSignatureException("Invalid Trifle signature", e) + PKIXReason.NO_TRUST_ANCHOR -> throw NoTrustAnchorException("No acceptable Trifle trust anchor found", e) + else -> throw UnSpecifiedFailureException("Unspecified Trifle verification failure", e) + } + } catch (e: Exception) { + throw UnSpecifiedFailureException("Unspecified Trifle verification failure", e) + } return true } diff --git a/jvm/src/test/kotlin/app/cash/trifle/TrifleTest.kt b/jvm/src/test/kotlin/app/cash/trifle/TrifleTest.kt index 002aa53..285c08f 100644 --- a/jvm/src/test/kotlin/app/cash/trifle/TrifleTest.kt +++ b/jvm/src/test/kotlin/app/cash/trifle/TrifleTest.kt @@ -205,7 +205,9 @@ internal class TrifleTest { @Test fun `test no data extracted with bad verification`() { - assertNull(signedData.verifyAndExtract(TestCertificateAuthority().rootCertificate)) + assertThrows { + signedData.verifyAndExtract(TestCertificateAuthority().rootCertificate) + } } }