Merge remote-tracking branch 'open/master' into mike-merge-4d2d9b83

This commit is contained in:
Mike Hearn 2018-11-16 15:35:27 +00:00
commit 9594aea9f7
17 changed files with 189 additions and 208 deletions

View File

@ -1373,8 +1373,6 @@ public final class net.corda.core.crypto.CordaSecurityProvider extends java.secu
public static final class net.corda.core.crypto.CordaSecurityProvider$Companion extends java.lang.Object
##
public final class net.corda.core.crypto.CordaSecurityProviderKt extends java.lang.Object
@NotNull
public static final String CORDA_SECURE_RANDOM_ALGORITHM = "CordaPRNG"
##
public final class net.corda.core.crypto.Crypto extends java.lang.Object
@NotNull
@ -1509,13 +1507,6 @@ public final class net.corda.core.crypto.CryptoUtils extends java.lang.Object
public static final boolean verify(java.security.PublicKey, byte[], net.corda.core.crypto.DigitalSignature)
public static final boolean verify(java.security.PublicKey, byte[], byte[])
##
public final class net.corda.core.crypto.DelegatingSecureRandomSpi extends java.security.SecureRandomSpi
public <init>(kotlin.jvm.functions.Function0<? extends java.security.SecureRandom>)
@Nullable
protected byte[] engineGenerateSeed(int)
protected void engineNextBytes(byte[])
protected void engineSetSeed(byte[])
##
@CordaSerializable
public class net.corda.core.crypto.DigitalSignature extends net.corda.core.utilities.OpaqueBytes
public <init>(byte[])

2
.idea/compiler.xml generated
View File

@ -165,6 +165,8 @@
<module name="ha-testing_integrationTest" target="1.8" />
<module name="ha-testing_main" target="1.8" />
<module name="ha-testing_test" target="1.8" />
<module name="ha-utilities_main" target="1.8" />
<module name="ha-utilities_test" target="1.8" />
<module name="health-survey_main" target="1.8" />
<module name="health-survey_test" target="1.8" />
<module name="hsm-tool_main" target="1.8" />

View File

@ -64,6 +64,7 @@ see changes to this list.
* Credit Suisse
* cyrsis
* Dan Newton (Accenture)
* Daniel Krajnik (BCS Technology International)
* Daniel Roig (SEB)
* Dave Hudson (R3)
* David John Grundy (Dankse Bank)

View File

