mirror of
https://github.com/corda/corda.git
synced 2025-01-13 08:20:01 +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"
|
||||
|
||||
description 'Network management module encapsulating components such as Doorman, HSM Signing Service and Network Map'
|
||||
|
||||
apply plugin: 'us.kirchmeier.capsule'
|
||||
apply plugin: 'kotlin'
|
||||
|
||||
@ -21,12 +23,12 @@ repositories {
|
||||
}
|
||||
}
|
||||
|
||||
configurations{
|
||||
configurations {
|
||||
integrationTestCompile.extendsFrom testCompile
|
||||
integrationTestRuntime.extendsFrom testRuntime
|
||||
}
|
||||
|
||||
sourceSets{
|
||||
sourceSets {
|
||||
integrationTest {
|
||||
kotlin {
|
||||
compileClasspath += main.output + test.output
|
||||
@ -46,8 +48,8 @@ sourceSets{
|
||||
|
||||
task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') {
|
||||
group = 'build'
|
||||
applicationClass 'com.r3.corda.doorman.MainKt'
|
||||
|
||||
applicationClass 'com.r3.corda.networkmanage.doorman.MainKt'
|
||||
archiveName "doorman-${version}-capsule.jar"
|
||||
capsuleManifest {
|
||||
applicationVersion = corda_dependency_version
|
||||
systemProperties['visualvm.display.name'] = 'Doorman'
|
||||
@ -60,7 +62,29 @@ task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') {
|
||||
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 {
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
|
||||
compile "net.corda:corda-core:$corda_dependency_version"
|
||||
@ -104,4 +128,4 @@ dependencies {
|
||||
}
|
||||
// Needed by jira rest client
|
||||
compile "com.atlassian.fugue:fugue:2.6.1"
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
package com.r3.corda.doorman
|
||||
package com.r3.corda.networkmanage.doorman
|
||||
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.doorman.persistence.DoormanSchemaService
|
||||
import com.r3.corda.doorman.signer.Signer
|
||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||
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.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -36,7 +37,7 @@ class DoormanIntegrationTest {
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||
// Identity service not needed doorman, corda persistence is not very generic.
|
||||
throw UnsupportedOperationException()
|
||||
}, DoormanSchemaService())
|
||||
}, SchemaService())
|
||||
val signer = Signer(intermediateCAKey, arrayOf(intermediateCACert.toX509Certificate(), rootCACert.toX509Certificate()))
|
||||
|
||||
//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 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
|
@ -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.mock
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.nhaarman.mockito_kotlin.whenever
|
||||
import com.r3.corda.doorman.buildCertPath
|
||||
import com.r3.corda.doorman.persistence.DoormanSchemaService
|
||||
import com.r3.corda.doorman.startDoorman
|
||||
import com.r3.corda.doorman.toX509Certificate
|
||||
import com.r3.corda.signing.hsm.HsmSigner
|
||||
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.signing.persistence.DBCertificateRequestStorage
|
||||
import com.r3.corda.signing.persistence.SigningServerSchemaService
|
||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import com.r3.corda.networkmanage.doorman.startDoorman
|
||||
import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.utilities.NetworkHostAndPort
|
||||
@ -58,7 +58,7 @@ class SigningServiceIntegrationTest {
|
||||
timer.cancel()
|
||||
}
|
||||
|
||||
private fun givenSignerSigningAllRequests(storage: DBCertificateRequestStorage): HsmSigner {
|
||||
private fun givenSignerSigningAllRequests(storage: SignedCertificateRequestStorage): HsmSigner {
|
||||
// Create all certificates
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(CordaX500Name(commonName = "Integration Test Corda Node Root CA",
|
||||
@ -71,14 +71,14 @@ class SigningServiceIntegrationTest {
|
||||
return mock {
|
||||
on { sign(any()) }.then {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val toSign = it.arguments[0] as List<ApprovedCertificateRequestData>
|
||||
val toSign = it.arguments[0] as List<CertificateRequestData>
|
||||
toSign.forEach {
|
||||
JcaPKCS10CertificationRequest(it.request).run {
|
||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, intermediateCACert, intermediateCAKey, subject, publicKey).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, {
|
||||
// Identity service not needed doorman, corda persistence is not very generic.
|
||||
throw UnsupportedOperationException()
|
||||
}, DoormanSchemaService())
|
||||
}, SchemaService())
|
||||
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = true)
|
||||
|
||||
// Start Corda network registration.
|
||||
@ -99,10 +99,10 @@ class SigningServiceIntegrationTest {
|
||||
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.
|
||||
throw UnsupportedOperationException()
|
||||
}, SigningServerSchemaService()))
|
||||
}, SchemaService()))
|
||||
|
||||
val hsmSigner = givenSignerSigningAllRequests(signingServiceStorage)
|
||||
// Poll the database for approved requests
|
||||
@ -136,7 +136,7 @@ class SigningServiceIntegrationTest {
|
||||
val database = configureDatabase(makeTestDataSourceProperties(), null, {
|
||||
// Identity service not needed doorman, corda persistence is not very generic.
|
||||
throw UnsupportedOperationException()
|
||||
}, SigningServerSchemaService())
|
||||
}, SchemaService())
|
||||
val doorman = startDoorman(NetworkHostAndPort(HOST, 0), database, approveAll = 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.hibernate.envers.Audited
|
||||
import java.security.cert.CertPath
|
||||
import java.time.Instant
|
||||
import javax.persistence.*
|
||||
|
||||
/**
|
||||
* 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>)
|
||||
}
|
||||
|
||||
@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 {
|
||||
object NotReady : 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.doorman.persistence.RequestStatus.*
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus.*
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
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 java.security.cert.CertPath
|
||||
import javax.persistence.*
|
||||
|
||||
interface NodeInfoStorage {
|
||||
/**
|
||||
@ -27,27 +26,4 @@ interface NodeInfoStorage {
|
||||
* The [nodeInfo] is keyed by the public key, old node info with the same public key will be replaced by the new node info.
|
||||
*/
|
||||
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.doorman.hash
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.node.NodeInfo
|
||||
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.schemas.MappedSchema
|
||||
import net.corda.core.schemas.PersistentState
|
||||
import net.corda.node.services.api.SchemaService
|
||||
|
||||
class DoormanSchemaService : SchemaService {
|
||||
class SchemaService : SchemaService {
|
||||
// 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))
|
||||
|
||||
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.ConfigParseOptions
|
||||
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.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.r3.corda.doorman.DoormanServer.Companion.logger
|
||||
import com.r3.corda.doorman.persistence.CertificationRequestStorage
|
||||
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
|
||||
import com.r3.corda.doorman.persistence.DoormanSchemaService
|
||||
import com.r3.corda.doorman.persistence.PersistenceNodeInfoStorage
|
||||
import com.r3.corda.doorman.signer.DefaultCsrHandler
|
||||
import com.r3.corda.doorman.signer.JiraCsrHandler
|
||||
import com.r3.corda.doorman.signer.Signer
|
||||
import com.r3.corda.doorman.webservice.NodeInfoWebService
|
||||
import com.r3.corda.doorman.webservice.RegistrationWebService
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.networkmanage.common.persistence.DBCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.PersistenceNodeInfoStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||
import com.r3.corda.networkmanage.common.utils.ShowHelpException
|
||||
import com.r3.corda.networkmanage.doorman.DoormanServer.Companion.logger
|
||||
import com.r3.corda.networkmanage.doorman.signer.DefaultCsrHandler
|
||||
import com.r3.corda.networkmanage.doorman.signer.JiraCsrHandler
|
||||
import com.r3.corda.networkmanage.doorman.signer.Signer
|
||||
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.identity.CordaX500Name
|
||||
import net.corda.core.internal.createDirectories
|
||||
@ -238,7 +239,7 @@ fun main(args: Array<String>) {
|
||||
keystorePassword,
|
||||
caPrivateKeyPassword)
|
||||
DoormanParameters.Mode.DOORMAN -> {
|
||||
val database = configureDatabase(dataSourceProperties, databaseProperties, { throw UnsupportedOperationException() }, DoormanSchemaService())
|
||||
val database = configureDatabase(dataSourceProperties, databaseProperties, { throw UnsupportedOperationException() }, SchemaService())
|
||||
val signer = buildLocalSigner(this)
|
||||
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.doorman.buildCertPath
|
||||
import com.r3.corda.doorman.persistence.CertificateResponse
|
||||
import com.r3.corda.doorman.persistence.CertificationRequestStorage
|
||||
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.doorman.persistence.RequestStatus
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.networkmanage.common.persistence.RequestStatus
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.doorman.JiraClient
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
|
||||
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.doorman.toX509Certificate
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.internal.toX509CertHolder
|
||||
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.doorman.webservice.NodeInfoWebService.Companion.networkMapPath
|
||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||
import com.r3.corda.networkmanage.doorman.webservice.NodeInfoWebService.Companion.networkMapPath
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SignedData
|
||||
import net.corda.core.node.NodeInfo
|
||||
@ -25,6 +25,7 @@ class NodeInfoWebService(private val nodeInfoStorage: NodeInfoStorage) {
|
||||
companion object {
|
||||
const val networkMapPath = "network-map"
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("register")
|
||||
@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.doorman.persistence.CertificateResponse
|
||||
import com.r3.corda.doorman.signer.CsrHandler
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||
import com.r3.corda.networkmanage.doorman.DoormanServerStatus
|
||||
import com.r3.corda.networkmanage.doorman.signer.CsrHandler
|
||||
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_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.signing.authentication.createProvider
|
||||
import com.r3.corda.signing.configuration.Parameters
|
||||
import com.r3.corda.signing.configuration.parseParameters
|
||||
import com.r3.corda.signing.generator.KeyCertificateGenerator
|
||||
import com.r3.corda.signing.hsm.HsmSigner
|
||||
import com.r3.corda.signing.menu.Menu
|
||||
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.signing.persistence.DBCertificateRequestStorage
|
||||
import com.r3.corda.signing.persistence.SigningServerSchemaService
|
||||
import com.r3.corda.signing.utils.mapCryptoServerException
|
||||
import com.r3.corda.networkmanage.common.persistence.SchemaService
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.createProvider
|
||||
import com.r3.corda.networkmanage.hsm.configuration.Parameters
|
||||
import com.r3.corda.networkmanage.hsm.configuration.parseParameters
|
||||
import com.r3.corda.networkmanage.hsm.generator.KeyCertificateGenerator
|
||||
import com.r3.corda.networkmanage.hsm.menu.Menu
|
||||
import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.DBSignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.signer.HsmSigner
|
||||
import com.r3.corda.networkmanage.hsm.utils.mapCryptoServerException
|
||||
import net.corda.node.utilities.configureDatabase
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@ -24,11 +24,11 @@ fun run(parameters: Parameters) {
|
||||
val database = configureDatabase(dataSourceProperties, databaseProperties, {
|
||||
// Identity service not needed
|
||||
throw UnsupportedOperationException()
|
||||
}, SigningServerSchemaService())
|
||||
}, SchemaService())
|
||||
|
||||
val storage = DBCertificateRequestStorage(database)
|
||||
val storage = DBSignedCertificateRequestStorage(database)
|
||||
val provider = createProvider()
|
||||
val sign: (List<ApprovedCertificateRequestData>) -> Unit = {
|
||||
val sign: (List<CertificateRequestData>) -> Unit = {
|
||||
val signer = HsmSigner(
|
||||
storage,
|
||||
certificateName,
|
||||
@ -85,7 +85,7 @@ private fun processError(exception: Exception) {
|
||||
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:")
|
||||
selectedItems.forEachIndexed { index, data ->
|
||||
println("${index + 1} ${data.request.subject}")
|
||||
@ -102,7 +102,7 @@ private fun confirmedKeyGen(): Boolean {
|
||||
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): ")
|
||||
val line = readLine()
|
||||
if (line == null) {
|
@ -1,4 +1,4 @@
|
||||
package com.r3.corda.signing.authentication
|
||||
package com.r3.corda.networkmanage.hsm.authentication
|
||||
|
||||
/*
|
||||
* Supported authentication modes
|
@ -1,7 +1,7 @@
|
||||
package com.r3.corda.signing.authentication
|
||||
package com.r3.corda.networkmanage.hsm.authentication
|
||||
|
||||
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.ByteArrayOutputStream
|
||||
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.ConfigParseOptions
|
||||
import joptsimple.ArgumentAcceptingOptionSpec
|
||||
import joptsimple.OptionParser
|
||||
import net.corda.core.internal.div
|
||||
import net.corda.node.utilities.X509Utilities
|
||||
import net.corda.nodeapi.config.parseAs
|
||||
import com.r3.corda.signing.authentication.AuthMode
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
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.
|
||||
*/
|
@ -1,14 +1,14 @@
|
||||
package com.r3.corda.signing.generator
|
||||
package com.r3.corda.networkmanage.hsm.generator
|
||||
|
||||
import CryptoServerCXI.CryptoServerCXI
|
||||
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 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.KeyStore
|
||||
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)
|
||||
|
@ -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.
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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.signing.authentication.readPassword
|
||||
import com.r3.corda.signing.persistence.ApprovedCertificateRequestData
|
||||
import com.r3.corda.signing.persistence.DBCertificateRequestStorage
|
||||
import com.r3.corda.signing.utils.X509Utilities.buildCertPath
|
||||
import com.r3.corda.signing.utils.X509Utilities.createClientCertificate
|
||||
import com.r3.corda.signing.utils.X509Utilities.getAndInitializeKeyStore
|
||||
import com.r3.corda.signing.utils.X509Utilities.retrieveCertificateAndKeys
|
||||
import com.r3.corda.networkmanage.hsm.authentication.Authenticator
|
||||
import com.r3.corda.networkmanage.hsm.authentication.readPassword
|
||||
import com.r3.corda.networkmanage.hsm.persistence.CertificateRequestData
|
||||
import com.r3.corda.networkmanage.hsm.persistence.SignedCertificateRequestStorage
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.buildCertPath
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.createClientCertificate
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.getAndInitializeKeyStore
|
||||
import com.r3.corda.networkmanage.hsm.utils.X509Utilities.retrieveCertificateAndKeys
|
||||
|
||||
/**
|
||||
* Encapsulates certificate signing logic
|
||||
*/
|
||||
class HsmSigner(private val storage: DBCertificateRequestStorage,
|
||||
class HsmSigner(private val storage: SignedCertificateRequestStorage,
|
||||
private val caCertificateName: String,
|
||||
private val caPrivateKeyPass: String?,
|
||||
private val caParentCertificateName: String,
|
||||
@ -29,7 +29,7 @@ class HsmSigner(private val storage: DBCertificateRequestStorage,
|
||||
* and sets the certificate field with an appropriate value.
|
||||
* @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 ->
|
||||
val keyStore = getAndInitializeKeyStore(provider, keyStorePassword)
|
||||
// This should be changed once we allow for more certificates in the chain. Preferably we should use
|
||||
@ -40,10 +40,10 @@ class HsmSigner(private val storage: DBCertificateRequestStorage,
|
||||
toSign.forEach {
|
||||
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:")
|
||||
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 java.util.HashMap
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* 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 net.corda.core.identity.CordaX500Name
|
||||
@ -74,7 +74,7 @@ object X509Utilities {
|
||||
purposes.add(KeyPurposeId.id_kp_serverAuth)
|
||||
purposes.add(KeyPurposeId.id_kp_clientAuth)
|
||||
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)
|
||||
|
@ -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.doorman.persistence.CertificateSigningRequest
|
||||
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
|
||||
import com.r3.corda.doorman.persistence.DoormanSchemaService
|
||||
import com.r3.corda.doorman.persistence.RequestStatus
|
||||
import com.r3.corda.doorman.toX509Certificate
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -24,7 +21,6 @@ import org.junit.Test
|
||||
import java.security.KeyPair
|
||||
import java.util.*
|
||||
import kotlin.test.*
|
||||
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
|
||||
class DBCertificateRequestStorageTest {
|
||||
private lateinit var storage: DBCertificateRequestStorage
|
||||
@ -32,7 +28,7 @@ class DBCertificateRequestStorageTest {
|
||||
|
||||
@Before
|
||||
fun startDb() {
|
||||
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, DoormanSchemaService())
|
||||
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, SchemaService())
|
||||
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.doorman.hash
|
||||
import com.r3.corda.doorman.persistence.*
|
||||
import com.r3.corda.doorman.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.doorman.toX509Certificate
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificationRequestStorage.Companion.DOORMAN_SIGNATURE
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
import com.r3.corda.networkmanage.common.utils.toX509Certificate
|
||||
import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
@ -59,7 +57,7 @@ class PersistenceNodeInfoStorageTest {
|
||||
|
||||
@Before
|
||||
fun startDb() {
|
||||
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, DoormanSchemaService())
|
||||
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { throw UnsupportedOperationException() }, SchemaService())
|
||||
nodeInfoStorage = PersistenceNodeInfoStorage(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.mock
|
||||
import com.nhaarman.mockito_kotlin.times
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.r3.corda.doorman.persistence.*
|
||||
import com.r3.corda.doorman.signer.DefaultCsrHandler
|
||||
import com.r3.corda.doorman.signer.Signer
|
||||
import com.r3.corda.networkmanage.common.persistence.*
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
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.identity.CordaX500Name
|
||||
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 org.junit.Test
|
||||
@ -9,8 +9,8 @@ import kotlin.test.assertFailsWith
|
||||
|
||||
class DoormanParametersTest {
|
||||
private val testDummyPath = ".${File.separator}testDummyPath.jks"
|
||||
private val validConfigPath = File(javaClass.getResource("/node.conf").toURI()).absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/node_fail.conf").toURI()).absolutePath
|
||||
private val validConfigPath = File(javaClass.getResource("/doorman.conf").toURI()).absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/doorman_fail.conf").toURI()).absolutePath
|
||||
|
||||
@Test
|
||||
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.mock
|
||||
import com.nhaarman.mockito_kotlin.times
|
||||
import com.nhaarman.mockito_kotlin.verify
|
||||
import com.r3.corda.doorman.persistence.NodeInfoStorage
|
||||
import com.r3.corda.doorman.webservice.NodeInfoWebService
|
||||
import com.r3.corda.networkmanage.common.persistence.NodeInfoStorage
|
||||
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.identity.CordaX500Name
|
||||
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.r3.corda.doorman.persistence.CertificateResponse
|
||||
import com.r3.corda.doorman.signer.CsrHandler
|
||||
import com.r3.corda.doorman.webservice.RegistrationWebService
|
||||
import com.r3.corda.networkmanage.common.persistence.CertificateResponse
|
||||
import com.r3.corda.networkmanage.common.utils.buildCertPath
|
||||
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.SecureHash
|
||||
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 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.r3.corda.signing.authentication.AuthMode
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class ConfigurationTest {
|
||||
private val validConfigPath = File(javaClass.getResource("/signing_service.conf").toURI()).absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/signing_service_fail.conf").toURI()).absolutePath
|
||||
private val validConfigPath = File(javaClass.getResource("/hsm.conf").toURI()).absolutePath
|
||||
private val invalidConfigPath = File(javaClass.getResource("/hsm_fail.conf").toURI()).absolutePath
|
||||
|
||||
@Test
|
||||
fun `authMode is parsed correctly`() {
|
@ -43,9 +43,8 @@ include 'samples:simm-valuation-demo'
|
||||
include 'samples:notary-demo'
|
||||
include 'samples:bank-of-corda-demo'
|
||||
include 'cordform-common'
|
||||
include 'doorman'
|
||||
include 'network-management'
|
||||
include 'verify-enclave'
|
||||
include 'sgx-jvm/hsm-tool'
|
||||
include 'signing-server'
|
||||
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…
Reference in New Issue
Block a user