Merging signing service and doorman (#72)

* Merging signing service and doorman

* Addressing review comments

* Removing redundant package name space from method call

* Adding description field to gradle
This commit is contained in:
mkit 2017-10-20 17:19:50 +01:00 committed by GitHub
parent 0ae205ec25
commit dfb226fbbb
58 changed files with 372 additions and 5190 deletions

View File

@ -1,54 +0,0 @@
package com.r3.corda.doorman
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import joptsimple.ArgumentAcceptingOptionSpec
import joptsimple.OptionParser
import net.corda.core.crypto.sha256
import org.bouncycastle.cert.X509CertificateHolder
import java.io.ByteArrayInputStream
import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
/**
* Convert commandline arguments to [Config] object will allow us to use kotlin delegate with [ConfigHelper].
*/
object OptionParserHelper {
fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Unit): Config {
val parser = OptionParser()
val helpOption = parser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp();
registerOptions(parser)
val optionSet = parser.parse(*this)
// Print help and exit on help option.
if (optionSet.has(helpOption)) {
throw ShowHelpException(parser)
}
// Convert all command line options to Config.
return ConfigFactory.parseMap(parser.recognizedOptions().mapValues {
val optionSpec = it.value
if (optionSpec is ArgumentAcceptingOptionSpec<*> && !optionSpec.requiresArgument() && optionSet.has(optionSpec)) null else optionSpec.value(optionSet)
}.filterValues { it != null })
}
}
class ShowHelpException(val parser: OptionParser) : Exception()
object CertificateUtilities {
fun toX509Certificate(byteArray: ByteArray): X509Certificate {
return CertificateFactory.getInstance("X509").generateCertificate(ByteArrayInputStream(byteArray)) as X509Certificate
}
}
fun X509CertificateHolder.toX509Certificate(): Certificate = CertificateUtilities.toX509Certificate(encoded)
fun buildCertPath(vararg certificates: Certificate): CertPath {
return CertificateFactory.getInstance("X509").generateCertPath(certificates.asList())
}
fun buildCertPath(certPathBytes: ByteArray): CertPath = CertificateFactory.getInstance("X509").generateCertPath(certPathBytes.inputStream())
// TODO: replace this with Crypto.hash when its available.
fun PublicKey.hash() = encoded.sha256().toString()

View File

