mirror of
https://github.com/corda/corda.git
synced 2025-02-05 10:39:13 +00:00
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:
parent
0ae205ec25
commit
dfb226fbbb
@ -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()
|
|
@ -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"
|
@ -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
|
@ -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
|
@ -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) {
|
@ -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()
|
@ -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
|
@ -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 = ""
|
||||||
|
)
|
@ -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 = ""
|
|
||||||
)
|
|
@ -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
|
@ -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()
|
@ -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()))
|
@ -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())
|
@ -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
|
@ -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
|
@ -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)
|
||||||
}
|
}
|
@ -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 {
|
@ -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
|
@ -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)
|
@ -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
|
@ -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) {
|
@ -1,4 +1,4 @@
|
|||||||
package com.r3.corda.signing.authentication
|
package com.r3.corda.networkmanage.hsm.authentication
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Supported authentication modes
|
* Supported authentication modes
|
@ -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
|
@ -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.
|
||||||
*/
|
*/
|
@ -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
|
@ -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)
|
||||||
|
|
@ -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))
|
||||||
|
}
|
@ -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>)
|
||||||
}
|
}
|
@ -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}")
|
@ -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>)
|
||||||
|
|
||||||
|
}
|
@ -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.
|
@ -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)
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
@ -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
|
@ -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`() {
|
@ -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
|
@ -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
|
@ -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
|
@ -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`() {
|
@ -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'
|
||||||
|
|
||||||
|
@ -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')
|
|
||||||
}
|
|
@ -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>)
|
|
||||||
|
|
||||||
}
|
|
@ -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))
|
|
||||||
}
|
|
@ -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()
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -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)
|
|
@ -1 +0,0 @@
|
|||||||
mock-maker-inline
|
|
Loading…
x
Reference in New Issue
Block a user