@ -77,7 +77,7 @@ buildscript {
ext.snappy_version = '0.4'
ext.class_graph_version = '4.2.12'
ext.jcabi_manifests_version = '1.1'
ext.picocli_version = '3.6.1'
ext.picocli_version = '3.8.0'
// Name of the IntelliJ SDK created for the deterministic Java rt.jar.
// ext.deterministic_idea_sdk = '1.8 (Deterministic)'

View File

@ -72,7 +72,8 @@ object JacksonSupport {
override val isFullParties: Boolean = false) : PartyObjectMapper, ObjectMapper(factory) {
override fun wellKnownPartyFromX500Name(name: CordaX500Name): Party? = rpc.wellKnownPartyFromX500Name(name)
override fun partyFromKey(owningKey: PublicKey): Party? = rpc.partyFromKey(owningKey)
override fun partiesFromName(query: String) = rpc.partiesFromName(query, fuzzyIdentityMatch)
// Second parameter is exactMatch, so we have to invert the meaning here.
override fun partiesFromName(query: String) = rpc.partiesFromName(query, !fuzzyIdentityMatch)
override fun nodeInfoFromParty(party: AbstractParty): NodeInfo? = rpc.nodeInfoFromParty(party)
}

View File

@ -1,17 +1,12 @@
package net.corda.core.crypto
import io.netty.util.concurrent.FastThreadLocal
import net.corda.core.KeepForDJVM
import net.corda.core.StubOutForDJVM
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_KEY
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
import net.corda.core.internal.VisibleForTesting
import net.corda.core.crypto.internal.PlatformSecureRandomService
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import java.security.Provider
import java.security.SecureRandom
import java.security.SecureRandomSpi
internal const val CORDA_SECURE_RANDOM_ALGORITHM = "CordaPRNG"
@KeepForDJVM
class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
@ -24,8 +19,12 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", CompositeSignature::class.java.name)
put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
// Assuming this Provider is the first SecureRandom Provider, this algorithm is the SecureRandom default:
putService(DelegatingSecureRandomService(this))
putPlatformSecureRandomService()
}
@StubOutForDJVM
private fun putPlatformSecureRandomService() {
putService(PlatformSecureRandomService(this))
}
}
@ -50,28 +49,3 @@ object CordaObjectIdentifier {
@JvmField
val COMPOSITE_SIGNATURE = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791003")
}
// Unlike all the NativePRNG algorithms, this doesn't use a global lock:
private class SunSecureRandom : SecureRandom(sun.security.provider.SecureRandom(), null)
private class DelegatingSecureRandomService(provider: CordaSecurityProvider) : Provider.Service(
provider, type, CORDA_SECURE_RANDOM_ALGORITHM, DelegatingSecureRandomSpi::class.java.name, null, null) {
private companion object {
private const val type = "SecureRandom"
}
internal val instance = DelegatingSecureRandomSpi(::SunSecureRandom)
override fun newInstance(constructorParameter: Any?) = instance
}
internal class DelegatingSecureRandomSpi internal constructor(secureRandomFactory: () -> SecureRandom) : SecureRandomSpi() {
private val threadLocalSecureRandom = object : FastThreadLocal<SecureRandom>() {
override fun initialValue() = secureRandomFactory()
}
override fun engineSetSeed(seed: ByteArray) = threadLocalSecureRandom.get().setSeed(seed)
override fun engineNextBytes(bytes: ByteArray) = threadLocalSecureRandom.get().nextBytes(bytes)
override fun engineGenerateSeed(numBytes: Int): ByteArray? = threadLocalSecureRandom.get().generateSeed(numBytes)
@VisibleForTesting
internal fun currentThreadSecureRandom() = threadLocalSecureRandom.get()
}

View File

