mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
CORDA-3201 - Enforce separate key for notary identity (#6308)
This commit is contained in:
parent
e6d5842a23
commit
a500084d38
@ -1435,7 +1435,7 @@
|
|||||||
<ID>ThrowsCount:JarScanningCordappLoader.kt$JarScanningCordappLoader$private fun parseVersion(versionStr: String?, attributeName: String): Int</ID>
|
<ID>ThrowsCount:JarScanningCordappLoader.kt$JarScanningCordappLoader$private fun parseVersion(versionStr: String?, attributeName: String): Int</ID>
|
||||||
<ID>ThrowsCount:LedgerDSLInterpreter.kt$Verifies$ fun failsWith(expectedMessage: String?): EnforceVerifyOrFail</ID>
|
<ID>ThrowsCount:LedgerDSLInterpreter.kt$Verifies$ fun failsWith(expectedMessage: String?): EnforceVerifyOrFail</ID>
|
||||||
<ID>ThrowsCount:MockServices.kt$ fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T</ID>
|
<ID>ThrowsCount:MockServices.kt$ fun <T : SerializeAsToken> createMockCordaService(serviceHub: MockServices, serviceConstructor: (AppServiceHub) -> T): T</ID>
|
||||||
<ID>ThrowsCount:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$private fun validateCertificates(registeringPublicKey: PublicKey, certificates: List<X509Certificate>)</ID>
|
<ID>ThrowsCount:NetworkRegistrationHelper.kt$NetworkRegistrationHelper$private fun validateCertificates( registeringPublicKey: PublicKey, registeringLegalName: CordaX500Name, expectedCertRole: CertRole, certificates: List<X509Certificate> )</ID>
|
||||||
<ID>ThrowsCount:NodeInfoFilesCopier.kt$NodeInfoFilesCopier$private fun atomicCopy(source: Path, destination: Path)</ID>
|
<ID>ThrowsCount:NodeInfoFilesCopier.kt$NodeInfoFilesCopier$private fun atomicCopy(source: Path, destination: Path)</ID>
|
||||||
<ID>ThrowsCount:NodeVaultService.kt$NodeVaultService$@Throws(VaultQueryException::class) private fun <T : ContractState> _queryBy(criteria: QueryCriteria, paging_: PageSpecification, sorting: Sort, contractStateType: Class<out T>, skipPagingChecks: Boolean): Vault.Page<T></ID>
|
<ID>ThrowsCount:NodeVaultService.kt$NodeVaultService$@Throws(VaultQueryException::class) private fun <T : ContractState> _queryBy(criteria: QueryCriteria, paging_: PageSpecification, sorting: Sort, contractStateType: Class<out T>, skipPagingChecks: Boolean): Vault.Page<T></ID>
|
||||||
<ID>ThrowsCount:NodeVaultService.kt$NodeVaultService$private fun makeUpdates(batch: Iterable<CoreTransaction>, statesToRecord: StatesToRecord, previouslySeen: Boolean): List<Vault.Update<ContractState>></ID>
|
<ID>ThrowsCount:NodeVaultService.kt$NodeVaultService$private fun makeUpdates(batch: Iterable<CoreTransaction>, statesToRecord: StatesToRecord, previouslySeen: Boolean): List<Vault.Update<ContractState>></ID>
|
||||||
|
@ -614,11 +614,22 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
val myNotaryIdentity = configuration.notary?.let {
|
val myNotaryIdentity = configuration.notary?.let {
|
||||||
if (it.serviceLegalName != null) {
|
if (it.serviceLegalName != null) {
|
||||||
val (notaryIdentity, notaryIdentityKeyPair) = loadNotaryClusterIdentity(it.serviceLegalName)
|
val (notaryIdentity, notaryIdentityKeyPair) = loadNotaryServiceIdentity(it.serviceLegalName)
|
||||||
keyPairs += notaryIdentityKeyPair
|
keyPairs += notaryIdentityKeyPair
|
||||||
notaryIdentity
|
notaryIdentity
|
||||||
} else {
|
} else {
|
||||||
// In case of a single notary service myNotaryIdentity will be the node's single identity.
|
// The only case where the myNotaryIdentity will be the node's legal identity is for existing single notary services running
|
||||||
|
// an older version. Current single notary services (V4.6+) sign requests using a separate notary service identity so the
|
||||||
|
// notary identity will be different from the node's legal identity.
|
||||||
|
|
||||||
|
// This check is here to ensure that a user does not accidentally/intentionally remove the serviceLegalName configuration
|
||||||
|
// parameter after a notary has been registered. If that was possible then notary would start and sign incoming requests
|
||||||
|
// with the node's legal identity key, corrupting the data.
|
||||||
|
check (!cryptoService.containsKey(DISTRIBUTED_NOTARY_KEY_ALIAS)) {
|
||||||
|
"The notary service key exists in the key store but no notary service legal name has been configured. " +
|
||||||
|
"Either include the relevant 'notary.serviceLegalName' configuration or validate this key is not necessary " +
|
||||||
|
"and remove from the key store."
|
||||||
|
}
|
||||||
identity
|
identity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1057,8 +1068,12 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Loads pre-generated notary service cluster identity. */
|
/**
|
||||||
private fun loadNotaryClusterIdentity(serviceLegalName: CordaX500Name): Pair<PartyAndCertificate, KeyPair> {
|
* Loads notary service identity. In the case of the experimental RAFT and BFT notary clusters, this loads the pre-generated
|
||||||
|
* cluster identity that all worker nodes share. In the case of a simple single notary, this loads the notary service identity
|
||||||
|
* that is generated during initial registration and is used to sign notarisation requests.
|
||||||
|
* */
|
||||||
|
private fun loadNotaryServiceIdentity(serviceLegalName: CordaX500Name): Pair<PartyAndCertificate, KeyPair> {
|
||||||
val privateKeyAlias = "$DISTRIBUTED_NOTARY_KEY_ALIAS"
|
val privateKeyAlias = "$DISTRIBUTED_NOTARY_KEY_ALIAS"
|
||||||
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS"
|
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS"
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ fun NodeConfiguration.shouldInitCrashShell() = shouldStartLocalShell() || should
|
|||||||
data class NotaryConfig(
|
data class NotaryConfig(
|
||||||
/** Specifies whether the notary validates transactions or not. */
|
/** Specifies whether the notary validates transactions or not. */
|
||||||
val validating: Boolean,
|
val validating: Boolean,
|
||||||
/** The legal name of cluster in case of a distributed notary service. */
|
/** The legal name of the notary service identity. */
|
||||||
val serviceLegalName: CordaX500Name? = null,
|
val serviceLegalName: CordaX500Name? = null,
|
||||||
/** The name of the notary service class to load. */
|
/** The name of the notary service class to load. */
|
||||||
val className: String? = null,
|
val className: String? = null,
|
||||||
|
@ -25,10 +25,10 @@ import org.bouncycastle.operator.ContentSigner
|
|||||||
import org.bouncycastle.util.io.pem.PemObject
|
import org.bouncycastle.util.io.pem.PemObject
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.io.StringWriter
|
import java.io.StringWriter
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import java.net.ConnectException
|
import java.net.ConnectException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
@ -63,6 +63,7 @@ open class NetworkRegistrationHelper(
|
|||||||
private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
|
private val requestIdStore = certificatesDirectory / "certificate-request-id.txt"
|
||||||
protected val rootTrustStore: X509KeyStore
|
protected val rootTrustStore: X509KeyStore
|
||||||
protected val rootCert: X509Certificate
|
protected val rootCert: X509Certificate
|
||||||
|
private val notaryServiceConfig: NotaryServiceConfig? = config.notaryServiceConfig
|
||||||
|
|
||||||
init {
|
init {
|
||||||
require(networkRootTrustStorePath.exists()) {
|
require(networkRootTrustStorePath.exists()) {
|
||||||
@ -95,34 +96,70 @@ open class NetworkRegistrationHelper(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notaryServiceConfig?.let { validateNotaryServiceKeyAndCert(certStore, it.notaryServiceKeyAlias, it.notaryServiceLegalName) }
|
||||||
|
|
||||||
val tlsCrlIssuerCert = getTlsCrlIssuerCert()
|
val tlsCrlIssuerCert = getTlsCrlIssuerCert()
|
||||||
|
|
||||||
// We use SELF_SIGNED_PRIVATE_KEY as progress indicator so we just store a dummy key and cert.
|
// We use SELF_SIGNED_PRIVATE_KEY as progress indicator so we just store a dummy key and cert.
|
||||||
// When registration succeeds, this entry should be deleted.
|
// When registration succeeds, this entry should be deleted.
|
||||||
certStore.query { setPrivateKey(SELF_SIGNED_PRIVATE_KEY, AliasPrivateKey(SELF_SIGNED_PRIVATE_KEY), listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), certificateStore.entryPassword) }
|
certStore.query { setPrivateKey(SELF_SIGNED_PRIVATE_KEY, AliasPrivateKey(SELF_SIGNED_PRIVATE_KEY), listOf(NOT_YET_REGISTERED_MARKER_KEYS_AND_CERTS.ECDSAR1_CERT), certificateStore.entryPassword) }
|
||||||
|
|
||||||
val nodeCaPublicKey = loadOrGenerateKeyPair()
|
val (entityPublicKey, receivedCertificates) = generateKeyPairAndCertificate(nodeCaKeyAlias, myLegalName, certRole, certStore)
|
||||||
|
|
||||||
val requestId = submitOrResumeCertificateSigningRequest(nodeCaPublicKey, cryptoService.getSigner(nodeCaKeyAlias))
|
onSuccess(entityPublicKey, cryptoService.getSigner(nodeCaKeyAlias), receivedCertificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name())
|
||||||
|
|
||||||
val nodeCaCertificates = pollServerForCertificates(requestId)
|
|
||||||
validateCertificates(nodeCaPublicKey, nodeCaCertificates)
|
|
||||||
|
|
||||||
certStore.setCertPathOnly(nodeCaKeyAlias, nodeCaCertificates)
|
|
||||||
certStore.value.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
|
||||||
certStore.value.save()
|
|
||||||
logProgress("Private key '$nodeCaKeyAlias' and its certificate-chain stored successfully.")
|
|
||||||
|
|
||||||
onSuccess(nodeCaPublicKey, cryptoService.getSigner(nodeCaKeyAlias), nodeCaCertificates, tlsCrlIssuerCert?.subjectX500Principal?.toX500Name())
|
|
||||||
// All done, clean up temp files.
|
// All done, clean up temp files.
|
||||||
requestIdStore.deleteIfExists()
|
requestIdStore.deleteIfExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadOrGenerateKeyPair(): PublicKey {
|
private fun generateKeyPairAndCertificate(keyAlias: String, legalName: CordaX500Name, certificateRole: CertRole, certStore: CertificateStore): Pair<PublicKey, List<X509Certificate>> {
|
||||||
return if (cryptoService.containsKey(nodeCaKeyAlias)) {
|
val entityPublicKey = loadOrGenerateKeyPair(keyAlias)
|
||||||
cryptoService.getPublicKey(nodeCaKeyAlias)!!
|
|
||||||
|
val requestId = submitOrResumeCertificateSigningRequest(entityPublicKey, legalName, certificateRole, cryptoService.getSigner(keyAlias))
|
||||||
|
|
||||||
|
val receivedCertificates = pollServerForCertificates(requestId)
|
||||||
|
validateCertificates(entityPublicKey, legalName, certificateRole, receivedCertificates)
|
||||||
|
|
||||||
|
certStore.setCertPathOnly(keyAlias, receivedCertificates)
|
||||||
|
certStore.value.internal.deleteEntry(SELF_SIGNED_PRIVATE_KEY)
|
||||||
|
certStore.value.save()
|
||||||
|
logProgress("Private key '$keyAlias' and its certificate-chain stored successfully.")
|
||||||
|
return Pair(entityPublicKey, receivedCertificates)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used when registering a notary to validate that the shared notary service key and certificate can be accessed.
|
||||||
|
*
|
||||||
|
* In the case that the notary service certificate and key is not available, a new key key is generated and a separate CSR is
|
||||||
|
* submitted to the Identity Manager.
|
||||||
|
*
|
||||||
|
* If this method successfully completes then the [cryptoService] will contain the notary service key and the [certStore] will contain
|
||||||
|
* the notary service certificate chain.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException If the notary service certificate already exists but the private key is not available.
|
||||||
|
*/
|
||||||
|
private fun validateNotaryServiceKeyAndCert(certStore: CertificateStore, notaryServiceKeyAlias: String, notaryServiceLegalName: CordaX500Name) {
|
||||||
|
if (certStore.contains(notaryServiceKeyAlias) && !cryptoService.containsKey(notaryServiceKeyAlias)) {
|
||||||
|
throw IllegalStateException("Notary service identity certificate exists but key pair missing. " +
|
||||||
|
"Please check no old certificates exist in the certificate store.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (certStore.contains(notaryServiceKeyAlias)) {
|
||||||
|
logProgress("Notary service certificate already exists. Continuing with node registration...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logProgress("Generating notary service identity for $notaryServiceLegalName...")
|
||||||
|
generateKeyPairAndCertificate(notaryServiceKeyAlias, notaryServiceLegalName, CertRole.SERVICE_IDENTITY, certStore)
|
||||||
|
// The request id store is reused for the next step - registering the node identity.
|
||||||
|
// Therefore we can remove this to enable it to be reused.
|
||||||
|
requestIdStore.deleteIfExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadOrGenerateKeyPair(keyAlias: String): PublicKey {
|
||||||
|
return if (cryptoService.containsKey(keyAlias)) {
|
||||||
|
cryptoService.getPublicKey(keyAlias)!!
|
||||||
} else {
|
} else {
|
||||||
cryptoService.generateKeyPair(nodeCaKeyAlias, cryptoService.defaultTLSSignatureScheme())
|
cryptoService.generateKeyPair(keyAlias, cryptoService.defaultTLSSignatureScheme())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,26 +174,31 @@ open class NetworkRegistrationHelper(
|
|||||||
return tlsCrlIssuerCert
|
return tlsCrlIssuerCert
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun validateCertificates(registeringPublicKey: PublicKey, certificates: List<X509Certificate>) {
|
private fun validateCertificates(
|
||||||
val nodeCACertificate = certificates.first()
|
registeringPublicKey: PublicKey,
|
||||||
|
registeringLegalName: CordaX500Name,
|
||||||
|
expectedCertRole: CertRole,
|
||||||
|
certificates: List<X509Certificate>
|
||||||
|
) {
|
||||||
|
val receivedCertificate = certificates.first()
|
||||||
|
|
||||||
val nodeCaSubject = try {
|
val certificateSubject = try {
|
||||||
CordaX500Name.build(nodeCACertificate.subjectX500Principal)
|
CordaX500Name.build(receivedCertificate.subjectX500Principal)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
throw CertificateRequestException("Received node CA cert has invalid subject name: ${e.message}")
|
throw CertificateRequestException("Received cert has invalid subject name: ${e.message}")
|
||||||
}
|
}
|
||||||
if (nodeCaSubject != myLegalName) {
|
if (certificateSubject != registeringLegalName) {
|
||||||
throw CertificateRequestException("Subject of received node CA cert doesn't match with node legal name: $nodeCaSubject")
|
throw CertificateRequestException("Subject of received cert doesn't match with legal name: $certificateSubject")
|
||||||
}
|
}
|
||||||
|
|
||||||
val nodeCaCertRole = try {
|
val receivedCertRole = try {
|
||||||
CertRole.extract(nodeCACertificate)
|
CertRole.extract(receivedCertificate)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
throw CertificateRequestException("Unable to extract cert role from received node CA cert: ${e.message}")
|
throw CertificateRequestException("Unable to extract cert role from received cert: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (certRole != nodeCaCertRole) {
|
if (expectedCertRole != receivedCertRole) {
|
||||||
throw CertificateRequestException("Received certificate contains invalid cert role, expected '$certRole', got '$nodeCaCertRole'.")
|
throw CertificateRequestException("Received certificate contains invalid cert role, expected '$expectedCertRole', got '$receivedCertRole'.")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate returned certificate is for the correct public key.
|
// Validate returned certificate is for the correct public key.
|
||||||
@ -169,22 +211,6 @@ open class NetworkRegistrationHelper(
|
|||||||
logProgress("Certificate signing request approved, storing private key with the certificate chain.")
|
logProgress("Certificate signing request approved, storing private key with the certificate chain.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CertificateStore.loadOrCreateKeyPair(alias: String, entryPassword: String = password): KeyPair {
|
|
||||||
// Create or load self signed keypair from the key store.
|
|
||||||
// We use the self sign certificate to store the key temporarily in the keystore while waiting for the request approval.
|
|
||||||
if (alias !in this) {
|
|
||||||
// NODE_CA should be TLS compatible due to the cert hierarchy structure.
|
|
||||||
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
|
||||||
val selfSignCert = X509Utilities.createSelfSignedCACertificate(myLegalName.x500Principal, keyPair)
|
|
||||||
// Save to the key store.
|
|
||||||
with(value) {
|
|
||||||
setPrivateKey(alias, keyPair.private, listOf(selfSignCert), keyPassword = entryPassword)
|
|
||||||
save()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return query { getCertificateAndKeyPair(alias, entryPassword) }.keyPair
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Poll Certificate Signing Server for approved certificate,
|
* Poll Certificate Signing Server for approved certificate,
|
||||||
* enter a slow polling loop if server return null.
|
* enter a slow polling loop if server return null.
|
||||||
@ -226,20 +252,27 @@ open class NetworkRegistrationHelper(
|
|||||||
* Submit Certificate Signing Request to Certificate signing service if request ID not found in file system.
|
* Submit Certificate Signing Request to Certificate signing service if request ID not found in file system.
|
||||||
* New request ID will be stored in requestId.txt
|
* New request ID will be stored in requestId.txt
|
||||||
* @param publicKey public key for which we need a certificate.
|
* @param publicKey public key for which we need a certificate.
|
||||||
|
* @param legalName legal name of the entity for which we need a certificate.
|
||||||
|
* @param certRole desired role of the entities certificate.
|
||||||
* @param contentSigner the [ContentSigner] that will sign the CSR.
|
* @param contentSigner the [ContentSigner] that will sign the CSR.
|
||||||
* @return Request ID return from the server.
|
* @return Request ID return from the server.
|
||||||
*/
|
*/
|
||||||
private fun submitOrResumeCertificateSigningRequest(publicKey: PublicKey, contentSigner: ContentSigner): String {
|
private fun submitOrResumeCertificateSigningRequest(
|
||||||
|
publicKey: PublicKey,
|
||||||
|
legalName: CordaX500Name,
|
||||||
|
certRole: CertRole,
|
||||||
|
contentSigner: ContentSigner
|
||||||
|
): String {
|
||||||
try {
|
try {
|
||||||
// Retrieve request id from file if exists, else post a request to server.
|
// Retrieve request id from file if exists, else post a request to server.
|
||||||
return if (!requestIdStore.exists()) {
|
return if (!requestIdStore.exists()) {
|
||||||
val request = X509Utilities.createCertificateSigningRequest(myLegalName.x500Principal, emailAddress, publicKey, contentSigner, certRole)
|
val request = X509Utilities.createCertificateSigningRequest(legalName.x500Principal, emailAddress, publicKey, contentSigner, certRole)
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
JcaPEMWriter(writer).use {
|
JcaPEMWriter(writer).use {
|
||||||
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
it.writeObject(PemObject("CERTIFICATE REQUEST", request.encoded))
|
||||||
}
|
}
|
||||||
logProgress("Certificate signing request with the following information will be submitted to the Corda certificate signing server.")
|
logProgress("Certificate signing request with the following information will be submitted to the Corda certificate signing server.")
|
||||||
logProgress("Legal Name: $myLegalName")
|
logProgress("Legal Name: $legalName")
|
||||||
logProgress("Email: $emailAddress")
|
logProgress("Email: $emailAddress")
|
||||||
logProgress("Public Key: $publicKey")
|
logProgress("Public Key: $publicKey")
|
||||||
logProgress("$writer")
|
logProgress("$writer")
|
||||||
@ -277,7 +310,8 @@ class NodeRegistrationConfiguration(
|
|||||||
val certificatesDirectory: Path,
|
val certificatesDirectory: Path,
|
||||||
val emailAddress: String,
|
val emailAddress: String,
|
||||||
val cryptoService: CryptoService,
|
val cryptoService: CryptoService,
|
||||||
val certificateStore: CertificateStore) {
|
val certificateStore: CertificateStore,
|
||||||
|
val notaryServiceConfig: NotaryServiceConfig? = null) {
|
||||||
|
|
||||||
constructor(config: NodeConfiguration) : this(
|
constructor(config: NodeConfiguration) : this(
|
||||||
p2pSslOptions = config.p2pSslOptions,
|
p2pSslOptions = config.p2pSslOptions,
|
||||||
@ -287,10 +321,29 @@ class NodeRegistrationConfiguration(
|
|||||||
certificatesDirectory = config.certificatesDirectory,
|
certificatesDirectory = config.certificatesDirectory,
|
||||||
emailAddress = config.emailAddress,
|
emailAddress = config.emailAddress,
|
||||||
cryptoService = BCCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore),
|
cryptoService = BCCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore),
|
||||||
certificateStore = config.signingCertificateStore.get(true)
|
certificateStore = config.signingCertificateStore.get(true),
|
||||||
|
notaryServiceConfig = config.notary?.let {
|
||||||
|
// Validation of the presence of the notary service legal name is only done here and not in the top level configuration
|
||||||
|
// file. This is to maintain backwards compatibility with older notaries using the legacy identity structure. Older
|
||||||
|
// notaries will be signing requests using the nodes legal identity key and therefore no separate notary service entity
|
||||||
|
// exists. Just having the validation here prevents any new notaries from being created with the legacy identity scheme
|
||||||
|
// but still allows drop in JAR replacements for old notaries.
|
||||||
|
requireNotNull(it.serviceLegalName) {
|
||||||
|
"The notary service legal name must be provided via the 'notary.serviceLegalName' configuration parameter"
|
||||||
|
}
|
||||||
|
require(it.serviceLegalName != config.myLegalName) {
|
||||||
|
"The notary service legal name must be different from the node legal name"
|
||||||
|
}
|
||||||
|
NotaryServiceConfig(X509Utilities.DISTRIBUTED_NOTARY_KEY_ALIAS, it.serviceLegalName!!)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class NotaryServiceConfig(
|
||||||
|
val notaryServiceKeyAlias: String,
|
||||||
|
val notaryServiceLegalName: CordaX500Name
|
||||||
|
)
|
||||||
|
|
||||||
class NodeRegistrationException(
|
class NodeRegistrationException(
|
||||||
message: String?,
|
message: String?,
|
||||||
cause: Throwable?
|
cause: Throwable?
|
||||||
|
@ -28,6 +28,8 @@ import net.corda.testing.core.ALICE_NAME
|
|||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
import net.corda.coretesting.internal.rigorousMock
|
import net.corda.coretesting.internal.rigorousMock
|
||||||
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
import net.corda.coretesting.internal.stubs.CertificateStoreStubs
|
||||||
|
import net.corda.node.services.config.NotaryConfig
|
||||||
|
import net.corda.testing.core.DUMMY_NOTARY_NAME
|
||||||
import org.assertj.core.api.Assertions.*
|
import org.assertj.core.api.Assertions.*
|
||||||
import org.bouncycastle.asn1.x509.GeneralName
|
import org.bouncycastle.asn1.x509.GeneralName
|
||||||
import org.bouncycastle.asn1.x509.GeneralSubtree
|
import org.bouncycastle.asn1.x509.GeneralSubtree
|
||||||
@ -37,6 +39,7 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
|||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.security.PublicKey
|
import java.security.PublicKey
|
||||||
import java.security.cert.CertPathValidatorException
|
import java.security.cert.CertPathValidatorException
|
||||||
@ -71,6 +74,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
doReturn(null).whenever(it).tlsCertCrlDistPoint
|
doReturn(null).whenever(it).tlsCertCrlDistPoint
|
||||||
doReturn(null).whenever(it).tlsCertCrlIssuer
|
doReturn(null).whenever(it).tlsCertCrlIssuer
|
||||||
doReturn(true).whenever(it).crlCheckSoftFail
|
doReturn(true).whenever(it).crlCheckSoftFail
|
||||||
|
doReturn(null).whenever(it).notary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +124,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `missing truststore`() {
|
fun `missing truststore`() {
|
||||||
val nodeCaCertPath = createNodeCaCertPath()
|
val nodeCaCertPath = createCertPath()
|
||||||
assertThatThrownBy {
|
assertThatThrownBy {
|
||||||
createFixedResponseRegistrationHelper(nodeCaCertPath)
|
createFixedResponseRegistrationHelper(nodeCaCertPath)
|
||||||
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
}.hasMessageContaining("This file must contain the root CA cert of your compatibility zone. Please contact your CZ operator.")
|
||||||
@ -128,7 +132,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
|
|
||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `node CA with incorrect cert role`() {
|
fun `node CA with incorrect cert role`() {
|
||||||
val nodeCaCertPath = createNodeCaCertPath(type = CertificateType.TLS)
|
val nodeCaCertPath = createCertPath(type = CertificateType.TLS)
|
||||||
saveNetworkTrustStore(CORDA_ROOT_CA to nodeCaCertPath.last())
|
saveNetworkTrustStore(CORDA_ROOT_CA to nodeCaCertPath.last())
|
||||||
val registrationHelper = createFixedResponseRegistrationHelper(nodeCaCertPath)
|
val registrationHelper = createFixedResponseRegistrationHelper(nodeCaCertPath)
|
||||||
assertThatExceptionOfType(CertificateRequestException::class.java)
|
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||||
@ -139,7 +143,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
@Test(timeout=300_000)
|
@Test(timeout=300_000)
|
||||||
fun `node CA with incorrect subject`() {
|
fun `node CA with incorrect subject`() {
|
||||||
val invalidName = CordaX500Name("Foo", "MU", "GB")
|
val invalidName = CordaX500Name("Foo", "MU", "GB")
|
||||||
val nodeCaCertPath = createNodeCaCertPath(legalName = invalidName)
|
val nodeCaCertPath = createCertPath(legalName = invalidName)
|
||||||
saveNetworkTrustStore(CORDA_ROOT_CA to nodeCaCertPath.last())
|
saveNetworkTrustStore(CORDA_ROOT_CA to nodeCaCertPath.last())
|
||||||
val registrationHelper = createFixedResponseRegistrationHelper(nodeCaCertPath)
|
val registrationHelper = createFixedResponseRegistrationHelper(nodeCaCertPath)
|
||||||
assertThatExceptionOfType(CertificateRequestException::class.java)
|
assertThatExceptionOfType(CertificateRequestException::class.java)
|
||||||
@ -220,36 +224,118 @@ class NetworkRegistrationHelperTest {
|
|||||||
createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).generateKeysAndRegister()
|
createRegistrationHelper(rootAndIntermediateCA = rootAndIntermediateCA).generateKeysAndRegister()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createNodeCaCertPath(type: CertificateType = CertificateType.NODE_CA,
|
@Test(timeout=300_000)
|
||||||
legalName: CordaX500Name = nodeLegalName,
|
fun `successful registration for notary node`() {
|
||||||
publicKey: PublicKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME).public,
|
val notaryServiceLegalName = DUMMY_NOTARY_NAME
|
||||||
rootAndIntermediateCA: Pair<CertificateAndKeyPair, CertificateAndKeyPair> = createDevIntermediateCaCertPath()): List<X509Certificate> {
|
val notaryNodeConfig = createNotaryNodeConfiguration(notaryServiceLegalName = notaryServiceLegalName)
|
||||||
|
assertThat(notaryNodeConfig.notary).isNotNull
|
||||||
|
|
||||||
|
val rootAndIntermediateCA = createDevIntermediateCaCertPath().also {
|
||||||
|
saveNetworkTrustStore(CORDA_ROOT_CA to it.first.certificate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock out the registration service to ensure notary service registration is handled correctly
|
||||||
|
createRegistrationHelper(CertRole.NODE_CA, notaryNodeConfig) {
|
||||||
|
when {
|
||||||
|
it.subject == nodeLegalName.toX500Name() -> {
|
||||||
|
val certType = CertificateType.values().first { it.role == CertRole.NODE_CA }
|
||||||
|
createCertPath(rootAndIntermediateCA = rootAndIntermediateCA, publicKey = it.publicKey, type = certType)
|
||||||
|
}
|
||||||
|
it.subject == notaryServiceLegalName.toX500Name() -> {
|
||||||
|
val certType = CertificateType.values().first { it.role == CertRole.SERVICE_IDENTITY }
|
||||||
|
createCertPath(rootAndIntermediateCA = rootAndIntermediateCA, publicKey = it.publicKey, type = certType, legalName = notaryServiceLegalName)
|
||||||
|
}
|
||||||
|
else -> throw IllegalStateException("Unknown CSR")
|
||||||
|
}
|
||||||
|
}.generateKeysAndRegister()
|
||||||
|
|
||||||
|
val nodeKeystore = config.signingCertificateStore.get()
|
||||||
|
|
||||||
|
nodeKeystore.run {
|
||||||
|
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
|
assertFalse(contains(CORDA_ROOT_CA))
|
||||||
|
assertFalse(contains(X509Utilities.CORDA_CLIENT_TLS))
|
||||||
|
assertThat(CertRole.extract(this[X509Utilities.CORDA_CLIENT_CA])).isEqualTo(CertRole.NODE_CA)
|
||||||
|
assertThat(CertRole.extract(this[DISTRIBUTED_NOTARY_KEY_ALIAS])).isEqualTo(CertRole.SERVICE_IDENTITY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout=300_000)
|
||||||
|
fun `notary registration fails when no separate notary service identity configured`() {
|
||||||
|
val notaryNodeConfig = createNotaryNodeConfiguration(notaryServiceLegalName = null)
|
||||||
|
assertThat(notaryNodeConfig.notary).isNotNull
|
||||||
|
|
||||||
|
assertThatThrownBy {
|
||||||
|
createRegistrationHelper(nodeConfig = notaryNodeConfig)
|
||||||
|
}.isInstanceOf(IllegalArgumentException::class.java)
|
||||||
|
.hasMessageContaining("notary service legal name must be provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout=300_000)
|
||||||
|
fun `notary registration fails when notary service identity configured with same legal name as node`() {
|
||||||
|
val notaryNodeConfig = createNotaryNodeConfiguration(notaryServiceLegalName = config.myLegalName)
|
||||||
|
assertThat(notaryNodeConfig.notary).isNotNull
|
||||||
|
|
||||||
|
assertThatThrownBy {
|
||||||
|
createRegistrationHelper(nodeConfig = notaryNodeConfig)
|
||||||
|
}.isInstanceOf(IllegalArgumentException::class.java)
|
||||||
|
.hasMessageContaining("notary service legal name must be different from the node")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNotaryNodeConfiguration(notaryServiceLegalName: CordaX500Name?): NodeConfiguration {
|
||||||
|
return rigorousMock<NodeConfiguration>().also {
|
||||||
|
doReturn(config.baseDirectory).whenever(it).baseDirectory
|
||||||
|
doReturn(config.certificatesDirectory).whenever(it).certificatesDirectory
|
||||||
|
doReturn(CertificateStoreStubs.P2P.withCertificatesDirectory(config.certificatesDirectory)).whenever(it).p2pSslOptions
|
||||||
|
doReturn(CertificateStoreStubs.Signing.withCertificatesDirectory(config.certificatesDirectory)).whenever(it)
|
||||||
|
.signingCertificateStore
|
||||||
|
doReturn(nodeLegalName).whenever(it).myLegalName
|
||||||
|
doReturn("").whenever(it).emailAddress
|
||||||
|
doReturn(null).whenever(it).tlsCertCrlDistPoint
|
||||||
|
doReturn(null).whenever(it).tlsCertCrlIssuer
|
||||||
|
doReturn(true).whenever(it).crlCheckSoftFail
|
||||||
|
doReturn(NotaryConfig(validating = false, serviceLegalName = notaryServiceLegalName)).whenever(it).notary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCertPath(type: CertificateType = CertificateType.NODE_CA,
|
||||||
|
legalName: CordaX500Name = nodeLegalName,
|
||||||
|
publicKey: PublicKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME).public,
|
||||||
|
rootAndIntermediateCA: Pair<CertificateAndKeyPair, CertificateAndKeyPair> = createDevIntermediateCaCertPath()): List<X509Certificate> {
|
||||||
val (rootCa, intermediateCa) = rootAndIntermediateCA
|
val (rootCa, intermediateCa) = rootAndIntermediateCA
|
||||||
val nameConstraints = if (type == CertificateType.NODE_CA) {
|
val nameConstraints = if (type == CertificateType.NODE_CA) {
|
||||||
NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.toX500Name()))), arrayOf())
|
NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, legalName.toX500Name()))), arrayOf())
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
val nodeCaCert = X509Utilities.createCertificate(
|
val cert = X509Utilities.createCertificate(
|
||||||
type,
|
type,
|
||||||
intermediateCa.certificate,
|
intermediateCa.certificate,
|
||||||
intermediateCa.keyPair,
|
intermediateCa.keyPair,
|
||||||
legalName.x500Principal,
|
legalName.x500Principal,
|
||||||
publicKey,
|
publicKey,
|
||||||
nameConstraints = nameConstraints)
|
nameConstraints = nameConstraints)
|
||||||
return listOf(nodeCaCert, intermediateCa.certificate, rootCa.certificate)
|
return listOf(cert, intermediateCa.certificate, rootCa.certificate)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createFixedResponseRegistrationHelper(response: List<X509Certificate>, certRole: CertRole = CertRole.NODE_CA): NetworkRegistrationHelper {
|
private fun createFixedResponseRegistrationHelper(response: List<X509Certificate>, certRole: CertRole = CertRole.NODE_CA): NetworkRegistrationHelper {
|
||||||
return createRegistrationHelper(certRole) { response }
|
return createRegistrationHelper(certRole) { response }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRegistrationHelper(certRole: CertRole = CertRole.NODE_CA, rootAndIntermediateCA: Pair<CertificateAndKeyPair, CertificateAndKeyPair> = createDevIntermediateCaCertPath()) = createRegistrationHelper(certRole) {
|
private fun createRegistrationHelper(
|
||||||
|
certRole: CertRole = CertRole.NODE_CA,
|
||||||
|
rootAndIntermediateCA: Pair<CertificateAndKeyPair, CertificateAndKeyPair> = createDevIntermediateCaCertPath(),
|
||||||
|
nodeConfig: NodeConfiguration = config
|
||||||
|
) = createRegistrationHelper(certRole, nodeConfig) {
|
||||||
val certType = CertificateType.values().first { it.role == certRole }
|
val certType = CertificateType.values().first { it.role == certRole }
|
||||||
createNodeCaCertPath(rootAndIntermediateCA = rootAndIntermediateCA, publicKey = it.publicKey, type = certType)
|
createCertPath(rootAndIntermediateCA = rootAndIntermediateCA, publicKey = it.publicKey, type = certType)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createRegistrationHelper(certRole: CertRole = CertRole.NODE_CA, dynamicResponse: (JcaPKCS10CertificationRequest) -> List<X509Certificate>): NetworkRegistrationHelper {
|
private fun createRegistrationHelper(
|
||||||
|
certRole: CertRole = CertRole.NODE_CA,
|
||||||
|
nodeConfig: NodeConfiguration = config,
|
||||||
|
dynamicResponse: (JcaPKCS10CertificationRequest) -> List<X509Certificate>
|
||||||
|
): NetworkRegistrationHelper {
|
||||||
val certService = rigorousMock<NetworkRegistrationService>().also {
|
val certService = rigorousMock<NetworkRegistrationService>().also {
|
||||||
val requests = mutableMapOf<String, JcaPKCS10CertificationRequest>()
|
val requests = mutableMapOf<String, JcaPKCS10CertificationRequest>()
|
||||||
doAnswer {
|
doAnswer {
|
||||||
@ -265,11 +351,11 @@ class NetworkRegistrationHelperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return when (certRole) {
|
return when (certRole) {
|
||||||
CertRole.NODE_CA -> NodeRegistrationHelper(NodeRegistrationConfiguration(config), certService, NodeRegistrationOption(config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
|
CertRole.NODE_CA -> NodeRegistrationHelper(NodeRegistrationConfiguration(nodeConfig), certService, NodeRegistrationOption(nodeConfig.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword))
|
||||||
CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper(
|
CertRole.SERVICE_IDENTITY -> NetworkRegistrationHelper(
|
||||||
NodeRegistrationConfiguration(config),
|
NodeRegistrationConfiguration(nodeConfig),
|
||||||
certService,
|
certService,
|
||||||
config.certificatesDirectory / networkRootTrustStoreFileName,
|
nodeConfig.certificatesDirectory / networkRootTrustStoreFileName,
|
||||||
networkRootTrustStorePassword,
|
networkRootTrustStorePassword,
|
||||||
DISTRIBUTED_NOTARY_KEY_ALIAS,
|
DISTRIBUTED_NOTARY_KEY_ALIAS,
|
||||||
CertRole.SERVICE_IDENTITY)
|
CertRole.SERVICE_IDENTITY)
|
||||||
|
@ -92,8 +92,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask,
|
|||||||
cordapp project(':samples:attachment-demo:workflows')
|
cordapp project(':samples:attachment-demo:workflows')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating: true]
|
notary = [validating: true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
cordapps = []
|
cordapps = []
|
||||||
rpcUsers = ext.rpcUsers
|
rpcUsers = ext.rpcUsers
|
||||||
|
@ -50,8 +50,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask,
|
|||||||
cordapp project(':finance:contracts')
|
cordapp project(':finance:contracts')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating: true]
|
notary = [validating: true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address "localhost:10003"
|
address "localhost:10003"
|
||||||
|
@ -27,8 +27,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask,
|
|||||||
cordapp project(':samples:cordapp-configuration:workflows')
|
cordapp project(':samples:cordapp-configuration:workflows')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
port 10003
|
port 10003
|
||||||
|
@ -62,8 +62,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask])
|
|||||||
cordapp project(':samples:irs-demo:cordapp:workflows-irs')
|
cordapp project(':samples:irs-demo:cordapp:workflows-irs')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address("localhost:10003")
|
address("localhost:10003")
|
||||||
@ -121,7 +123,9 @@ task prepareDockerNodes(type: net.corda.plugins.Dockerform, dependsOn: ['jar', n
|
|||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Service,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
cordapps = ["${project(":finance").group}:contracts:$corda_release_version", "${project(":finance").group}:workflows:$corda_release_version"]
|
cordapps = ["${project(":finance").group}:contracts:$corda_release_version", "${project(":finance").group}:workflows:$corda_release_version"]
|
||||||
rpcUsers = rpcUsersList
|
rpcUsers = rpcUsersList
|
||||||
useTestClock true
|
useTestClock true
|
||||||
|
@ -38,8 +38,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask])
|
|||||||
cordapp project(':samples:network-verifier:workflows')
|
cordapp project(':samples:network-verifier:workflows')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating : false]
|
notary = [validating : false,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
port 10003
|
port 10003
|
||||||
|
@ -55,13 +55,15 @@ task deployNodesSingle(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) {
|
|||||||
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
|
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
p2pPort 10009
|
p2pPort 10009
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address "localhost:10010"
|
address "localhost:10010"
|
||||||
adminAddress "localhost:10110"
|
adminAddress "localhost:10110"
|
||||||
}
|
}
|
||||||
notary = [validating: true]
|
notary = [validating: true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +87,7 @@ task deployNodesCustom(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) {
|
|||||||
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
|
rpcUsers = [[user: "demou", password: "demop", permissions: ["ALL"]]]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
p2pPort 10009
|
p2pPort 10009
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address "localhost:10010"
|
address "localhost:10010"
|
||||||
@ -93,7 +95,8 @@ task deployNodesCustom(type: Cordform, dependsOn: ['jar', nodeTask, webTask]) {
|
|||||||
}
|
}
|
||||||
notary = [
|
notary = [
|
||||||
validating: true,
|
validating: true,
|
||||||
className: "net.corda.notarydemo.MyCustomValidatingNotaryService"
|
className: "net.corda.notarydemo.MyCustomValidatingNotaryService",
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask,
|
|||||||
rpcUsers = [['username': "default", 'password': "default", 'permissions': [ 'ALL' ]]]
|
rpcUsers = [['username': "default", 'password': "default", 'permissions': [ 'ALL' ]]]
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address "localhost:10014"
|
address "localhost:10014"
|
||||||
|
@ -83,8 +83,10 @@ task deployNodes(type: net.corda.plugins.Cordform, dependsOn: ['jar', nodeTask])
|
|||||||
cordapp project(':samples:trader-demo:workflows-trader')
|
cordapp project(':samples:trader-demo:workflows-trader')
|
||||||
}
|
}
|
||||||
node {
|
node {
|
||||||
name "O=Notary Service,L=Zurich,C=CH"
|
name "O=Notary Node,L=Zurich,C=CH"
|
||||||
notary = [validating : true]
|
notary = [validating : true,
|
||||||
|
serviceLegalName: "O=Notary Service,L=Zurich,C=CH"
|
||||||
|
]
|
||||||
p2pPort 10002
|
p2pPort 10002
|
||||||
rpcSettings {
|
rpcSettings {
|
||||||
address "localhost:10003"
|
address "localhost:10003"
|
||||||
|
Loading…
Reference in New Issue
Block a user