mirror of
https://github.com/corda/corda.git
synced 2025-06-17 06:38:21 +00:00
Creating a PartyAndCertificate only requires a CertPath
This commit is contained in:
@ -2,6 +2,7 @@ package net.corda.node.internal
|
||||
|
||||
import com.codahale.metrics.MetricRegistry
|
||||
import com.google.common.annotations.VisibleForTesting
|
||||
import com.google.common.collect.Lists
|
||||
import com.google.common.collect.MutableClassToInstanceMap
|
||||
import com.google.common.util.concurrent.MoreExecutors
|
||||
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner
|
||||
@ -66,7 +67,6 @@ import net.corda.node.utilities.*
|
||||
import net.corda.node.utilities.AddOrRemove.ADD
|
||||
import org.apache.activemq.artemis.utils.ReusableLatch
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.slf4j.Logger
|
||||
import rx.Observable
|
||||
import java.io.IOException
|
||||
@ -88,6 +88,9 @@ import java.util.concurrent.ExecutorService
|
||||
import java.util.concurrent.TimeUnit.SECONDS
|
||||
import java.util.stream.Collectors.toList
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.component1
|
||||
import kotlin.collections.component2
|
||||
import kotlin.collections.set
|
||||
import kotlin.reflect.KClass
|
||||
import net.corda.core.crypto.generateKeyPair as cryptoGenerateKeyPair
|
||||
|
||||
@ -417,8 +420,9 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
checkpointStorage = DBCheckpointStorage()
|
||||
_services = ServiceHubInternalImpl()
|
||||
attachments = NodeAttachmentService(configuration.dataSourceProperties, services.monitoringService.metrics, configuration.database)
|
||||
network = makeMessagingService()
|
||||
info = makeInfo()
|
||||
val legalIdentity = obtainIdentity("identity", configuration.myLegalName)
|
||||
network = makeMessagingService(legalIdentity)
|
||||
info = makeInfo(legalIdentity)
|
||||
|
||||
val tokenizableServices = mutableListOf(attachments, network, services.vaultService, services.vaultQueryService,
|
||||
services.keyManagementService, services.identityService, platformClock, services.schedulerService)
|
||||
@ -486,12 +490,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
HibernateObserver(services.vaultService.rawUpdates, HibernateConfiguration(services.schemaService, configuration.database ?: Properties(), {services.identityService}))
|
||||
}
|
||||
|
||||
private fun makeInfo(): NodeInfo {
|
||||
private fun makeInfo(legalIdentity: PartyAndCertificate): NodeInfo {
|
||||
val advertisedServiceEntries = makeServiceEntries()
|
||||
val legalIdentity = obtainLegalIdentity()
|
||||
val allIdentitiesSet = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet()
|
||||
val allIdentities = (advertisedServiceEntries.map { it.identity } + legalIdentity).toNonEmptySet()
|
||||
val addresses = myAddresses() // TODO There is no support for multiple IP addresses yet.
|
||||
return NodeInfo(addresses, legalIdentity, allIdentitiesSet, platformVersion, advertisedServiceEntries, findMyLocation())
|
||||
return NodeInfo(addresses, legalIdentity, allIdentities, platformVersion, advertisedServiceEntries, findMyLocation())
|
||||
}
|
||||
|
||||
/**
|
||||
@ -502,7 +505,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
return advertisedServices.map {
|
||||
val serviceId = it.type.id
|
||||
val serviceName = it.name ?: X500Name("${configuration.myLegalName},OU=$serviceId")
|
||||
val identity = obtainKeyPair(serviceId, serviceName).first
|
||||
val identity = obtainIdentity(serviceId, serviceName)
|
||||
ServiceEntry(it, identity)
|
||||
}
|
||||
}
|
||||
@ -613,8 +616,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
val instant = platformClock.instant()
|
||||
val expires = instant + NetworkMapService.DEFAULT_EXPIRATION_PERIOD
|
||||
val reg = NodeRegistration(info, instant.toEpochMilli(), ADD, expires)
|
||||
val legalIdentityKey = obtainLegalIdentityKey()
|
||||
val request = RegistrationRequest(reg.toWire(services.keyManagementService, legalIdentityKey.public), network.myAddress)
|
||||
val request = RegistrationRequest(reg.toWire(services.keyManagementService, info.legalIdentityAndCert.owningKey), network.myAddress)
|
||||
return network.sendRequest(NetworkMapService.REGISTER_TOPIC, request, networkMapAddress)
|
||||
}
|
||||
|
||||
@ -680,15 +682,11 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
runOnStop.clear()
|
||||
}
|
||||
|
||||
protected abstract fun makeMessagingService(): MessagingService
|
||||
protected abstract fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService
|
||||
|
||||
protected abstract fun startMessagingService(rpcOps: RPCOps)
|
||||
|
||||
protected fun obtainLegalIdentity(): PartyAndCertificate = identityKeyPair.first
|
||||
protected fun obtainLegalIdentityKey(): KeyPair = identityKeyPair.second
|
||||
private val identityKeyPair by lazy { obtainKeyPair("identity", configuration.myLegalName) }
|
||||
|
||||
private fun obtainKeyPair(serviceId: String, serviceName: X500Name): Pair<PartyAndCertificate, KeyPair> {
|
||||
private fun obtainIdentity(id: String, name: X500Name): PartyAndCertificate {
|
||||
// Load the private identity key, creating it if necessary. The identity key is a long term well known key that
|
||||
// is distributed to other peers and we use it (or a key signed by it) when we need to do something
|
||||
// "permissioned". The identity file is what gets distributed and contains the node's legal name along with
|
||||
@ -697,50 +695,52 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
|
||||
// TODO: Integrate with Key management service?
|
||||
val keyStore = KeyStoreWrapper(configuration.nodeKeystore, configuration.keyStorePassword)
|
||||
val privateKeyAlias = "$serviceId-private-key"
|
||||
val compositeKeyAlias = "$serviceId-composite-key"
|
||||
val privateKeyAlias = "$id-private-key"
|
||||
val compositeKeyAlias = "$id-composite-key"
|
||||
|
||||
if (!keyStore.containsAlias(privateKeyAlias)) {
|
||||
val privKeyFile = configuration.baseDirectory / privateKeyAlias
|
||||
val pubIdentityFile = configuration.baseDirectory / "$serviceId-public"
|
||||
val pubIdentityFile = configuration.baseDirectory / "$id-public"
|
||||
val compositeKeyFile = configuration.baseDirectory / compositeKeyAlias
|
||||
// TODO: Remove use of [ServiceIdentityGenerator.generateToDisk].
|
||||
// Get keys from key file.
|
||||
// TODO: this is here to smooth out the key storage transition, remove this migration in future release.
|
||||
if (privKeyFile.exists()) {
|
||||
migrateKeysFromFile(keyStore, serviceName, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias)
|
||||
migrateKeysFromFile(keyStore, name, pubIdentityFile, privKeyFile, compositeKeyFile, privateKeyAlias, compositeKeyAlias)
|
||||
} else {
|
||||
log.info("$privateKeyAlias not found in keystore ${configuration.nodeKeystore}, generating fresh key!")
|
||||
keyStore.saveNewKeyPair(serviceName, privateKeyAlias, generateKeyPair())
|
||||
log.info("$privateKeyAlias not found in key store ${configuration.nodeKeystore}, generating fresh key!")
|
||||
keyStore.saveNewKeyPair(name, privateKeyAlias, generateKeyPair())
|
||||
}
|
||||
}
|
||||
|
||||
val (cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
// Get keys from keystore.
|
||||
val loadedServiceName = cert.subject
|
||||
if (loadedServiceName != serviceName)
|
||||
throw ConfigurationException("The legal name in the config file doesn't match the stored identity keystore:$serviceName vs $loadedServiceName")
|
||||
val (x509Cert, keys) = keyStore.certificateAndKeyPair(privateKeyAlias)
|
||||
|
||||
// Use composite key instead if exists
|
||||
// TODO: Use configuration to indicate composite key should be used instead of public key for the identity.
|
||||
val (keyPair, certs) = if (keyStore.containsAlias(compositeKeyAlias)) {
|
||||
val compositeKey = Crypto.toSupportedPublicKey(keyStore.getCertificate(compositeKeyAlias).publicKey)
|
||||
val compositeKeyCert = keyStore.getCertificate(compositeKeyAlias)
|
||||
val certificates = if (keyStore.containsAlias(compositeKeyAlias)) {
|
||||
// Use composite key instead if it exists
|
||||
val certificate = keyStore.getCertificate(compositeKeyAlias)
|
||||
// We have to create the certificate chain for the composite key manually, this is because in order to store
|
||||
// the chain in keystore we need a private key, however there are no corresponding private key for composite key.
|
||||
Pair(KeyPair(compositeKey, keys.private), listOf(compositeKeyCert, *keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)))
|
||||
// the chain in key store we need a private key, however there is no corresponding private key for the composite key.
|
||||
Lists.asList(certificate, keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA))
|
||||
} else {
|
||||
Pair(keys, keyStore.getCertificateChain(privateKeyAlias).toList())
|
||||
keyStore.getCertificateChain(privateKeyAlias).let {
|
||||
check(it[0].toX509CertHolder() == x509Cert) { "Certificates from key store do not line up!" }
|
||||
it.asList()
|
||||
}
|
||||
}
|
||||
val certPath = CertificateFactory.getInstance("X509").generateCertPath(certs)
|
||||
|
||||
val subject = certificates[0].toX509CertHolder().subject
|
||||
if (subject != name)
|
||||
throw ConfigurationException("The name for $id doesn't match what's in the key store: $name vs $subject")
|
||||
|
||||
partyKeys += keys
|
||||
return Pair(PartyAndCertificate(loadedServiceName, keyPair.public, X509CertificateHolder(certs.first().encoded), certPath), keyPair)
|
||||
return PartyAndCertificate(CertificateFactory.getInstance("X509").generateCertPath(certificates))
|
||||
}
|
||||
|
||||
private fun migrateKeysFromFile(keyStore: KeyStoreWrapper, serviceName: X500Name,
|
||||
pubKeyFile: Path, privKeyFile: Path, compositeKeyFile:Path,
|
||||
privateKeyAlias: String, compositeKeyAlias: String) {
|
||||
log.info("Migrating $privateKeyAlias from file to keystore...")
|
||||
log.info("Migrating $privateKeyAlias from file to key store...")
|
||||
// Check that the identity in the config file matches the identity file we have stored to disk.
|
||||
// Load the private key.
|
||||
val publicKey = Crypto.decodePublicKey(pubKeyFile.readAll())
|
||||
@ -753,13 +753,6 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
|
||||
log.info("Finish migrating $privateKeyAlias from file to keystore.")
|
||||
}
|
||||
|
||||
private fun getTestPartyAndCertificate(party: Party, trustRoot: CertificateAndKeyPair): PartyAndCertificate {
|
||||
val certFactory = CertificateFactory.getInstance("X509")
|
||||
val certHolder = X509Utilities.createCertificate(CertificateType.IDENTITY, trustRoot.certificate, trustRoot.keyPair, party.name, party.owningKey)
|
||||
val certPath = certFactory.generateCertPath(listOf(certHolder.cert, trustRoot.certificate.cert))
|
||||
return PartyAndCertificate(party, certHolder, certPath)
|
||||
}
|
||||
|
||||
protected open fun generateKeyPair() = cryptoGenerateKeyPair()
|
||||
|
||||
private inner class ServiceHubInternalImpl : ServiceHubInternal, SingletonSerializeAsToken() {
|
||||
|
@ -2,6 +2,7 @@ package net.corda.node.internal
|
||||
|
||||
import com.codahale.metrics.JmxReporter
|
||||
import net.corda.core.concurrent.CordaFuture
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.concurrent.doneFuture
|
||||
import net.corda.core.internal.concurrent.flatMap
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
@ -133,7 +134,7 @@ open class Node(override val configuration: FullNodeConfiguration,
|
||||
|
||||
private lateinit var userService: RPCUserService
|
||||
|
||||
override fun makeMessagingService(): MessagingService {
|
||||
override fun makeMessagingService(legalIdentity: PartyAndCertificate): MessagingService {
|
||||
userService = RPCUserServiceImpl(configuration.rpcUsers)
|
||||
|
||||
val (serverAddress, advertisedAddress) = with(configuration) {
|
||||
@ -147,7 +148,7 @@ open class Node(override val configuration: FullNodeConfiguration,
|
||||
|
||||
printBasicNodeInfo("Incoming connection address", advertisedAddress.toString())
|
||||
|
||||
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null
|
||||
val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) legalIdentity.owningKey else null
|
||||
return NodeMessagingClient(
|
||||
configuration,
|
||||
versionInfo,
|
||||
|
@ -7,7 +7,9 @@ import net.corda.core.identity.AbstractParty
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.core.serialization.SingletonSerializeAsToken
|
||||
import net.corda.core.utilities.loggerFor
|
||||
import net.corda.core.utilities.trace
|
||||
@ -16,16 +18,13 @@ import org.bouncycastle.cert.X509CertificateHolder
|
||||
import java.security.InvalidAlgorithmParameterException
|
||||
import java.security.PublicKey
|
||||
import java.security.cert.*
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.annotation.concurrent.ThreadSafe
|
||||
import kotlin.collections.LinkedHashSet
|
||||
|
||||
/**
|
||||
* Simple identity service which caches parties and provides functionality for efficient lookup.
|
||||
*
|
||||
* @param identities initial set of identities for the service, typically only used for unit tests.
|
||||
* @param certPaths initial set of certificate paths for the service, typically only used for unit tests.
|
||||
*/
|
||||
@ThreadSafe
|
||||
class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptySet(),
|
||||
@ -43,7 +42,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
* Certificate store for certificate authority and intermediary certificates.
|
||||
*/
|
||||
override val caCertStore: CertStore
|
||||
override val trustRootHolder = X509CertificateHolder(trustRoot.encoded)
|
||||
override val trustRootHolder = trustRoot.toX509CertHolder()
|
||||
override val trustAnchor: TrustAnchor = TrustAnchor(trustRoot, null)
|
||||
private val keyToParties = ConcurrentHashMap<PublicKey, PartyAndCertificate>()
|
||||
private val principalToParties = ConcurrentHashMap<X500Name, PartyAndCertificate>()
|
||||
@ -54,7 +53,6 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
keyToParties.putAll(identities.associateBy { it.owningKey } )
|
||||
principalToParties.putAll(identities.associateBy { it.name })
|
||||
confidentialIdentities.forEach { identity ->
|
||||
require(identity.certPath.certificates.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
||||
principalToParties.computeIfAbsent(identity.name) { identity }
|
||||
}
|
||||
}
|
||||
@ -66,13 +64,10 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
// TODO: Check the certificate validation logic
|
||||
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
|
||||
override fun verifyAndRegisterIdentity(identity: PartyAndCertificate): PartyAndCertificate? {
|
||||
require(identity.certPath.certificates.size >= 2) { "Certificate path must at least include subject and issuing certificates" }
|
||||
// Validate the chain first, before we do anything clever with it
|
||||
identity.verify(trustAnchor)
|
||||
|
||||
log.trace { "Registering identity $identity" }
|
||||
require(Arrays.equals(identity.certificate.subjectPublicKeyInfo.encoded, identity.owningKey.encoded)) { "Party certificate must end with party's public key" }
|
||||
|
||||
keyToParties[identity.owningKey] = identity
|
||||
// Always keep the first party we registered, as that's the well known identity
|
||||
principalToParties.computeIfAbsent(identity.name) { identity }
|
||||
@ -83,7 +78,7 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
override fun certificateFromParty(party: Party): PartyAndCertificate = principalToParties[party.name] ?: throw IllegalArgumentException("Unknown identity ${party.name}")
|
||||
|
||||
// We give the caller a copy of the data set to avoid any locking problems
|
||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = java.util.ArrayList(keyToParties.values)
|
||||
override fun getAllIdentities(): Iterable<PartyAndCertificate> = ArrayList(keyToParties.values)
|
||||
|
||||
override fun partyFromKey(key: PublicKey): Party? = keyToParties[key]?.party
|
||||
override fun partyFromX500Name(principal: X500Name): Party? = principalToParties[principal]?.party
|
||||
@ -128,13 +123,13 @@ class InMemoryIdentityService(identities: Iterable<PartyAndCertificate> = emptyS
|
||||
return results
|
||||
}
|
||||
|
||||
@Throws(IdentityService.UnknownAnonymousPartyException::class)
|
||||
@Throws(UnknownAnonymousPartyException::class)
|
||||
override fun assertOwnership(party: Party, anonymousParty: AnonymousParty) {
|
||||
val path = keyToParties[anonymousParty.owningKey]?.certPath ?: throw IdentityService.UnknownAnonymousPartyException("Unknown anonymous party ${anonymousParty.owningKey.toStringShort()}")
|
||||
require(path.certificates.size > 1) { "Certificate path must contain at least two certificates" }
|
||||
val actual = path.certificates[1]
|
||||
require(actual is X509Certificate && actual.publicKey == party.owningKey) { "Next certificate in the path must match the party key ${party.owningKey.toStringShort()}." }
|
||||
val target = path.certificates.first()
|
||||
require(target is X509Certificate && target.publicKey == anonymousParty.owningKey) { "Certificate path starts with a certificate for the anonymous party" }
|
||||
val anonymousIdentity = keyToParties[anonymousParty.owningKey] ?:
|
||||
throw UnknownAnonymousPartyException("Unknown $anonymousParty")
|
||||
val issuingCert = anonymousIdentity.certPath.certificates[1]
|
||||
require(issuingCert.publicKey == party.owningKey) {
|
||||
"Issuing certificate's public key must match the party key ${party.owningKey.toStringShort()}."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.corda.node.services.keys
|
||||
import net.corda.core.crypto.ContentSignerBuilder
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.cert
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.utilities.days
|
||||
@ -35,10 +34,11 @@ fun freshCertificate(identityService: IdentityService,
|
||||
revocationEnabled: Boolean = false): PartyAndCertificate {
|
||||
val issuerCertificate = issuer.certificate
|
||||
val window = X509Utilities.getCertificateValidityWindow(Duration.ZERO, 3650.days, issuerCertificate)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject, issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val ourCertificate = X509Utilities.createCertificate(CertificateType.IDENTITY, issuerCertificate.subject,
|
||||
issuerSigner, issuer.name, subjectPublicKey, window)
|
||||
val certFactory = CertificateFactory.getInstance("X509")
|
||||
val ourCertPath = certFactory.generateCertPath(listOf(ourCertificate.cert) + issuer.certPath.certificates)
|
||||
val anonymisedIdentity = PartyAndCertificate(Party(issuer.name, subjectPublicKey), ourCertificate, ourCertPath)
|
||||
val anonymisedIdentity = PartyAndCertificate(ourCertPath)
|
||||
identityService.verifyAndRegisterIdentity(anonymisedIdentity)
|
||||
return anonymisedIdentity
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import net.corda.core.internal.ThreadBox
|
||||
import net.corda.core.internal.concurrent.openFuture
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.core.internal.noneOrSingle
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.node.NodeInfo
|
||||
import net.corda.core.node.services.NetworkMapCache
|
||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||
@ -25,7 +26,6 @@ import net.corda.node.services.messaging.NodeLoginModule.Companion.VERIFIER_ROLE
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_TLS
|
||||
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
|
||||
import net.corda.node.utilities.getX509Certificate
|
||||
import net.corda.node.utilities.loadKeyStore
|
||||
import net.corda.nodeapi.*
|
||||
import net.corda.nodeapi.ArtemisMessagingComponent.Companion.NODE_USER
|
||||
@ -52,7 +52,6 @@ import org.apache.activemq.artemis.spi.core.security.jaas.RolePrincipal
|
||||
import org.apache.activemq.artemis.spi.core.security.jaas.UserPrincipal
|
||||
import org.apache.activemq.artemis.utils.ConfigurationHelper
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import rx.Subscription
|
||||
import java.io.IOException
|
||||
import java.math.BigInteger
|
||||
@ -273,12 +272,7 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
|
||||
private fun createArtemisSecurityManager(): ActiveMQJAASSecurityManager {
|
||||
val keyStore = loadKeyStore(config.sslKeystore, config.keyStorePassword)
|
||||
val trustStore = loadKeyStore(config.trustStoreFile, config.trustStorePassword)
|
||||
val ourCertificate = keyStore.getX509Certificate(CORDA_CLIENT_TLS)
|
||||
|
||||
// This is a sanity check and should not fail unless things have been misconfigured
|
||||
require(ourCertificate.subject == config.myLegalName) {
|
||||
"Legal name does not match with our subject CN: ${ourCertificate.subject}"
|
||||
}
|
||||
val defaultCertPolicies = mapOf(
|
||||
PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch,
|
||||
NODE_ROLE to CertificateChainCheckPolicy.LeafMustMatch,
|
||||
@ -512,12 +506,12 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>,
|
||||
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
||||
}
|
||||
// Make sure certificate has the same name.
|
||||
val peerCertificate = X509CertificateHolder(session.peerCertificateChain.first().encoded)
|
||||
val peerCertificate = session.peerCertificateChain[0].toX509CertHolder()
|
||||
require(peerCertificate.subject == expectedLegalName) {
|
||||
"Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " +
|
||||
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
|
||||
}
|
||||
X509Utilities.validateCertificateChain(X509CertificateHolder(session.localCertificates.last().encoded), *session.peerCertificates)
|
||||
X509Utilities.validateCertificateChain(session.localCertificates.last().toX509CertHolder(), *session.peerCertificates)
|
||||
server.onTcpConnection(peerLegalName)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
connection.close()
|
||||
|
@ -21,6 +21,7 @@ import java.util.Collections.synchronizedMap
|
||||
class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformVersion: Int)
|
||||
: AbstractNetworkMapService(services, minimumPlatformVersion) {
|
||||
|
||||
// Only the node_party_path column is needed to reconstruct a PartyAndCertificate but we have the others for human readability
|
||||
private object Table : JDBCHashedTable("${NODE_DATABASE_PREFIX}network_map_nodes") {
|
||||
val nodeParty = partyAndCertificate("node_party_name", "node_party_key", "node_party_certificate", "node_party_path")
|
||||
val registrationInfo = blob("node_registration_info")
|
||||
@ -28,16 +29,15 @@ class PersistentNetworkMapService(services: ServiceHubInternal, minimumPlatformV
|
||||
|
||||
override val nodeRegistrations: MutableMap<PartyAndCertificate, NodeRegistrationInfo> = synchronizedMap(object : AbstractJDBCHashMap<PartyAndCertificate, NodeRegistrationInfo, Table>(Table, loadOnInit = true) {
|
||||
// TODO: We should understand an X500Name database field type, rather than manually doing the conversion ourselves
|
||||
override fun keyFromRow(row: ResultRow): PartyAndCertificate = PartyAndCertificate(X500Name(row[table.nodeParty.name]), row[table.nodeParty.owningKey],
|
||||
row[table.nodeParty.certificate], row[table.nodeParty.certPath])
|
||||
override fun keyFromRow(row: ResultRow): PartyAndCertificate = PartyAndCertificate(row[table.nodeParty.certPath])
|
||||
|
||||
override fun valueFromRow(row: ResultRow): NodeRegistrationInfo = deserializeFromBlob(row[table.registrationInfo])
|
||||
|
||||
override fun addKeyToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
|
||||
insert[table.nodeParty.name] = entry.key.name.toString()
|
||||
insert[table.nodeParty.owningKey] = entry.key.owningKey
|
||||
insert[table.nodeParty.certPath] = entry.key.certPath
|
||||
insert[table.nodeParty.certificate] = entry.key.certificate
|
||||
insert[table.nodeParty.certPath] = entry.key.certPath
|
||||
}
|
||||
|
||||
override fun addValueToInsert(insert: InsertStatement, entry: Map.Entry<PartyAndCertificate, NodeRegistrationInfo>, finalizables: MutableList<() -> Unit>) {
|
||||
|
@ -1,8 +1,11 @@
|
||||
package net.corda.node.utilities
|
||||
|
||||
import net.corda.core.crypto.*
|
||||
import net.corda.core.crypto.CertificateAndKeyPair
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.cert
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.read
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.core.internal.write
|
||||
import org.bouncycastle.asn1.x500.X500Name
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
@ -145,8 +148,8 @@ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): Certi
|
||||
* @return The X509Certificate found in the KeyStore under the specified alias.
|
||||
*/
|
||||
fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
|
||||
val encoded = getCertificate(alias)?.encoded ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
||||
return X509CertificateHolder(encoded)
|
||||
val certificate = getCertificate(alias) ?: throw IllegalArgumentException("No certificate under alias \"$alias\"")
|
||||
return certificate.toX509CertHolder()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -206,5 +209,5 @@ class KeyStoreWrapper(private val storePath: Path, private val storePassword: St
|
||||
|
||||
fun getCertificate(alias: String): Certificate = keyStore.getCertificate(alias)
|
||||
|
||||
fun certificateAndKeyPair(alias: String) = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
||||
fun certificateAndKeyPair(alias: String): CertificateAndKeyPair = keyStore.getCertificateAndKeyPair(alias, storePassword)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import net.corda.core.crypto.generateKeyPair
|
||||
import net.corda.core.identity.AnonymousParty
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.node.services.IdentityService
|
||||
import net.corda.core.node.services.UnknownAnonymousPartyException
|
||||
import net.corda.node.services.identity.InMemoryIdentityService
|
||||
import net.corda.node.utilities.CertificateType
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
@ -90,10 +90,10 @@ class InMemoryIdentityServiceTests {
|
||||
val txKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val service = InMemoryIdentityService(trustRoot = DUMMY_CA.certificate)
|
||||
// TODO: Generate certificate with an EdDSA key rather than ECDSA
|
||||
val identity = Party(CertificateAndKeyPair(rootCert, rootKey))
|
||||
val identity = Party(rootCert)
|
||||
val txIdentity = AnonymousParty(txKey.public)
|
||||
|
||||
assertFailsWith<IdentityService.UnknownAnonymousPartyException> {
|
||||
assertFailsWith<UnknownAnonymousPartyException> {
|
||||
service.assertOwnership(identity, txIdentity)
|
||||
}
|
||||
}
|
||||
@ -107,7 +107,7 @@ class InMemoryIdentityServiceTests {
|
||||
fun `get anonymous identity by key`() {
|
||||
val trustRoot = DUMMY_CA
|
||||
val (alice, aliceTxIdentity) = createParty(ALICE.name, trustRoot)
|
||||
val (bob, bobTxIdentity) = createParty(ALICE.name, trustRoot)
|
||||
val (_, bobTxIdentity) = createParty(ALICE.name, trustRoot)
|
||||
|
||||
// Now we have identities, construct the service and let it know about both
|
||||
val service = InMemoryIdentityService(setOf(alice), emptySet(), trustRoot.certificate.cert)
|
||||
@ -163,7 +163,7 @@ class InMemoryIdentityServiceTests {
|
||||
val txKey = Crypto.generateKeyPair()
|
||||
val txCert = X509Utilities.createCertificate(CertificateType.IDENTITY, issuer.certificate, issuerKeyPair, x500Name, txKey.public)
|
||||
val txCertPath = certFactory.generateCertPath(listOf(txCert.cert) + issuer.certPath.certificates)
|
||||
return Pair(issuer, PartyAndCertificate(Party(x500Name, txKey.public), txCert, txCertPath))
|
||||
return Pair(issuer, PartyAndCertificate(txCertPath))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -9,12 +9,12 @@ import net.corda.core.crypto.cert
|
||||
import net.corda.core.crypto.commonName
|
||||
import net.corda.core.internal.exists
|
||||
import net.corda.core.internal.toTypedArray
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
import net.corda.node.utilities.loadKeyStore
|
||||
import net.corda.testing.ALICE
|
||||
import net.corda.testing.getTestX509Name
|
||||
import net.corda.testing.testNodeConfiguration
|
||||
import org.bouncycastle.cert.X509CertificateHolder
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
@ -68,7 +68,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertFalse(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_CA)
|
||||
assertEquals(3, certificateChain.size)
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { X509CertificateHolder(it.encoded).subject.commonName })
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
|
||||
}
|
||||
|
||||
sslKeystore.run {
|
||||
@ -78,7 +78,7 @@ class NetworkRegistrationHelperTest {
|
||||
assertTrue(containsAlias(X509Utilities.CORDA_CLIENT_TLS))
|
||||
val certificateChain = getCertificateChain(X509Utilities.CORDA_CLIENT_TLS)
|
||||
assertEquals(4, certificateChain.size)
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { X509CertificateHolder(it.encoded).subject.commonName })
|
||||
assertEquals(listOf("CORDA_CLIENT_CA", "CORDA_CLIENT_CA", "CORDA_INTERMEDIATE_CA", "CORDA_ROOT_CA"), certificateChain.map { it.toX509CertHolder().subject.commonName })
|
||||
}
|
||||
|
||||
trustStore.run {
|
||||
|
Reference in New Issue
Block a user