@ -2,17 +2,52 @@
@file:DeleteForDJVM
package net.corda.core.crypto.internal
import io.netty.util.concurrent.FastThreadLocal
import net.corda.core.DeleteForDJVM
import net.corda.core.crypto.CORDA_SECURE_RANDOM_ALGORITHM
import net.corda.core.crypto.DummySecureRandom
import net.corda.core.utilities.SgxSupport
import org.apache.commons.lang.SystemUtils
import java.security.Provider
import java.security.SecureRandom
import java.security.SecureRandomSpi
/**
* This has been migrated into a separate class so that it
* is easier to delete from the core-deterministic module.
*/
internal val platformSecureRandom = when {
SgxSupport.isInsideEnclave -> DummySecureRandom
else -> SecureRandom.getInstance(CORDA_SECURE_RANDOM_ALGORITHM)
internal val platformSecureRandom: () -> SecureRandom = when {
SgxSupport.isInsideEnclave -> { { DummySecureRandom } }
SystemUtils.IS_OS_LINUX -> {
{ SunSecureRandom() }
}
else -> SecureRandom::getInstanceStrong
}
@DeleteForDJVM
class PlatformSecureRandomService(provider: Provider)
: Provider.Service(provider, "SecureRandom", algorithm, PlatformSecureRandomSpi::javaClass.name, null, null) {
companion object {
const val algorithm = "CordaPRNG"
}
private val instance: SecureRandomSpi = PlatformSecureRandomSpi()
override fun newInstance(constructorParameter: Any?) = instance
}
@DeleteForDJVM
private class PlatformSecureRandomSpi : SecureRandomSpi() {
private val threadLocalSecureRandom = object : FastThreadLocal<SecureRandom>() {
override fun initialValue() = platformSecureRandom()
}
private val secureRandom: SecureRandom = threadLocalSecureRandom.get()
override fun engineSetSeed(seed: ByteArray) = secureRandom.setSeed(seed)
override fun engineNextBytes(bytes: ByteArray) = secureRandom.nextBytes(bytes)
override fun engineGenerateSeed(numBytes: Int): ByteArray = secureRandom.generateSeed(numBytes)
}
// Enterprise performance tweak: Unlike all the NativePRNG algorithms, this doesn't use a global lock:
// TODO: This is using private Java API. Just replace this with an implementation that always reads /dev/urandom on Linux.
private class SunSecureRandom : SecureRandom(sun.security.provider.SecureRandom(), null)

View File

@ -18,12 +18,18 @@ import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
import java.security.SecureRandom
import java.security.Security
internal val cordaSecurityProvider = CordaSecurityProvider().also {
val cordaSecurityProvider = CordaSecurityProvider().also {
// Among the others, we should register [CordaSecurityProvider] as the first provider, to ensure that when invoking [SecureRandom()]
// the [platformSecureRandom] is returned (which is registered in CordaSecurityProvider).
// Note that internally, [SecureRandom()] will look through all registered providers.
// Then it returns the first PRNG algorithm of the first provider that has registered a SecureRandom
// implementation (in our case [CordaSecurityProvider]), or null if none of the registered providers supplies
// a SecureRandom implementation.
Security.insertProviderAt(it, 1) // The position is 1-based.
}
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
internal val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
internal val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
putAll(EdDSASecurityProvider())
// Override the normal EdDSA engine with one which can handle X509 keys.
put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.java.name)
@ -38,7 +44,7 @@ internal val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
// TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider.
Security.addProvider(it)
}
internal val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
require(name == "BCPQC") // The constant it comes from is not final.
}.also {
Security.addProvider(it)
@ -47,7 +53,7 @@ internal val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
// that could cause unexpected and suspicious behaviour.
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
// The val is private to avoid any harmful state changes.
internal val providerMap = listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider).map { it.name to it }.toMap()
val providerMap = listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider).map { it.name to it }.toMap()
@DeleteForDJVM
internal fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom // To minimise diff of CryptoUtils against open-source.
fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source.

View File

@ -6,6 +6,7 @@ import net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.RSA_SHA256
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
import net.corda.core.crypto.internal.PlatformSecureRandomService
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.OpaqueBytes
import net.corda.nodeapi.internal.DEV_INTERMEDIATE_CA
@ -21,10 +22,7 @@ import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.apache.commons.lang.ArrayUtils.EMPTY_BYTE_ARRAY
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jce.ECNamedCurveTable
@ -36,9 +34,9 @@ import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals
import org.junit.Test
import java.math.BigInteger
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.cert.X509Certificate
import java.security.SecureRandom
import java.security.Security
import java.util.*
import kotlin.test.*
@ -948,26 +946,25 @@ class CryptoUtilsTest {
this.outputStream.close()
}
private fun createCert(signer: ContentSigner, keyPair: KeyPair): X509Certificate {
val dname = X500Name("CN=TestEntity")
val startDate = Calendar.getInstance().let { cal ->
cal.time = Date()
cal.add(Calendar.HOUR, -1)
cal.time
}
val endDate = Calendar.getInstance().let { cal ->
cal.time = startDate
cal.add(Calendar.YEAR, 1)
cal.time
}
val certificate = JcaX509v3CertificateBuilder(
dname,
BigInteger.TEN,
startDate,
endDate,
dname,
keyPair.public
).build(signer)
return JcaX509CertificateConverter().getCertificate(certificate)
@Test
fun `test default SecureRandom uses platformSecureRandom`() {
// Note than in Corda, [CordaSecurityProvider] is registered as the first provider.
// Remove [CordaSecurityProvider] in case it is already registered.
Security.removeProvider(CordaSecurityProvider.PROVIDER_NAME)
// Try after removing CordaSecurityProvider.
val secureRandomNotRegisteredCordaProvider = SecureRandom()
assertNotEquals(PlatformSecureRandomService.algorithm, secureRandomNotRegisteredCordaProvider.algorithm)
// Now register CordaSecurityProvider as last Provider.
Security.addProvider(CordaSecurityProvider())
val secureRandomRegisteredLastCordaProvider = SecureRandom()
assertNotEquals(PlatformSecureRandomService.algorithm, secureRandomRegisteredLastCordaProvider.algorithm)
// Remove Corda Provider again and add it as the first Provider entry.
Security.removeProvider(CordaSecurityProvider.PROVIDER_NAME)
Security.insertProviderAt(CordaSecurityProvider(), 1) // This is base-1.
val secureRandomRegisteredFirstCordaProvider = SecureRandom()
assertEquals(PlatformSecureRandomService.algorithm, secureRandomRegisteredFirstCordaProvider.algorithm)
}
}

