ENT-3142 NetworkParameters signing tool (#4996)

This commit is contained in:
James Brown 2019-04-11 16:45:04 +01:00 committed by Shams Asari
parent 2e97eaee0d
commit 7e58b7397d
3 changed files with 259 additions and 0 deletions

View File

@ -0,0 +1,32 @@
apply plugin: 'java'
apply plugin: 'kotlin'
description 'NetworkParameters signing tool'
dependencies {
compile project(':tools:cliutils')
compile "org.slf4j:jul-to-slf4j:$slf4j_version"
compile "org.apache.logging.log4j:log4j-slf4j-impl:$log4j_version"
compile "com.jcabi:jcabi-manifests:$jcabi_manifests_version"
compile project(':core')
compile project(':node-api')
}
jar {
from(configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }) {
exclude "META-INF/*.SF"
exclude "META-INF/*.DSA"
exclude "META-INF/*.RSA"
}
baseName = "netparams"
manifest {
attributes(
'Main-Class': 'net.corda.netparams.NetParamsKt'
)
}
}
processResources {
from file("$rootDir/config/dev/log4j2.xml")
from file("$rootDir/node-api/src/main/resources/certificates/cordadevcakeys.jks")
}

View File

@ -0,0 +1,226 @@
package net.corda.netparams
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import net.corda.cliutils.CordaCliWrapper
import net.corda.cliutils.start
import net.corda.core.identity.Party
import net.corda.core.internal.*
import net.corda.core.node.NetworkParameters
import net.corda.core.node.NotaryInfo
import net.corda.core.serialization.SerializationContext
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.deserialize
import net.corda.core.serialization.internal.SerializationEnvironment
import net.corda.core.serialization.internal.nodeSerializationEnv
import net.corda.core.serialization.serialize
import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.createDevNetworkMapCa
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.serialization.internal.*
import net.corda.serialization.internal.amqp.*
import picocli.CommandLine.*
import java.io.File
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.time.Instant
/**
* NetworkParameters signing tool for Corda
*
* This utility can be used to create and sign NetworkParameters files. This is useful for manual bootstrapping
* of a corda network, or by distribution via the NetworkMap
*
* By default the NetworkParameters will be signed with the development NetworkMap key, which is what happens
* currently with the Corda network bootstrapper. Arbitrary signing keys can be specified using
* the --keystore and --alias parameters, in which case the specified Java keystore (and key) will be used instead.
*
* Values for the NetworkParameters are specified in a configuration file (in HOCON format),
* using the --config option. An example content is shown below:
*
* notaries : []
* minimumPlatformVersion = 1
* maxMessageSize = 10485760
* maxTransactionSize = 10485760
* whitelistContracts = {}
* eventHorizonDays = 30
* epoch = 1
*
* Notary nodes for the network can be specified in the notaries[] section (as a string of filenames), as below:
*
* notaries: ["/path/to/nodeinfo"]
*
* or via the commandline with the --notary-info option. Notaries are identified either by their respective nodeInfo files,
* or by a path to the identity certificate (JKS file containing the identity cert)
*
* Example usage:
*
* # Generate NetworkParameters with one notary, identified by it's NodeInfo file (and signed with default development NetworkMap key)
* java -jar netparams.jar --config netparams.conf --notary-info /path/to/nodeinfo --output /path/to/network-parameters
*
* # Generate using the notary's identity certificate (instead of it's nodeinfo)
* java -jar netparams.jar --config netparams.conf --notary-keystore /path/to/node/certificates/nodekeystore.jks
*
* # Generate using an arbitrary 'netparams' signing key
* java -jar netparams.jar --config netparams.conf --notary-info /path/to/nodeinfo --keystore /my/keystore.jks --keyalias netparams
*
*/
fun main(args: Array<String>) {
NetParamsSigner().start(args)
}
class NetParamsSigner : CordaCliWrapper("netparams-signer", "Sign network parameters") {
@Option(names = ["--config"], paramLabel = "file", description = ["Network Parameters config."])
private var configFile: Path? = null
@Option(names = ["--output"], paramLabel = "file", description = ["Network Parameters "])
private var outputFile: Path? = null
@Option(names = ["--notary-info"], paramLabel = "nodeInfo", description = ["Path to notary NodeInfo"])
private var notaryInfos: MutableList<Path> = mutableListOf<Path>()
@Option(names = ["--notary-keystore"], paramLabel = "keyStore", description = ["Path to node keystore"])
private var notaryKeyStores: MutableList<Path> = mutableListOf<Path>()
@Option(names = ["--notary-keypass"], paramLabel = "password", description = ["Password to node keystore"])
private var notaryKeyPasswords: MutableList<String> = mutableListOf<String>()
@Option(names = ["--keystore"], description = ["Keystore containing NetworkParameters signing key"])
private var keyStorePath: Path? = null
@Option(names = ["--keystore-pass"], description = ["Keystore password"])
private var keyStorePass: String? = null
@Option(names = ["--keyalias"], description = ["Alias of signing key"])
private var keyAlias: String? = null
@Option(names = ["--keypass"], description = ["Password of signing key"])
private var keyPass: String? = null
private fun getInput(prompt: String): String {
print(prompt)
System.out.flush()
val console = System.console()
if(console != null)
return console.readPassword().toString()
else
return readLine()!!
}
private object AMQPInspectorSerializationScheme : AbstractAMQPSerializationScheme(emptyList()) {
override fun canDeserializeVersion(magic: CordaSerializationMagic, target: SerializationContext.UseCase): Boolean {
return magic == amqpMagic
}
override fun rpcClientSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
override fun rpcServerSerializerFactory(context: SerializationContext) = throw UnsupportedOperationException()
}
private fun initialiseSerialization() {
nodeSerializationEnv = SerializationEnvironment.with(
SerializationFactoryImpl().apply {
registerScheme(AMQPInspectorSerializationScheme)
},
AMQP_P2P_CONTEXT)
}
override fun runProgram(): Int {
require(configFile != null ) { "The --config parameter must be specified" }
initialiseSerialization()
// notary specified by nodeInfo paths
val n1: List<NotaryInfo> = notaryInfos.map {
val party = identityFromNodeInfoPath(it.toFile())
NotaryInfo(party, false)
}
// notary specified by keystore path (+password)
require(notaryKeyStores.size == notaryKeyPasswords.size)
val n2: List<NotaryInfo> = notaryKeyStores.zip(notaryKeyPasswords) { path, password ->
val party = identityFromKeyStore(path.toFile(), password)
NotaryInfo(party, false)
}
val networkParameters = parametersFromConfig(configFile!!, n1 + n2)
print(networkParameters.toString())
val signingkey = if(keyStorePath != null) {
require(keyAlias != null) { "The --keyAlias parameters must be specified" }
if(keyStorePass == null)
keyStorePass = getInput("Store password (${keyStorePath?.fileName}): ")
if(keyPass == null)
keyPass = getInput("Key password (${keyAlias}): ")
val keyStore = X509KeyStore.fromFile(keyStorePath!!, keyStorePass!!)
keyStore.getCertificateAndKeyPair(keyAlias!!, keyPass!!)
}
else {
// issue from the development root
createDevNetworkMapCa()
}
// sign & serialise
val serializedSignedNetParams = signingkey.sign(networkParameters).serialize()
if(outputFile != null) {
print("\nWriting: " + outputFile)
serializedSignedNetParams.open().copyTo(outputFile!!, StandardCopyOption.REPLACE_EXISTING)
}
else {
print("\nUse --output to write results")
}
return 0
}
fun identityFromKeyStore(keyStorePath: File, keyStorePass: String, alias: String = "identity-private-key") : Party {
val keyStore = X509KeyStore.fromFile(keyStorePath.toPath(), keyStorePass)
return Party(keyStore.getCertificate(alias))
}
fun identityFromNodeInfoPath(nodeInfoPath: File) : Party{
val serializedNodeInfo = SerializedBytes<SignedNodeInfo>(nodeInfoPath.toPath().readAll())
val signedNodeInfo = serializedNodeInfo.deserialize()
val nodeInfo = signedNodeInfo.verified()
return nodeInfo.legalIdentities.last()
}
fun parseConfig(config: Config, optionalNotaryList: List<NotaryInfo>) : NetworkParameters {
// convert the notary list (of nodeinfo paths) to NotaryInfos
val notaryList: List<NotaryInfo> = config.getConfigList("notaries").map {
val path = it.getString("notaryNodeInfoFile")
val party = identityFromNodeInfoPath(File(path))
NotaryInfo(party, it.getBoolean("validating"))
}
return NetworkParameters(
minimumPlatformVersion = config.getInt("minimumPlatformVersion"),
maxMessageSize = config.getInt("maxMessageSize"),
maxTransactionSize = config.getInt("maxTransactionSize"),
epoch = config.getInt("epoch"),
modifiedTime = Instant.now(),
whitelistedContractImplementations = emptyMap(), // TODO: not supported
notaries = notaryList + optionalNotaryList
)
}
fun parametersFromConfig(file: Path, notaryList: List<NotaryInfo>) : NetworkParameters {
val parseOptions = ConfigParseOptions.defaults().setAllowMissing(true)
val config = ConfigFactory.parseFile(file.toFile(), parseOptions).resolve()
return parseConfig(config, notaryList)
}
}

View File

@ -23,6 +23,7 @@ include 'experimental:behave'
include 'experimental:quasar-hook'
include 'experimental:corda-utils'
include 'experimental:nodeinfo'
include 'experimental:netparams'
include 'jdk8u-deterministic'
include 'test-common'
include 'test-cli'