diff --git a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt index d61e0179dd..dbcdc531ba 100644 --- a/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt +++ b/client/jackson/src/test/kotlin/net/corda/client/jackson/JacksonSupportTest.kt @@ -202,7 +202,7 @@ class JacksonSupportTest(@Suppress("unused") private val name: String, factory: fun DigitalSignatureWithCert() { val digitalSignature = DigitalSignatureWithCert(MINI_CORP.identity.certificate, secureRandomBytes(128)) val json = mapper.valueToTree(digitalSignature) - val (by, bytes) = json.assertHasOnlyFields("by", "bytes") + val (by, bytes) = json.assertHasOnlyFields("by", "bytes", "parentCertsChain") assertThat(by.valueAs(mapper)).isEqualTo(MINI_CORP.identity.certificate) assertThat(bytes.binaryValue()).isEqualTo(digitalSignature.bytes) assertThat(mapper.convertValue(json)).isEqualTo(digitalSignature) diff --git a/core/src/main/kotlin/net/corda/core/internal/DigitalSignatureWithCert.kt b/core/src/main/kotlin/net/corda/core/internal/DigitalSignatureWithCert.kt index d9414f0c18..6a76a22454 100644 --- a/core/src/main/kotlin/net/corda/core/internal/DigitalSignatureWithCert.kt +++ b/core/src/main/kotlin/net/corda/core/internal/DigitalSignatureWithCert.kt @@ -4,26 +4,46 @@ import net.corda.core.crypto.DigitalSignature import net.corda.core.crypto.SignedData import net.corda.core.crypto.verify import net.corda.core.serialization.CordaSerializable +import net.corda.core.serialization.DeprecatedConstructorForDeserialization import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.deserialize import net.corda.core.utilities.OpaqueBytes -import java.security.cert.CertPath -import java.security.cert.X509Certificate +import java.security.cert.* // TODO: Rename this to DigitalSignature.WithCert once we're happy for it to be public API. The methods will need documentation // and the correct exceptions will be need to be annotated -/** A digital signature with attached certificate of the public key. */ -open class DigitalSignatureWithCert(val by: X509Certificate, bytes: ByteArray) : DigitalSignature(bytes) { +/** A digital signature with attached certificate of the public key and (optionally) the remaining chain of the certificates from the certificate path. */ +class DigitalSignatureWithCert(val by: X509Certificate, val parentCertsChain: List, bytes: ByteArray) : DigitalSignature(bytes) { + @DeprecatedConstructorForDeserialization(1) + constructor(by: X509Certificate, bytes: ByteArray) : this(by, emptyList(), bytes) + + /* The difference between this and the master branch with respect to the implementation of the following two properties + * is due to the missing changes in the JSON/YAML serialization framework. In the master branch the following are + * expressed as properties instead of functions. + */ + fun fullCertChain(): List = listOf(by) + parentCertsChain + fun fullCertPath(): CertPath = CertificateFactory.getInstance("X.509").generateCertPath(fullCertChain()) + fun verify(content: ByteArray): Boolean = by.publicKey.verify(content, this) fun verify(content: OpaqueBytes): Boolean = verify(content.bytes) -} -/** - * A digital signature with attached certificate path. The first certificate in the path corresponds to the data signer key. - * @param path certificate path associated with this signature - * @param bytes signature bytes - */ -class DigitalSignatureWithCertPath(val path: List, bytes: ByteArray): DigitalSignatureWithCert(path.first(), bytes) + init { + if (parentCertsChain.isNotEmpty()) { + val parameters = PKIXParameters(setOf(TrustAnchor(parentCertsChain.last(), null))).apply { isRevocationEnabled = false } + try { + CertPathValidator.getInstance("PKIX").validate(fullCertPath(), parameters) + } catch (e: CertPathValidatorException) { + throw IllegalArgumentException( + """Cert path failed to validate. +Reason: ${e.reason} +Offending cert index: ${e.index} +Cert path: ${fullCertPath()} +""", e) + } + } + + } +} /** Similar to [SignedData] but instead of just attaching the public key, the certificate for the key is attached instead. */ @CordaSerializable diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt index b3c9f32edf..cc6c39881e 100644 --- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt +++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/network/NetworkMap.kt @@ -2,7 +2,6 @@ package net.corda.nodeapi.internal.network import net.corda.core.crypto.SecureHash import net.corda.core.internal.CertRole -import net.corda.core.internal.DigitalSignatureWithCertPath import net.corda.core.internal.SignedDataWithCert import net.corda.core.node.NetworkParameters import net.corda.core.node.NodeInfo @@ -57,9 +56,10 @@ data class ParametersUpdate( /** Verify that a Network Map certificate path and its [CertRole] is correct. */ fun SignedDataWithCert.verifiedNetworkMapCert(rootCert: X509Certificate): T { require(CertRole.extract(sig.by) == CertRole.NETWORK_MAP) { "Incorrect cert role: ${CertRole.extract(sig.by)}" } - val path = when (this.sig) { - is DigitalSignatureWithCertPath -> (sig as DigitalSignatureWithCertPath).path - else -> listOf(sig.by, rootCert) + val path = if (sig.parentCertsChain.isEmpty()) { + listOf(sig.by, rootCert) + } else { + sig.fullCertChain() } X509Utilities.validateCertificateChain(rootCert, path) return verified()