View File

@ -1,41 +1,9 @@
package net.corda.core.crypto
import net.corda.core.crypto.internal.cordaSecurityProvider
import net.corda.core.internal.concurrent.fork
import net.corda.core.internal.join
import net.corda.core.utilities.getOrThrow
import org.junit.Test
import java.security.SecureRandom
import java.util.concurrent.Executors
import kotlin.test.assertEquals
import kotlin.test.assertNotEquals
import kotlin.test.assertNotSame
import kotlin.test.assertSame
class SecureRandomTest {
private companion object {
private val getSpi = SecureRandom::class.java.getDeclaredMethod("getSecureRandomSpi").apply { isAccessible = true }
private fun SecureRandom.spi() = getSpi.invoke(this)
init {
newSecureRandom() // Ensure all globals installed before running tests.
}
}
@Test
fun `newSecureRandom returns a global that delegates to thread-local`() {
val sr = newSecureRandom()
assertSame(sr, newSecureRandom())
checkDelegatesToThreadLocal(sr)
}
@Test
fun `regular SecureRandom delegates to thread-local`() {
val sr = SecureRandom()
assertSame(sr.spi(), SecureRandom().spi())
checkDelegatesToThreadLocal(sr)
}
@Test(timeout = 1000)
fun `regular SecureRandom does not spend a lot of time seeding itself`() {
val bytes = ByteArray(1000)
@ -46,39 +14,4 @@ class SecureRandomTest {
}
}
}
@Test
fun `regular SecureRandom with seed delegates to thread-local`() {
val sr = SecureRandom(byteArrayOf(1, 2, 3))
assertSame(sr.spi(), SecureRandom(byteArrayOf(4, 5, 6)).spi())
checkDelegatesToThreadLocal(sr)
}
@Test
fun `SecureRandom#getInstance makes a SecureRandom that delegates to thread-local`() {
CORDA_SECURE_RANDOM_ALGORITHM.let {
val sr = SecureRandom.getInstance(it)
assertEquals(it, sr.algorithm)
assertSame(sr.spi(), SecureRandom.getInstance(it).spi())
checkDelegatesToThreadLocal(sr)
}
}
private fun checkDelegatesToThreadLocal(sr: SecureRandom) {
val spi = sr.spi() as DelegatingSecureRandomSpi
val fg = spi.currentThreadSecureRandom()
val e = Executors.newSingleThreadExecutor()
val bg = e.fork(spi::currentThreadSecureRandom).getOrThrow()
assertNotSame(fg, bg) // Background thread got a distinct instance.
// Each thread always gets the same instance:
assertSame(fg, spi.currentThreadSecureRandom())
assertSame(bg, e.fork(spi::currentThreadSecureRandom).getOrThrow())
e.join()
assertSame(fg.provider, bg.provider)
assertNotSame(cordaSecurityProvider, fg.provider)
assertEquals(fg.algorithm, bg.algorithm)
assertNotEquals(CORDA_SECURE_RANDOM_ALGORITHM, fg.algorithm)
assertSame(cordaSecurityProvider, sr.provider)
assertEquals(CORDA_SECURE_RANDOM_ALGORITHM, sr.algorithm)
}
}