@ -6,6 +6,8 @@ ext {
version "$corda_dependency_version" version "$corda_dependency_version"
description 'Network management module encapsulating components such as Doorman, HSM Signing Service and Network Map'
apply plugin: 'us.kirchmeier.capsule' apply plugin: 'us.kirchmeier.capsule'
apply plugin: 'kotlin' apply plugin: 'kotlin'
@ -46,8 +48,8 @@ sourceSets{
task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') { task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') {
group = 'build' group = 'build'
applicationClass 'com.r3.corda.doorman.MainKt' applicationClass 'com.r3.corda.networkmanage.doorman.MainKt'
archiveName "doorman-${version}-capsule.jar"
capsuleManifest { capsuleManifest {
applicationVersion = corda_dependency_version applicationVersion = corda_dependency_version
systemProperties['visualvm.display.name'] = 'Doorman' systemProperties['visualvm.display.name'] = 'Doorman'
@ -60,7 +62,29 @@ task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') {
reallyExecutable { trampolining() } reallyExecutable { trampolining() }
} }
task buildHsmJAR(type: FatCapsule, dependsOn: 'jar') {
group = 'build'
applicationClass 'com.r3.corda.networkmanage.hsm.MainKt'
archiveName "hsm-${version}-capsule.jar"
capsuleManifest {
applicationVersion = corda_dependency_version
systemProperties['visualvm.display.name'] = 'HSM Signing Service'
minJavaVersion = '1.8.0'
jvmArgs = ['-XX:+UseG1GC']
}
// Make the resulting JAR file directly executable on UNIX by prepending a shell script to it.
// This lets you run the file like so: ./corda.jar
// Other than being slightly less typing, this has one big advantage: Ctrl-C works properly in the terminal.
reallyExecutable { trampolining() }
}
task integrationTest(type: Test) {
testClassesDirs = sourceSets.integrationTest.output.classesDirs
classpath = sourceSets.integrationTest.runtimeClasspath
}
dependencies { dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "net.corda:corda-core:$corda_dependency_version" compile "net.corda:corda-core:$corda_dependency_version"

View File

@ -1,8 +1,9 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import com.r3.corda.doorman.persistence.DoormanSchemaService import com.r3.corda.networkmanage.common.persistence.SchemaService
import com.r3.corda.doorman.signer.Signer import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.doorman.signer.Signer
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -36,7 +37,7 @@ class DoormanIntegrationTest {
val database = configureDatabase(makeTestDataSourceProperties(), null, { val database = configureDatabase(makeTestDataSourceProperties(), null, {
// Identity service not needed doorman, corda persistence is not very generic. // Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException() throw UnsupportedOperationException()
}, DoormanSchemaService()) }, SchemaService())
val signer = Signer(intermediateCAKey, arrayOf(intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate())) val signer = Signer(intermediateCAKey, arrayOf(intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()))
//Start doorman server //Start doorman server

View File

@ -1,10 +1,10 @@
package com.r3.corda.signing package com.r3.corda.networkmanage.hsm
import com.r3.corda.signing.configuration.Parameters import com.r3.corda.networkmanage.hsm.SigningServiceIntegrationTest.Companion.DB_NAME
import com.r3.corda.networkmanage.hsm.SigningServiceIntegrationTest.Companion.H2_TCP_PORT
import com.r3.corda.networkmanage.hsm.SigningServiceIntegrationTest.Companion.HOST
import com.r3.corda.networkmanage.hsm.configuration.Parameters
import java.util.* import java.util.*
import com.r3.corda.signing.SigningServiceIntegrationTest.Companion.DB_NAME
import com.r3.corda.signing.SigningServiceIntegrationTest.Companion.HOST
import com.r3.corda.signing.SigningServiceIntegrationTest.Companion.H2_TCP_PORT
/** /**
* The main method for an interactive HSM signing service test/demo. It is supposed to be executed with the * The main method for an interactive HSM signing service test/demo. It is supposed to be executed with the

View File

@ -1,17 +1,17 @@
package com.r3.corda.signing package com.r3.corda.networkmanage.hsm
import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.verify
import com.nhaarman.mockito_kotlin.whenever import com.nhaarman.mockito_kotlin.whenever
import com.r3.corda.doorman.buildCertPath import com.r3.corda.networkmanage.common.persistence.SchemaService
import com.r3.corda.doorman.persistence.DoormanSchemaService import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.startDoorman import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.doorman.toX509Certificate import com.r3.corda.networkmanage.doorman.startDoorman
import com.r3.corda.signing.hsm.HsmSigner import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
import com.r3.corda.signing.persistence.DBCertificateRequestStorage import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
import com.r3.corda.signing.persistence.SigningServerSchemaService import com.r3.corda.networkmanage.hsm.signer.HsmSigner
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.NetworkHostAndPort import net.corda.core.utilities.NetworkHostAndPort
@ -58,7 +58,7 @@ class SigningServiceIntegrationTest {
timer.cancel() timer.cancel()
} }
private fun givenSignerSigningAllRequests(storage: DBCertificateRequestStorage): HsmSigner { private fun givenSignerSigningAllRequests(storage: SignedCertificateRequestStorage): HsmSigner {
// Create all certificates // Create all certificates
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Integration Test Corda Node Root CA", val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Integration Test Corda Node Root CA",
@ -71,14 +71,14 @@ class SigningServiceIntegrationTest {
return mock { return mock {
on { sign(any()) }.then { on { sign(any()) }.then {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val toSign = it.arguments[0] as List<ApprovedCertificateRequestData> val toSign = it.arguments[0] as List<CertificateRequestData>
toSign.forEach { toSign.forEach {
JcaPKCS10CertificationRequest(it.request).run { JcaPKCS10CertificationRequest(it.request).run {
val certificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate() val certificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).toX509Certificate()
it.certPath = buildCertPath(certificate, rootCACert.toX509Certificate()) it.certPath = buildCertPath(certificate, rootCACert.toX509Certificate())
} }
} }
storage.sign(toSign, listOf("TEST")) storage.store(toSign, listOf("TEST"))
} }
} }
} }
@ -89,7 +89,7 @@ class SigningServiceIntegrationTest {
val database = configureDatabase(makeTestDataSourceProperties(), null, { val database = configureDatabase(makeTestDataSourceProperties(), null, {
// Identity service not needed doorman, corda persistence is not very generic. // Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException() throw UnsupportedOperationException()
}, DoormanSchemaService()) }, SchemaService())
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true) val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true)
// Start Corda network registration. // Start Corda network registration.
@ -99,10 +99,10 @@ class SigningServiceIntegrationTest {
whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}")) whenever(it.certificateSigningService).thenReturn(URL("http://$HOST:${doorman.hostAndPort.port}"))
} }
val signingServiceStorage = DBCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), { val signingServiceStorage = DBSignedCertificateRequestStorage(configureDatabase(makeTestDataSourceProperties(), makeNotInitialisingTestDatabaseProperties(), {
// Identity service not needed doorman, corda persistence is not very generic. // Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException() throw UnsupportedOperationException()
}, SigningServerSchemaService())) }, SchemaService()))
val hsmSigner = givenSignerSigningAllRequests(signingServiceStorage) val hsmSigner = givenSignerSigningAllRequests(signingServiceStorage)
// Poll the database for approved requests // Poll the database for approved requests
@ -136,7 +136,7 @@ class SigningServiceIntegrationTest {
val database = configureDatabase(makeTestDataSourceProperties(), null, { val database = configureDatabase(makeTestDataSourceProperties(), null, {
// Identity service not needed doorman, corda persistence is not very generic. // Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException() throw UnsupportedOperationException()
}, SigningServerSchemaService()) }, SchemaService())
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true) val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true)
thread(start = true, isDaemon = true) { thread(start = true, isDaemon = true) {

View File

@ -1,10 +1,7 @@
package com.r3.corda.doorman.persistence package com.r3.corda.networkmanage.common.persistence
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.hibernate.envers.Audited
import java.security.cert.CertPath import java.security.cert.CertPath
import java.time.Instant
import javax.persistence.*
/** /**
* Provide certificate signing request storage for the certificate signing server. * Provide certificate signing request storage for the certificate signing server.
@ -58,65 +55,6 @@ interface CertificationRequestStorage {
fun putCertificatePath(requestId: String, certificates: CertPath, signedBy: List<String>) fun putCertificatePath(requestId: String, certificates: CertPath, signedBy: List<String>)
} }
@Entity
@Table(name = "certificate_signing_request", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash")))
class CertificateSigningRequest(
@Id
@Column(name = "request_id", length = 64)
var requestId: String = "",
// TODO: Store X500Name with a proper schema.
@Column(name = "legal_name", length = 256)
var legalName: String = "",
@Lob
@Column
var request: ByteArray = ByteArray(0),
@Audited
@Column(name = "status")
@Enumerated(EnumType.STRING)
var status: RequestStatus = RequestStatus.New,
@Audited
@Column(name = "modified_by", length = 512)
@ElementCollection(targetClass = String::class, fetch = FetchType.EAGER)
var modifiedBy: List<String> = emptyList(),
@Audited
@Column(name = "modified_at")
var modifiedAt: Instant? = Instant.now(),
@Audited
@Column(name = "remark", length = 256, nullable = true)
var remark: String? = null,
// TODO: The certificate data can have its own table.
@Embedded
var certificateData: CertificateData? = null
)
@Embeddable
class CertificateData(
@Column(name = "public_key_hash", length = 64, nullable = true)
var publicKeyHash: String? = null,
@Lob
@Column(nullable = true)
var certificatePath: ByteArray? = null,
@Column(name = "certificate_status", nullable = true)
var certificateStatus: CertificateStatus? = null
)
enum class CertificateStatus {
VALID, SUSPENDED, REVOKED
}
enum class RequestStatus {
New, Approved, Rejected, Signed
}
sealed class CertificateResponse { sealed class CertificateResponse {
object NotReady : CertificateResponse() object NotReady : CertificateResponse()
data class Ready(val certificatePath: CertPath) : CertificateResponse() data class Ready(val certificatePath: CertPath) : CertificateResponse()

View File

@ -1,7 +1,6 @@
package com.r3.corda.doorman.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.doorman.hash import com.r3.corda.networkmanage.common.persistence.RequestStatus.*
import com.r3.corda.doorman.persistence.RequestStatus.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.x500Name import net.corda.core.internal.x500Name

View File

@ -0,0 +1,87 @@
package com.r3.corda.networkmanage.common.persistence
import org.hibernate.envers.Audited
import java.time.Instant
import javax.persistence.*
@Entity
@Table(name = "certificate_signing_request", indexes = arrayOf(Index(name = "IDX_PUB_KEY_HASH", columnList = "public_key_hash")))
class CertificateSigningRequest(
@Id
@Column(name = "request_id", length = 64)
var requestId: String = "",
// TODO: Store X500Name with a proper schema.
@Column(name = "legal_name", length = 256)
var legalName: String = "",
@Lob
@Column
var request: ByteArray = ByteArray(0),
@Audited
@Column(name = "status")
@Enumerated(EnumType.STRING)
var status: RequestStatus = RequestStatus.New,
@Audited
@Column(name = "modified_by", length = 512)
@ElementCollection(targetClass = String::class, fetch = FetchType.EAGER)
var modifiedBy: List<String> = emptyList(),
@Audited
@Column(name = "modified_at")
var modifiedAt: Instant? = Instant.now(),
@Audited
@Column(name = "remark", length = 256, nullable = true)
var remark: String? = null,
// TODO: The certificate data can have its own table.
@Embedded
var certificateData: CertificateData? = null
)
@Embeddable
class CertificateData(
@Column(name = "public_key_hash", length = 64, nullable = true)
var publicKeyHash: String? = null,
@Lob
@Column(nullable = true)
var certificatePath: ByteArray? = null,
@Column(name = "certificate_status", nullable = true)
var certificateStatus: CertificateStatus? = null
)
enum class CertificateStatus {
VALID, SUSPENDED, REVOKED
}
enum class RequestStatus {
New, Approved, Rejected, Signed
}
@Entity
@Table(name = "node_info")
class NodeInfoEntity(
@Id
@Column(name = "node_info_hash", length = 64)
var nodeInfoHash: String = "",
@Lob
@Column(name = "node_info")
var nodeInfo: ByteArray = ByteArray(0)
)
@Entity
@Table(name = "public_key_node_info_link")
class PublicKeyNodeInfoLink(
@Id
@Column(name = "public_key_hash", length = 64)
var publicKeyHash: String = "",
@Column(name = "node_info_hash", length = 64)
var nodeInfoHash: String = ""
)

View File

@ -1,8 +1,7 @@
package com.r3.corda.doorman.persistence package com.r3.corda.networkmanage.common.persistence
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import java.security.cert.CertPath import java.security.cert.CertPath
import javax.persistence.*
interface NodeInfoStorage { interface NodeInfoStorage {
/** /**
@ -28,26 +27,3 @@ interface NodeInfoStorage {
*/ */
fun putNodeInfo(nodeInfo: NodeInfo) fun putNodeInfo(nodeInfo: NodeInfo)
} }
@Entity
@Table(name = "node_info")
class NodeInfoEntity(
@Id
@Column(name = "node_info_hash", length = 64)
var nodeInfoHash: String = "",
@Lob
@Column(name = "node_info")
var nodeInfo: ByteArray = ByteArray(0)
)
@Entity
@Table(name = "public_key_node_info_link")
class PublicKeyNodeInfoLink(
@Id
@Column(name = "public_key_hash", length = 64)
var publicKeyHash: String = "",
@Column(name = "node_info_hash", length = 64)
var nodeInfoHash: String = ""
)

View File

@ -1,7 +1,6 @@
package com.r3.corda.doorman.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.doorman.buildCertPath import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.hash
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.serialization.deserialize import net.corda.core.serialization.deserialize

View File

@ -0,0 +1,7 @@
package com.r3.corda.networkmanage.common.persistence
import net.corda.core.crypto.sha256
import java.security.PublicKey
// TODO: replace this with Crypto.hash when its available.
fun PublicKey.hash() = encoded.sha256().toString()

View File

@ -1,15 +1,15 @@
package com.r3.corda.doorman.persistence package com.r3.corda.networkmanage.common.persistence
import net.corda.core.contracts.ContractState import net.corda.core.contracts.ContractState
import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState import net.corda.core.schemas.PersistentState
import net.corda.node.services.api.SchemaService import net.corda.node.services.api.SchemaService
class DoormanSchemaService : SchemaService { class SchemaService : SchemaService {
// Entities for compulsory services // Entities for compulsory services
object DoormanServices object SchemaServices
object DoormanServicesV1 : MappedSchema(schemaFamily = DoormanServices.javaClass, version = 1, object DoormanServicesV1 : MappedSchema(schemaFamily = SchemaServices.javaClass, version = 1,
mappedTypes = listOf(CertificateSigningRequest::class.java, NodeInfoEntity::class.java, PublicKeyNodeInfoLink::class.java)) mappedTypes = listOf(CertificateSigningRequest::class.java, NodeInfoEntity::class.java, PublicKeyNodeInfoLink::class.java))
override var schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = mapOf(Pair(DoormanServicesV1, SchemaService.SchemaOptions())) override var schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = mapOf(Pair(DoormanServicesV1, SchemaService.SchemaOptions()))

View File

@ -0,0 +1,44 @@
package com.r3.corda.networkmanage.common.utils
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory
import joptsimple.ArgumentAcceptingOptionSpec
import joptsimple.OptionParser
import org.bouncycastle.cert.X509CertificateHolder
import java.io.ByteArrayInputStream
import java.security.cert.CertPath
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Unit): Config {
val parser = OptionParser()
val helpOption = parser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp();
registerOptions(parser)
val optionSet = parser.parse(*this)
// Print help and exit on help option.
if (optionSet.has(helpOption)) {
throw ShowHelpException(parser)
}
// Convert all command line options to Config.
return ConfigFactory.parseMap(parser.recognizedOptions().mapValues {
val optionSpec = it.value
if (optionSpec is ArgumentAcceptingOptionSpec<*> && !optionSpec.requiresArgument() && optionSet.has(optionSpec)) true else optionSpec.value(optionSet)
}.filterValues { it != null })
}
class ShowHelpException(val parser: OptionParser) : Exception()
object CertificateUtilities {
fun toX509Certificate(byteArray: ByteArray): X509Certificate {
return CertificateFactory.getInstance("X509").generateCertificate(ByteArrayInputStream(byteArray)) as X509Certificate
}
}
fun X509CertificateHolder.toX509Certificate(): Certificate = CertificateUtilities.toX509Certificate(encoded)
fun buildCertPath(vararg certificates: Certificate): CertPath {
return CertificateFactory.getInstance("X509").generateCertPath(certificates.asList())
}
fun buildCertPath(certPathBytes: ByteArray): CertPath = CertificateFactory.getInstance("X509").generateCertPath(certPathBytes.inputStream())

View File

@ -1,6 +1,6 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.r3.corda.doorman.OptionParserHelper.toConfigWithOptions import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import net.corda.core.internal.div import net.corda.core.internal.div

View File

@ -1,4 +1,4 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.atlassian.jira.rest.client.api.JiraRestClient import com.atlassian.jira.rest.client.api.JiraRestClient
import com.atlassian.jira.rest.client.api.domain.Field import com.atlassian.jira.rest.client.api.domain.Field

View File

@ -1,17 +1,18 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory
import com.r3.corda.doorman.DoormanServer.Companion.logger import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
import com.r3.corda.doorman.persistence.CertificationRequestStorage import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE import com.r3.corda.networkmanage.common.persistence.DBCertificateRequestStorage
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage import com.r3.corda.networkmanage.common.persistence.PersistenceNodeInfoStorage
import com.r3.corda.doorman.persistence.DoormanSchemaService import com.r3.corda.networkmanage.common.persistence.SchemaService
import com.r3.corda.doorman.persistence.PersistenceNodeInfoStorage import com.r3.corda.networkmanage.common.utils.ShowHelpException
import com.r3.corda.doorman.signer.DefaultCsrHandler import com.r3.corda.networkmanage.doorman.DoormanServer.Companion.logger
import com.r3.corda.doorman.signer.JiraCsrHandler import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
import com.r3.corda.doorman.signer.Signer import com.r3.corda.networkmanage.doorman.signer.JiraCsrHandler
import com.r3.corda.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.doorman.signer.Signer
import com.r3.corda.doorman.webservice.RegistrationWebService import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.createDirectories import net.corda.core.internal.createDirectories
@ -238,7 +239,7 @@ fun main(args: Array<String>) {
keystorePassword, keystorePassword,
caPrivateKeyPassword) caPrivateKeyPassword)
DoormanParameters.Mode.DOORMAN -> { DoormanParameters.Mode.DOORMAN -> {
val database = configureDatabase(dataSourceProperties, databaseProperties, { throw UnsupportedOperationException() }, DoormanSchemaService()) val database = configureDatabase(dataSourceProperties, databaseProperties, { throw UnsupportedOperationException() }, SchemaService())
val signer = buildLocalSigner(this) val signer = buildLocalSigner(this)
startDoorman(NetworkHostAndPort(host, port), database, approveAll, signer, jiraConfig) startDoorman(NetworkHostAndPort(host, port), database, approveAll, signer, jiraConfig)
} }

View File

@ -1,11 +1,11 @@
package com.r3.corda.doorman.signer package com.r3.corda.networkmanage.doorman.signer
import com.r3.corda.doorman.JiraClient import com.r3.corda.networkmanage.common.persistence.CertificateResponse
import com.r3.corda.doorman.buildCertPath import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
import com.r3.corda.doorman.persistence.CertificateResponse import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.persistence.CertificationRequestStorage import com.r3.corda.networkmanage.common.persistence.RequestStatus
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.persistence.RequestStatus import com.r3.corda.networkmanage.doorman.JiraClient
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
interface CsrHandler { interface CsrHandler {

View File

@ -1,7 +1,7 @@
package com.r3.corda.doorman.signer package com.r3.corda.networkmanage.doorman.signer
import com.r3.corda.doorman.buildCertPath import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.toX509Certificate import com.r3.corda.networkmanage.common.utils.toX509Certificate
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.toX509CertHolder import net.corda.core.internal.toX509CertHolder
import net.corda.core.internal.x500Name import net.corda.core.internal.x500Name

View File

@ -1,7 +1,7 @@
package com.r3.corda.doorman.webservice package com.r3.corda.networkmanage.doorman.webservice
import com.r3.corda.doorman.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.doorman.webservice.NodeInfoWebService.Companion.networkMapPath import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.networkMapPath
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignedData import net.corda.core.crypto.SignedData
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
@ -25,6 +25,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage) {
companion object { companion object {
const val networkMapPath = "network-map" const val networkMapPath = "network-map"
} }
@POST @POST
@Path("register") @Path("register")
@Consumes(MediaType.APPLICATION_OCTET_STREAM) @Consumes(MediaType.APPLICATION_OCTET_STREAM)

View File

@ -1,8 +1,8 @@
package com.r3.corda.doorman.webservice package com.r3.corda.networkmanage.doorman.webservice
import com.r3.corda.doorman.DoormanServerStatus import com.r3.corda.networkmanage.common.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificateResponse import com.r3.corda.networkmanage.doorman.DoormanServerStatus
import com.r3.corda.doorman.signer.CsrHandler import com.r3.corda.networkmanage.doorman.signer.CsrHandler
import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_CA import net.corda.node.utilities.X509Utilities.CORDA_CLIENT_CA
import net.corda.node.utilities.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.node.utilities.X509Utilities.CORDA_INTERMEDIATE_CA
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA

View File

@ -1,16 +1,16 @@
package com.r3.corda.signing package com.r3.corda.networkmanage.hsm
import com.r3.corda.signing.authentication.Authenticator import com.r3.corda.networkmanage.common.persistence.SchemaService
import com.r3.corda.signing.authentication.createProvider import com.r3.corda.networkmanage.hsm.authentication.Authenticator
import com.r3.corda.signing.configuration.Parameters import com.r3.corda.networkmanage.hsm.authentication.createProvider
import com.r3.corda.signing.configuration.parseParameters import com.r3.corda.networkmanage.hsm.configuration.Parameters
import com.r3.corda.signing.generator.KeyCertificateGenerator import com.r3.corda.networkmanage.hsm.configuration.parseParameters
import com.r3.corda.signing.hsm.HsmSigner import com.r3.corda.networkmanage.hsm.generator.KeyCertificateGenerator
import com.r3.corda.signing.menu.Menu import com.r3.corda.networkmanage.hsm.menu.Menu
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
import com.r3.corda.signing.persistence.DBCertificateRequestStorage import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
import com.r3.corda.signing.persistence.SigningServerSchemaService import com.r3.corda.networkmanage.hsm.signer.HsmSigner
import com.r3.corda.signing.utils.mapCryptoServerException import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
fun main(args: Array<String>) { fun main(args: Array<String>) {
@ -24,11 +24,11 @@ fun run(parameters: Parameters) {
val database = configureDatabase(dataSourceProperties, databaseProperties, { val database = configureDatabase(dataSourceProperties, databaseProperties, {
// Identity service not needed // Identity service not needed
throw UnsupportedOperationException() throw UnsupportedOperationException()
}, SigningServerSchemaService()) }, SchemaService())
val storage = DBCertificateRequestStorage(database) val storage = DBSignedCertificateRequestStorage(database)
val provider = createProvider() val provider = createProvider()
val sign: (List<ApprovedCertificateRequestData>) -> Unit = { val sign: (List<CertificateRequestData>) -> Unit = {
val signer = HsmSigner( val signer = HsmSigner(
storage, storage,
certificateName, certificateName,
@ -85,7 +85,7 @@ private fun processError(exception: Exception) {
println("An error occured: ${processed.message}") println("An error occured: ${processed.message}")
} }
private fun confirmedSign(selectedItems: List<ApprovedCertificateRequestData>): Boolean { private fun confirmedSign(selectedItems: List<CertificateRequestData>): Boolean {
println("Are you sure you want to sign the following requests:") println("Are you sure you want to sign the following requests:")
selectedItems.forEachIndexed { index, data -> selectedItems.forEachIndexed { index, data ->
println("${index + 1} ${data.request.subject}") println("${index + 1} ${data.request.subject}")
@ -102,7 +102,7 @@ private fun confirmedKeyGen(): Boolean {
return result return result
} }
private fun getSelection(toSelect: List<ApprovedCertificateRequestData>): List<ApprovedCertificateRequestData> { private fun getSelection(toSelect: List<CertificateRequestData>): List<CertificateRequestData> {
print("CSRs to be signed (comma separated list): ") print("CSRs to be signed (comma separated list): ")
val line = readLine() val line = readLine()
if (line == null) { if (line == null) {

View File

@ -1,4 +1,4 @@
package com.r3.corda.signing.authentication package com.r3.corda.networkmanage.hsm.authentication
/* /*
* Supported authentication modes * Supported authentication modes

View File

@ -1,7 +1,7 @@
package com.r3.corda.signing.authentication package com.r3.corda.networkmanage.hsm.authentication
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
import com.r3.corda.signing.configuration.Parameters import com.r3.corda.networkmanage.hsm.configuration.Parameters
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.Console import java.io.Console

View File

@ -1,36 +1,16 @@
package com.r3.corda.signing.configuration package com.r3.corda.networkmanage.hsm.configuration
import com.typesafe.config.Config import com.r3.corda.networkmanage.common.utils.toConfigWithOptions
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import joptsimple.ArgumentAcceptingOptionSpec
import joptsimple.OptionParser
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.X509Utilities
import net.corda.nodeapi.config.parseAs import net.corda.nodeapi.config.parseAs
import com.r3.corda.signing.authentication.AuthMode
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.Paths import java.nio.file.Paths
import java.util.* import java.util.*
class ShowHelpException(val parser: OptionParser) : Exception()
fun Array<out String>.toConfigWithOptions(registerOptions: OptionParser.() -> Unit): Config {
val parser = OptionParser()
val helpOption = parser.acceptsAll(listOf("h", "?", "help"), "show help").forHelp();
registerOptions(parser)
val optionSet = parser.parse(*this)
// Print help and exit on help option.
if (optionSet.has(helpOption)) {
throw ShowHelpException(parser)
}
// Convert all command line options to Config.
return ConfigFactory.parseMap(parser.recognizedOptions().mapValues {
val optionSpec = it.value
if (optionSpec is ArgumentAcceptingOptionSpec<*> && !optionSpec.requiresArgument() && optionSet.has(optionSpec)) true else optionSpec.value(optionSet)
}.filterValues { it != null })
}
/** /**
* Configuration parameters. * Configuration parameters.
*/ */

View File

@ -1,14 +1,14 @@
package com.r3.corda.signing.generator package com.r3.corda.networkmanage.hsm.generator
import CryptoServerCXI.CryptoServerCXI import CryptoServerCXI.CryptoServerCXI
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.createIntermediateCert
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.createSelfSignedCACert
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getCleanEcdsaKeyPair
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.retrieveCertificateAndKeys
import net.corda.node.utilities.addOrReplaceKey import net.corda.node.utilities.addOrReplaceKey
import com.r3.corda.signing.authentication.Authenticator
import com.r3.corda.signing.utils.X509Utilities.createIntermediateCert
import com.r3.corda.signing.utils.X509Utilities.createSelfSignedCACert
import com.r3.corda.signing.utils.X509Utilities.getAndInitializeKeyStore
import com.r3.corda.signing.utils.X509Utilities.getCleanEcdsaKeyPair
import com.r3.corda.signing.utils.X509Utilities.retrieveCertificateAndKeys
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyStore import java.security.KeyStore
import java.security.PrivateKey import java.security.PrivateKey

View File

@ -1,4 +1,4 @@
package com.r3.corda.signing.menu package com.r3.corda.networkmanage.hsm.menu
data class MenuItem(val key: String, val label: String, val action: () -> Unit, val isTerminating: Boolean = false) data class MenuItem(val key: String, val label: String, val action: () -> Unit, val isTerminating: Boolean = false)

View File

@ -0,0 +1,27 @@
package com.r3.corda.networkmanage.hsm.persistence
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest
import com.r3.corda.networkmanage.common.persistence.DBCertificateRequestStorage
import com.r3.corda.networkmanage.common.persistence.RequestStatus
import net.corda.node.utilities.CordaPersistence
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import java.security.cert.CertPath
data class CertificateRequestData(val requestId: String, val request: PKCS10CertificationRequest, var certPath: CertPath? = null)
class DBSignedCertificateRequestStorage(database: CordaPersistence) : SignedCertificateRequestStorage {
private val storage = DBCertificateRequestStorage(database)
override fun store(requests: List<CertificateRequestData>, signers: List<String>) {
for ((requestId, _, certPath) in requests) {
storage.putCertificatePath(requestId, certPath!!, signers)
}
}
override fun getApprovedRequests(): List<CertificateRequestData> {
return storage.getRequests(RequestStatus.Approved).map { it.toRequestData() }
}
private fun CertificateSigningRequest.toRequestData() = CertificateRequestData(requestId, PKCS10CertificationRequest(request))
}

View File

@ -1,19 +1,20 @@
package com.r3.corda.signing.persistence package com.r3.corda.networkmanage.hsm.persistence
/** /**
* Provides an API for database level manipulations of CSRs (Certificate Signing Requests). * Provides an API for storing signed CSRs (Certificate Signing Requests).
*/ */
interface CertificateRequestStorage { interface SignedCertificateRequestStorage {
/** /**
* Returns all certificate signing requests that have been approved for signing. * Returns all certificate signing requests that have been approved for signing.
*/ */
fun getApprovedRequests(): List<ApprovedCertificateRequestData> fun getApprovedRequests(): List<CertificateRequestData>
/** /**
* Marks the database CSR entries as signed. Also it persists the certificate and the signature in the database. * Marks the database CSR entries as signed. Also it persists the certificate and the signature in the database.
* *
* @param requests Requests that are to be marked as signed. * @param requests Signed requests that are to be stored.
* @param signers List of user names that signed those requests. To be specific, each request has been signed by all of those users. * @param signers List of user names that signed those requests. To be specific, each request has been signed by all of those users.
*/ */
fun sign(requests: List<ApprovedCertificateRequestData>, signers: List<String>) fun store(requests: List<CertificateRequestData>, signers: List<String>)
} }

View File

@ -1,18 +1,18 @@
package com.r3.corda.signing.hsm package com.r3.corda.networkmanage.hsm.signer
import com.r3.corda.signing.authentication.Authenticator import com.r3.corda.networkmanage.hsm.authentication.Authenticator
import com.r3.corda.signing.authentication.readPassword import com.r3.corda.networkmanage.hsm.authentication.readPassword
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
import com.r3.corda.signing.persistence.DBCertificateRequestStorage import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
import com.r3.corda.signing.utils.X509Utilities.buildCertPath import com.r3.corda.networkmanage.hsm.utils.X509Utilities.buildCertPath
import com.r3.corda.signing.utils.X509Utilities.createClientCertificate import com.r3.corda.networkmanage.hsm.utils.X509Utilities.createClientCertificate
import com.r3.corda.signing.utils.X509Utilities.getAndInitializeKeyStore import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
import com.r3.corda.signing.utils.X509Utilities.retrieveCertificateAndKeys import com.r3.corda.networkmanage.hsm.utils.X509Utilities.retrieveCertificateAndKeys
/** /**
* Encapsulates certificate signing logic * Encapsulates certificate signing logic
*/ */
class HsmSigner(private val storage: DBCertificateRequestStorage, class HsmSigner(private val storage: SignedCertificateRequestStorage,
private val caCertificateName: String, private val caCertificateName: String,
private val caPrivateKeyPass: String?, private val caPrivateKeyPass: String?,
private val caParentCertificateName: String, private val caParentCertificateName: String,
@ -29,7 +29,7 @@ class HsmSigner(private val storage: DBCertificateRequestStorage,
* and sets the certificate field with an appropriate value. * and sets the certificate field with an appropriate value.
* @param toSign list of approved certificates to be signed * @param toSign list of approved certificates to be signed
*/ */
override fun sign(toSign: List<ApprovedCertificateRequestData>) { override fun sign(toSign: List<CertificateRequestData>) {
authenticator.connectAndAuthenticate { provider, signers -> authenticator.connectAndAuthenticate { provider, signers ->
val keyStore = getAndInitializeKeyStore(provider, keyStorePassword) val keyStore = getAndInitializeKeyStore(provider, keyStorePassword)
// This should be changed once we allow for more certificates in the chain. Preferably we should use // This should be changed once we allow for more certificates in the chain. Preferably we should use
@ -40,7 +40,7 @@ class HsmSigner(private val storage: DBCertificateRequestStorage,
toSign.forEach { toSign.forEach {
it.certPath = buildCertPath(createClientCertificate(caCertAndKey, it.request, validDays, provider), caParentCertificate) it.certPath = buildCertPath(createClientCertificate(caCertAndKey, it.request, validDays, provider), caParentCertificate)
} }
storage.sign(toSign, signers) storage.store(toSign, signers)
println("The following certificates have been signed by $signers:") println("The following certificates have been signed by $signers:")
toSign.forEachIndexed { index, data -> toSign.forEachIndexed { index, data ->
println("${index + 1} ${data.request.subject}") println("${index + 1} ${data.request.subject}")

View File

@ -0,0 +1,15 @@
package com.r3.corda.networkmanage.hsm.signer
import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
/**
* Encapsulates the logic related to the certificate signing process.
*/
interface Signer {
/**
* Signs the provided list of [CertificateRequestData]
*/
fun sign(toSign: List<CertificateRequestData>)
}

View File

@ -1,7 +1,7 @@
package com.r3.corda.signing.utils package com.r3.corda.networkmanage.hsm.utils
import CryptoServerAPI.CryptoServerException import CryptoServerAPI.CryptoServerException
import java.util.HashMap import java.util.*
/** /**
* CryptoServer error translator object. * CryptoServer error translator object.

View File

@ -1,4 +1,4 @@
package com.r3.corda.signing.utils package com.r3.corda.networkmanage.hsm.utils
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -74,7 +74,7 @@ object X509Utilities {
purposes.add(KeyPurposeId.id_kp_serverAuth) purposes.add(KeyPurposeId.id_kp_serverAuth)
purposes.add(KeyPurposeId.id_kp_clientAuth) purposes.add(KeyPurposeId.id_kp_clientAuth)
purposes.add(KeyPurposeId.anyExtendedKeyUsage) purposes.add(KeyPurposeId.anyExtendedKeyUsage)
builder.addExtension(Extension.extendedKeyUsage, false, DERSequence(purposes)) builder.addExtension(Extension.extendedKeyUsage, false, DERSequence(purposes).toASN1Primitive())
val cert = signCertificate(builder, keyPair.private, provider) val cert = signCertificate(builder, keyPair.private, provider)

View File

@ -1,11 +1,8 @@
package com.r3.corda.doorman.internal.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.doorman.buildCertPath import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.persistence.CertificateSigningRequest import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.persistence.RequestStatus
import com.r3.corda.doorman.toX509Certificate
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -24,7 +21,6 @@ import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
import java.util.* import java.util.*
import kotlin.test.* import kotlin.test.*
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
class DBCertificateRequestStorageTest { class DBCertificateRequestStorageTest {
private lateinit var storage: DBCertificateRequestStorage private lateinit var storage: DBCertificateRequestStorage
@ -32,7 +28,7 @@ class DBCertificateRequestStorageTest {
@Before @Before
fun startDb() { fun startDb() {
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, DoormanSchemaService()) persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, SchemaService())
storage = DBCertificateRequestStorage(persistence) storage = DBCertificateRequestStorage(persistence)
} }

View File

@ -1,10 +1,8 @@
package com.r3.corda.doorman.internal.persistence package com.r3.corda.networkmanage.common.persistence
import com.r3.corda.doorman.buildCertPath import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.hash import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.persistence.* import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
import com.r3.corda.doorman.toX509Certificate
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
@ -59,7 +57,7 @@ class PersistenceNodeInfoStorageTest {
@Before @Before
fun startDb() { fun startDb() {
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, DoormanSchemaService()) persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, SchemaService())
nodeInfoStorage = PersistenceNodeInfoStorage(persistence) nodeInfoStorage = PersistenceNodeInfoStorage(persistence)
requestStorage = DBCertificateRequestStorage(persistence) requestStorage = DBCertificateRequestStorage(persistence)
} }

View File

@ -1,12 +1,14 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.times import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.verify
import com.r3.corda.doorman.persistence.* import com.r3.corda.networkmanage.common.persistence.*
import com.r3.corda.doorman.signer.DefaultCsrHandler import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.signer.Signer import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
import com.r3.corda.networkmanage.doorman.signer.Signer
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.node.utilities.X509Utilities import net.corda.node.utilities.X509Utilities

View File

@ -1,4 +1,4 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import org.junit.Test import org.junit.Test
@ -9,8 +9,8 @@ import kotlin.test.assertFailsWith
class DoormanParametersTest { class DoormanParametersTest {
private val testDummyPath = ".${File.separator}testDummyPath.jks" private val testDummyPath = ".${File.separator}testDummyPath.jks"
private val validConfigPath = File(javaClass.getResource("/node.conf").toURI()).absolutePath private val validConfigPath = File(javaClass.getResource("/doorman.conf").toURI()).absolutePath
private val invalidConfigPath = File(javaClass.getResource("/node_fail.conf").toURI()).absolutePath private val invalidConfigPath = File(javaClass.getResource("/doorman_fail.conf").toURI()).absolutePath
@Test @Test
fun `parse mode flag arg correctly`() { fun `parse mode flag arg correctly`() {

View File

@ -1,11 +1,13 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.nhaarman.mockito_kotlin.any import com.nhaarman.mockito_kotlin.any
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import com.nhaarman.mockito_kotlin.times import com.nhaarman.mockito_kotlin.times
import com.nhaarman.mockito_kotlin.verify import com.nhaarman.mockito_kotlin.verify
import com.r3.corda.doorman.persistence.NodeInfoStorage import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
import com.r3.corda.doorman.webservice.NodeInfoWebService import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate

View File

@ -1,9 +1,11 @@
package com.r3.corda.doorman package com.r3.corda.networkmanage.doorman
import com.nhaarman.mockito_kotlin.* import com.nhaarman.mockito_kotlin.*
import com.r3.corda.doorman.persistence.CertificateResponse import com.r3.corda.networkmanage.common.persistence.CertificateResponse
import com.r3.corda.doorman.signer.CsrHandler import com.r3.corda.networkmanage.common.utils.buildCertPath
import com.r3.corda.doorman.webservice.RegistrationWebService import com.r3.corda.networkmanage.common.utils.toX509Certificate
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
import com.r3.corda.networkmanage.doorman.webservice.RegistrationWebService
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name

View File

@ -1,4 +1,4 @@
package com.r3.corda.signing.authentication package com.r3.corda.networkmanage.hsm.authentication
import CryptoServerCXI.CryptoServerCXI import CryptoServerCXI.CryptoServerCXI
import CryptoServerJCE.CryptoServerProvider import CryptoServerJCE.CryptoServerProvider

View File

@ -1,15 +1,15 @@
package com.r3.corda.signing.configuration package com.r3.corda.networkmanage.hsm.configuration
import com.r3.corda.networkmanage.hsm.authentication.AuthMode
import com.typesafe.config.ConfigException import com.typesafe.config.ConfigException
import com.r3.corda.signing.authentication.AuthMode
import org.junit.Test import org.junit.Test
import java.io.File import java.io.File
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
class ConfigurationTest { class ConfigurationTest {
private val validConfigPath = File(javaClass.getResource("/signing_service.conf").toURI()).absolutePath private val validConfigPath = File(javaClass.getResource("/hsm.conf").toURI()).absolutePath
private val invalidConfigPath = File(javaClass.getResource("/signing_service_fail.conf").toURI()).absolutePath private val invalidConfigPath = File(javaClass.getResource("/hsm_fail.conf").toURI()).absolutePath
@Test @Test
fun `authMode is parsed correctly`() { fun `authMode is parsed correctly`() {

View File

@ -43,9 +43,8 @@ include 'samples:simm-valuation-demo'
include 'samples:notary-demo' include 'samples:notary-demo'
include 'samples:bank-of-corda-demo' include 'samples:bank-of-corda-demo'
include 'cordform-common' include 'cordform-common'
include 'doorman' include 'network-management'
include 'verify-enclave' include 'verify-enclave'
include 'sgx-jvm/hsm-tool' include 'sgx-jvm/hsm-tool'
include 'signing-server'
include 'perftestcordapp' include 'perftestcordapp'

View File

@ -1,91 +0,0 @@
ext {
// We use Corda release artifact dependencies instead of project dependencies to make sure each doorman releases are
// align with the corresponding Corda release.
corda_dependency_version = '2.0-20171017.135310-6'
}
version "$corda_dependency_version"
apply plugin: 'us.kirchmeier.capsule'
apply plugin: 'kotlin'
repositories {
mavenLocal()
mavenCentral()
maven {
url 'http://oss.sonatype.org/content/repositories/snapshots'
}
jcenter()
maven {
url 'http://ci-artifactory.corda.r3cev.com/artifactory/corda-dev'
}
}
configurations{
integrationTestCompile.extendsFrom testCompile
integrationTestRuntime.extendsFrom testRuntime
}
sourceSets{
integrationTest {
kotlin {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/kotlin')
}
java {
compileClasspath += main.output + test.output
runtimeClasspath += main.output + test.output
srcDir file('src/integration-test/java')
}
resources {
srcDir file('src/integration-test/resources')
}
}
}
task buildSigningServerJAR(type: FatCapsule, dependsOn: 'jar') {
group = 'build'
applicationClass 'com.r3.corda.signing.MainKt'
capsuleManifest {
applicationVersion = corda_dependency_version
systemProperties['visualvm.display.name'] = 'Signing Server'
minJavaVersion = '1.8.0'
jvmArgs = ['-XX:+UseG1GC']
}
// Make the resulting JAR file directly executable on UNIX by prepending a shell script to it.
// This lets you run the file like so: ./corda.jar
// Other than being slightly less typing, this has one big advantage: Ctrl-C works properly in the terminal.
reallyExecutable { trampolining() }
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile fileTree(dir: 'libs', include: '*.jar')
compile "net.corda:corda-core:$corda_dependency_version"
compile "net.corda:corda-node:$corda_dependency_version"
compile "net.corda:corda-node-api:$corda_dependency_version"
testCompile "net.corda:corda-test-utils:$corda_dependency_version"
testCompile "net.corda:corda-node-driver:$corda_dependency_version"
// Log4J: logging framework (with SLF4J bindings)
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
compile "org.apache.logging.log4j:log4j-core:${log4j_version}"
compile "org.apache.logging.log4j:log4j-web:${log4j_version}"
// JOpt: for command line flags.
compile "net.sf.jopt-simple:jopt-simple:5.0.2"
// TypeSafe Config: for simple and human friendly config files.
compile "com.typesafe:config:1.3.0"
// Hibernate audit plugin
compile "org.hibernate:hibernate-envers:5.2.11.Final"
// Unit testing helpers.
testCompile 'junit:junit:4.12'
testCompile "org.assertj:assertj-core:${assertj_version}"
integrationTestCompile project(':doorman')
}

View File

@ -1,15 +0,0 @@
package com.r3.corda.signing.hsm
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData
/**
* Encapsulates the logic related to the certificate signing process.
*/
interface Signer {
/**
* Signs the provided list of [ApprovedCertificateRequestData]
*/
fun sign(toSign: List<ApprovedCertificateRequestData>)
}

View File

@ -1,102 +0,0 @@
package com.r3.corda.signing.persistence
import net.corda.node.utilities.CordaPersistence
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.hibernate.envers.Audited
import java.security.cert.CertPath
import java.sql.Connection
import java.time.Instant
import javax.persistence.*
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
data class ApprovedCertificateRequestData(val requestId: String, val request: PKCS10CertificationRequest, var certPath: CertPath? = null)
class DBCertificateRequestStorage(private val database: CordaPersistence) : CertificateRequestStorage {
enum class Status {
Approved, Signed
}
@Entity
@Table(name = "certificate_signing_request")
class CertificateSigningRequest(
@Id
@Column(name = "request_id", length = 64)
var requestId: String = "",
@Lob
@Column
var request: ByteArray = ByteArray(0),
@Lob
@Column(nullable = true)
var certificatePath: ByteArray? = null,
@Audited
@Column(name = "status")
@Enumerated(EnumType.STRING)
var status: Status = Status.Approved,
@Audited
@Column(name = "modified_by", length = 512)
@ElementCollection(targetClass = String::class, fetch = FetchType.EAGER)
var modifiedBy: List<String> = emptyList(),
@Audited
@Column(name = "modified_at")
var modifiedAt: Instant? = Instant.now()
)
override fun getApprovedRequests(): List<ApprovedCertificateRequestData> {
return getRequestIdsByStatus(Status.Approved)
}
override fun sign(requests: List<ApprovedCertificateRequestData>, signers: List<String>) {
requests.forEach {
database.transaction(Connection.TRANSACTION_SERIALIZABLE) {
val request = singleRequestWhere { builder, path ->
builder.and(
builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), it.requestId),
builder.equal(path.get<String>(CertificateSigningRequest::status.name), Status.Approved)
)
}
if (request != null) {
val now = Instant.now()
request.certificatePath = it.certPath?.encoded
request.status = Status.Signed
request.modifiedAt = now
request.modifiedBy = signers
session.update(request)
}
}
}
}
private fun singleRequestWhere(predicate: (CriteriaBuilder, Path<CertificateSigningRequest>) -> Predicate): CertificateSigningRequest? {
return database.transaction {
val builder = session.criteriaBuilder
val criteriaQuery = builder.createQuery(CertificateSigningRequest::class.java)
val query = criteriaQuery.from(CertificateSigningRequest::class.java).run {
criteriaQuery.where(predicate(builder, this))
}
session.createQuery(query).uniqueResultOptional().orElse(null)
}
}
private fun getRequestIdsByStatus(status: Status): List<ApprovedCertificateRequestData> {
return database.transaction {
val builder = session.criteriaBuilder
val query = builder.createQuery(CertificateSigningRequest::class.java).run {
from(CertificateSigningRequest::class.java).run {
where(builder.equal(get<Status>(CertificateSigningRequest::status.name), status))
}
}
session.createQuery(query).setLockMode(LockModeType.PESSIMISTIC_WRITE).resultList.map { it.toRequestData() }
}
}
private fun CertificateSigningRequest.toRequestData() = ApprovedCertificateRequestData(requestId, PKCS10CertificationRequest(request))
}

View File

@ -1,20 +0,0 @@
package com.r3.corda.signing.persistence
import net.corda.core.contracts.ContractState
import net.corda.core.schemas.MappedSchema
import net.corda.core.schemas.PersistentState
import net.corda.node.services.api.SchemaService
class SigningServerSchemaService: SchemaService {
// Entities for compulsory services
object SigningServerServices
object SigningServerServicesV1 : MappedSchema(schemaFamily = SigningServerServices.javaClass, version = 1,
mappedTypes = listOf(DBCertificateRequestStorage.CertificateSigningRequest::class.java))
override val schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = mapOf(Pair(SigningServerServicesV1, SchemaService.SchemaOptions()))
override fun selectSchemas(state: ContractState): Iterable<MappedSchema> = setOf(SigningServerServicesV1)
override fun generateMappedObject(state: ContractState, schema: MappedSchema): PersistentState = throw UnsupportedOperationException()
}

View File

@ -1,172 +0,0 @@
package com.r3.corda.signing.persistence
import com.r3.corda.signing.persistence.DBCertificateRequestStorage.CertificateSigningRequest
import com.r3.corda.signing.persistence.DBCertificateRequestStorage.Status
import com.r3.corda.signing.utils.X509Utilities.buildCertPath
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.CordaX500Name
import net.corda.node.utilities.CertificateType
import net.corda.node.utilities.CordaPersistence
import net.corda.node.utilities.X509Utilities
import net.corda.node.utilities.configureDatabase
import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.io.ByteArrayInputStream
import java.security.cert.Certificate
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
import java.util.*
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
class DBCertificateRequestStorageTest {
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val intermediateCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Corda Node Intermediate CA", organisation = "R3 Ltd", locality = "London", country = "GB"), intermediateCAKey)
private lateinit var storage: DBCertificateRequestStorage
private lateinit var persistence: CordaPersistence
@Before
fun startDb() {
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, SigningServerSchemaService())
storage = DBCertificateRequestStorage(persistence)
}
@After
fun closeDb() {
persistence.close()
}
@Test
fun `getApprovedRequests returns only requests with status APPROVED`() {
// given
(1..10).forEach {
createAndPersistRequest("Bank$it", Status.Approved)
}
(11..15).forEach {
createAndPersistRequest("Bank$it", Status.Signed)
}
// when
val result = storage.getApprovedRequests()
// then
assertEquals(10, result.size)
result.forEach {
val request = getRequestById(it.requestId)
assertNotNull(request)
assertEquals(Status.Approved, request?.status)
}
}
@Test
fun `sign changes the status of requests to SIGNED`() {
// given
(1..10).map {
createAndPersistRequest("Bank$it")
}
val requests = storage.getApprovedRequests()
// Create a signed certificate
requests.forEach { certifyAndSign(it) }
val signers = listOf("TestUserA", "TestUserB")
// when
storage.sign(requests, signers)
// then
requests.forEach {
val request = getRequestById(it.requestId)
assertNotNull(request)
assertEquals(Status.Signed, request?.status)
assertEquals(signers.toString(), request?.modifiedBy.toString())
assertNotNull(request?.certificatePath)
}
}
private fun certifyAndSign(approvedRequestData: ApprovedCertificateRequestData) {
JcaPKCS10CertificationRequest(approvedRequestData.request).run {
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, subject))), arrayOf())
approvedRequestData.certPath = buildCertPath(
X509Utilities.createCertificate(
CertificateType.CLIENT_CA,
intermediateCACert,
intermediateCAKey,
subject,
publicKey,
nameConstraints = nameConstraints).toX509Certificate())
}
}
private fun getRequestById(requestId: String): CertificateSigningRequest? {
return persistence.transaction {
singleRequestWhere { builder, path ->
builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), requestId)
}
}
}
private fun singleRequestWhere(predicate: (CriteriaBuilder, Path<CertificateSigningRequest>) -> Predicate): CertificateSigningRequest? {
return persistence.transaction {
val builder = session.criteriaBuilder
val criteriaQuery = builder.createQuery(CertificateSigningRequest::class.java)
val query = criteriaQuery.from(CertificateSigningRequest::class.java).run {
criteriaQuery.where(predicate(builder, this))
}
session.createQuery(query).uniqueResultOptional().orElse(null)
}
}
private fun createAndPersistRequest(legalName: String, status: Status = Status.Approved): String {
val requestId = SecureHash.randomSHA256().toString()
persistence.transaction {
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val x500Name = CordaX500Name(organisation = legalName, locality = "London", country = "GB")
session.save(CertificateSigningRequest(
requestId = requestId,
status = status,
request = X509Utilities.createCertificateSigningRequest(x500Name, "my@mail.com", keyPair).encoded
))
}
return requestId
}
private fun makeTestDataSourceProperties(nodeName: String = SecureHash.randomSHA256().toString()): Properties {
val props = Properties()
props.setProperty("dataSourceClassName", "org.h2.jdbcx.JdbcDataSource")
props.setProperty("dataSource.url", "jdbc:h2:mem:${nodeName}_persistence;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE")
props.setProperty("dataSource.user", "sa")
props.setProperty("dataSource.password", "")
return props
}
private fun makeTestDatabaseProperties(key: String? = null, value: String? = null): Properties {
val props = Properties()
props.setProperty("transactionIsolationLevel", "repeatableRead") //for other possible values see net.corda.node.utilities.CordaPeristence.parserTransactionIsolationLevel(String)
if (key != null) {
props.setProperty(key, value)
}
return props
}
}
private object CertificateUtilities {
fun toX509Certificate(byteArray: ByteArray): X509Certificate {
return CertificateFactory.getInstance("X509").generateCertificate(ByteArrayInputStream(byteArray)) as X509Certificate
}
}
/**
* Converts [X509CertificateHolder] to standard Java [Certificate]
*/
private fun X509CertificateHolder.toX509Certificate(): Certificate = CertificateUtilities.toX509Certificate(encoded)