View File

@ -188,8 +188,7 @@ class Cash : OnLedgerAsset<Currency, Cash.Commands, Cash.State>() {
// sum to more than the inputs. An issuance of zero size is not allowed.
//
// Note that this means literally anyone with access to the network can issue cash claims of arbitrary
// amounts! It is up to the recipient to decide if the backing party is trustworthy or not, via some
// as-yet-unwritten identity service. See ADP-22 for discussion.
// amounts! It is up to the recipient to decide if the backing party is trustworthy or not.
// The grouping ensures that all outputs have the same deposit reference and currency.
val inputAmount = inputs.sumCashOrZero(Issued(issuer, currency))

View File

@ -18,6 +18,8 @@ import org.slf4j.LoggerFactory
import java.nio.file.Path
import java.security.KeyPair
import java.security.PublicKey
import java.security.cert.X509Certificate
import javax.security.auth.x500.X500Principal
/**
* Contains utility methods for generating identities for a node.
@ -43,50 +45,67 @@ object DevIdentityGenerator {
return identity.party
}
fun generateDistributedNotaryCompositeIdentity(dirs: List<Path>, notaryName: CordaX500Name, threshold: Int = 1): Party {
require(dirs.isNotEmpty())
log.trace { "Generating composite identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
val keyPairs = (1..dirs.size).map { generateKeyPair() }
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
keyPairs.zip(dirs) { keyPair, nodeDir ->
generateCertificates(keyPair, notaryKey, notaryName, nodeDir)
}
return Party(notaryName, notaryKey)
}
/** Generates a CFT notary identity, where the entire cluster shares a key pair. */
fun generateDistributedNotarySingularIdentity(dirs: List<Path>, notaryName: CordaX500Name): Party {
require(dirs.isNotEmpty())
log.trace { "Generating singular identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
val keyPair = generateKeyPair()
val notaryKey = keyPair.public
dirs.forEach { dir ->
generateCertificates(keyPair, notaryKey, notaryName, dir)
dirs.forEach { nodeDir ->
val keyStore = getKeyStore(nodeDir)
setPrivateKey(keyStore, keyPair, notaryName.x500Principal)
}
return Party(notaryName, notaryKey)
}
private fun generateCertificates(keyPair: KeyPair, notaryKey: PublicKey, notaryName: CordaX500Name, nodeDir: Path) {
val (serviceKeyCert, compositeKeyCert) = listOf(keyPair.public, notaryKey).map { publicKey ->
X509Utilities.createCertificate(
CertificateType.SERVICE_IDENTITY,
DEV_INTERMEDIATE_CA.certificate,
DEV_INTERMEDIATE_CA.keyPair,
notaryName.x500Principal,
publicKey)
}
val distServKeyStoreFile = (nodeDir / "certificates").createDirectories() / "distributedService.jks"
X509KeyStore.fromFile(distServKeyStoreFile, DEV_CA_KEY_STORE_PASS, createNew = true).update {
setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
setPrivateKey(
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
keyPair.private,
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
DEV_CA_KEY_STORE_PASS // Unfortunately we have to use the same password for private key due to Artemis limitation, for more details please see:
// org.apache.activemq.artemis.core.remoting.impl.ssl.SSLSupport.loadKeyManagerFactory
// where it is calling `KeyManagerFactory.init()` with store password
/*DEV_CA_PRIVATE_KEY_PASS*/)
/** Generates a BFT notary identity: individual key pairs for each cluster member, and a shared composite key. */
fun generateDistributedNotaryCompositeIdentity(dirs: List<Path>, notaryName: CordaX500Name, threshold: Int = 1): Party {
require(dirs.isNotEmpty())
log.trace { "Generating composite identity \"$notaryName\" for nodes: ${dirs.joinToString()}" }
val keyPairs = (1..dirs.size).map { generateKeyPair() }
val notaryKey = CompositeKey.Builder().addKeys(keyPairs.map { it.public }).build(threshold)
keyPairs.zip(dirs) { keyPair, nodeDir ->
val keyStore = getKeyStore(nodeDir)
setPrivateKey(keyStore, keyPair, notaryName.x500Principal)
setCompositeKey(keyStore, notaryKey, notaryName.x500Principal)
}
return Party(notaryName, notaryKey)
}
private fun getKeyStore(nodeDir: Path): X509KeyStore {
val distServKeyStoreFile = nodeDir / "certificates/distributedService.jks"
return X509KeyStore.fromFile(distServKeyStoreFile, DEV_CA_KEY_STORE_PASS, createNew = true)
}
private fun setPrivateKey(keyStore: X509KeyStore, keyPair: KeyPair, notaryPrincipal: X500Principal) {
val serviceKeyCert = createCertificate(keyPair.public, notaryPrincipal)
keyStore.setPrivateKey(
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
keyPair.private,
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
DEV_CA_KEY_STORE_PASS // Unfortunately we have to use the same password for private key due to Artemis limitation, for more details please see:
// org.apache.activemq.artemis.core.remoting.impl.ssl.SSLSupport.loadKeyManagerFactory
// where it is calling `KeyManagerFactory.init()` with store password
/*DEV_CA_PRIVATE_KEY_PASS*/)
}
private fun setCompositeKey(keyStore: X509KeyStore, compositeKey: PublicKey, notaryPrincipal: X500Principal) {
val compositeKeyCert = createCertificate(compositeKey, notaryPrincipal)
keyStore.setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
}
private fun createCertificate(publicKey: PublicKey, principal: X500Principal): X509Certificate {
return X509Utilities.createCertificate(
CertificateType.SERVICE_IDENTITY,
DEV_INTERMEDIATE_CA.certificate,
DEV_INTERMEDIATE_CA.keyPair,
principal,
publicKey)
}
}

View File

@ -33,6 +33,7 @@ import net.corda.serialization.internal.amqp.AbstractAMQPSerializationScheme
import net.corda.serialization.internal.amqp.amqpMagic
import java.io.File
import java.io.InputStream
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Path
import java.nio.file.StandardCopyOption.REPLACE_EXISTING
import java.security.PublicKey
@ -226,7 +227,13 @@ internal constructor(private val initSerEnv: Boolean,
println("Copying CorDapp JARs into node directories")
for (nodeDir in nodeDirs) {
val cordappsDir = (nodeDir / "cordapps").createDirectories()
cordappJars.forEach { it.copyToDirectory(cordappsDir) }
cordappJars.forEach {
try {
it.copyToDirectory(cordappsDir)
} catch (e: FileAlreadyExistsException) {
println("WARNING: ${it.fileName} already exists in $cordappsDir, ignoring and leaving existing CorDapp untouched")
}
}
}
}
generateServiceIdentitiesForNotaryClusters(configs)

View File

@ -891,13 +891,17 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key"
val signingCertificateStore = configuration.signingCertificateStore.get()
// A composite key is only required for BFT notaries.
val certificates = if (cryptoService.containsKey(compositeKeyAlias)) {
val certificate = signingCertificateStore[compositeKeyAlias]
// We have to create the certificate chain for the composite key manually, this is because we don't have a keystore
// provider that understand compositeKey-privateKey combo. The cert chain is created using the composite key certificate +
// the tail of the private key certificates, as they are both signed by the same certificate chain.
listOf(certificate) + signingCertificateStore.query { getCertificateChain(privateKeyAlias) }.drop(1)
} else throw IllegalStateException("The identity public key for the notary service $serviceLegalName was not found in the key store.")
} else {
// We assume the notary is CFT, and each cluster member shares the same notary key pair.
signingCertificateStore.query { getCertificateChain(privateKeyAlias) }
}
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
if (subject != serviceLegalName) {

View File

@ -67,7 +67,7 @@ fun TestCordappImpl.packageAsJar(file: Path) {
scanResult.use {
val manifest = createTestManifest(name, title, version, vendor, targetVersion)
JarOutputStream(file.outputStream(), manifest).use { jos ->
val time = FileTime.from(Instant.now())
val time = FileTime.from(Instant.EPOCH)
// The same resource may be found in different locations (this will happen when running from gradle) so just
// pick the first one found.
scanResult.allResources.asMap().forEach { path, resourceList ->

View File

@ -114,31 +114,43 @@ class NodeTabView : Fragment() {
fieldset("Additional configuration") {
styleClass.addAll("services-panel")
val extraServices = if (nodeController.hasNotary()) {
listOf(USD, GBP, CHF, EUR).map { CurrencyIssuer(it) }
} else {
listOf(NotaryService(true), NotaryService(false))
}
val servicesList = CheckListView(extraServices.observable()).apply {
vboxConstraints { vGrow = Priority.ALWAYS }
model.item.extraServices.set(checkModel.checkedItems)
if (!nodeController.hasNotary()) {
checkModel.check(0)
checkModel.checkedItems.addListener(ListChangeListener { change ->
while (change.next()) {
if (change.wasAdded()) {
val item = change.addedSubList.last()
val idx = checkModel.getItemIndex(item)
checkModel.checkedIndices.forEach {
if (it != idx) checkModel.clearCheck(it)
if (nodeController.hasNotary()) {
val extraServices: List<ExtraService> = listOf(USD, GBP, CHF, EUR).map { CurrencyIssuer(it) }
val servicesList = CheckListView(extraServices.observable()).apply {
vboxConstraints { vGrow = Priority.ALWAYS }
model.item.extraServices.set(checkModel.checkedItems)
if (!nodeController.hasNotary()) {
checkModel.check(0)
checkModel.checkedItems.addListener(ListChangeListener { change ->
while (change.next()) {
if (change.wasAdded()) {
val item = change.addedSubList.last()
val idx = checkModel.getItemIndex(item)
checkModel.checkedIndices.forEach {
if (it != idx) checkModel.clearCheck(it)
}
}
}
}
})
})
}
}
add(servicesList)
} else {
val notaryTypes = listOf(NotaryService(true), NotaryService(false))
val notaryTypeToggleGroup = togglegroup()
notaryTypeToggleGroup.selectedValueProperty<NotaryService>().addListener { observValue, oldValue, newValue ->
oldValue?.let {
model.item.extraServices.removeAll(it)
}
newValue?.let {
model.item.extraServices.add(it)
}
}
notaryTypes.forEachIndexed { index, notaryType ->
val toggle = radiobutton(notaryType.toString(), notaryTypeToggleGroup, notaryType)
toggle.isSelected = index == 0
}
}
add(servicesList)
}
}

View File

@ -36,7 +36,7 @@ class InteractiveShellTest {
constructor(party: Party) : this(party.name.toString())
constructor(b: Int?, amount: Amount<UserValue>) : this("${(b ?: 0) + amount.quantity} ${amount.token}")
constructor(b: Array<String>) : this(b.joinToString("+"))
constructor(amounts: Array<Amount<UserValue>>) : this(amounts.map(Amount<UserValue>::toString).joinToString("++"))
constructor(amounts: Array<Amount<UserValue>>) : this(amounts.joinToString("++", transform = Amount<UserValue>::toString))
override val progressTracker = ProgressTracker()
override fun call() = a