Fix post-merge issues:

- Update Doorman to reflect the X500 naming and configuration changes
- Minor other changes
This commit is contained in:
Andrius Dagys 2017-05-11 17:35:21 +01:00
parent dfb63231e3
commit 550be76787
9 changed files with 110 additions and 102 deletions

View File

@ -1,17 +1,45 @@
package com.r3.corda.doorman package com.r3.corda.doorman
import com.r3.corda.doorman.OptionParserHelper.toConfigWithOptions import com.r3.corda.doorman.OptionParserHelper.toConfigWithOptions
import com.typesafe.config.Config
import com.typesafe.config.ConfigFactory import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions import com.typesafe.config.ConfigParseOptions
import net.corda.core.div import net.corda.core.div
import net.corda.nodeapi.config.getOrElse import net.corda.node.utilities.getPath
import net.corda.nodeapi.config.getValue import net.corda.nodeapi.config.parseAs
import java.nio.file.Path import java.nio.file.Path
import java.util.* import java.util.*
class DoormanParameters(vararg args: String) { data class DoormanParameters(val basedir: Path,
private val argConfig = args.toConfigWithOptions { val keystorePassword: String?,
val caPrivateKeyPassword: String?,
val rootKeystorePassword: String?,
val rootPrivateKeyPassword: String?,
val host: String,
val port: Int,
val dataSourceProperties: Properties,
val keygen: Boolean = false,
val rootKeygen: Boolean = false,
val jiraConfig: JiraConfig? = null,
val keystorePath: Path = basedir / "certificates" / "caKeystore.jks",
val rootStorePath: Path = basedir / "certificates" / "rootCAKeystore.jks"
) {
val mode = if (rootKeygen) Mode.ROOT_KEYGEN else if (keygen) Mode.CA_KEYGEN else Mode.DOORMAN
enum class Mode {
DOORMAN, CA_KEYGEN, ROOT_KEYGEN
}
data class JiraConfig(
val address: String,
val projectCode: String,
val username: String,
val password: String,
val doneTransitionCode: Int
)
}
fun parseParameters(vararg args: String): DoormanParameters {
val argConfig = args.toConfigWithOptions {
accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath") accepts("basedir", "Overriding configuration filepath, default to current directory.").withRequiredArg().defaultsTo(".").describedAs("filepath")
accepts("configFile", "Overriding configuration file, default to <<current directory>>/node.conf.").withRequiredArg().describedAs("filepath") accepts("configFile", "Overriding configuration file, default to <<current directory>>/node.conf.").withRequiredArg().describedAs("filepath")
accepts("keygen", "Generate CA keypair and certificate using provide Root CA key.").withOptionalArg() accepts("keygen", "Generate CA keypair and certificate using provide Root CA key.").withOptionalArg()
@ -25,33 +53,12 @@ class DoormanParameters(vararg args: String) {
accepts("host", "Doorman web service host override").withRequiredArg().describedAs("hostname") accepts("host", "Doorman web service host override").withRequiredArg().describedAs("hostname")
accepts("port", "Doorman web service port override").withRequiredArg().ofType(Int::class.java).describedAs("port number") accepts("port", "Doorman web service port override").withRequiredArg().ofType(Int::class.java).describedAs("port number")
} }
private val basedir: Path by argConfig
private val configFile by argConfig.getOrElse { basedir / "node.conf" }
private val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
val keystorePath: Path by config.getOrElse { basedir / "certificates" / "caKeystore.jks" }
val rootStorePath: Path by config.getOrElse { basedir / "certificates" / "rootCAKeystore.jks" }
val keystorePassword: String? by config
val caPrivateKeyPassword: String? by config
val rootKeystorePassword: String? by config
val rootPrivateKeyPassword: String? by config
val host: String by config
val port: Int by config
val dataSourceProperties: Properties by config
val jiraConfig = if (config.hasPath("jiraConfig")) JiraConfig(config.getConfig("jiraConfig")) else null
private val keygen: Boolean by config.getOrElse { false }
private val rootKeygen: Boolean by config.getOrElse { false }
val mode = if (rootKeygen) Mode.ROOT_KEYGEN else if (keygen) Mode.CA_KEYGEN else Mode.DOORMAN val configFile = if (argConfig.hasPath("configFile")) {
argConfig.getPath("configFile")
enum class Mode { } else {
DOORMAN, CA_KEYGEN, ROOT_KEYGEN argConfig.getPath("basedir") / "node.conf"
}
class JiraConfig(config: Config) {
val address: String by config
val projectCode: String by config
val username: String by config
val password: String by config
val doneTransitionCode: Int by config
} }
val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
return config.parseAs<DoormanParameters>()
} }

View File

@ -3,7 +3,7 @@ package com.r3.corda.doorman
import com.r3.corda.doorman.persistence.CertificateResponse import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestData import com.r3.corda.doorman.persistence.CertificationRequestData
import com.r3.corda.doorman.persistence.CertificationRequestStorage import com.r3.corda.doorman.persistence.CertificationRequestStorage
import net.corda.core.crypto.X509Utilities.CACertAndKey import net.corda.core.crypto.CertificateAndKey
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
@ -26,7 +26,7 @@ import javax.ws.rs.core.Response.Status.UNAUTHORIZED
* Provides functionality for asynchronous submission of certificate signing requests and retrieval of the results. * Provides functionality for asynchronous submission of certificate signing requests and retrieval of the results.
*/ */
@Path("") @Path("")
class DoormanWebService(val intermediateCACertAndKey: CACertAndKey, val rootCert: Certificate, val storage: CertificationRequestStorage, val serverStatus: DoormanServerStatus) { class DoormanWebService(val intermediateCACertAndKey: CertificateAndKey, val rootCert: Certificate, val storage: CertificationRequestStorage, val serverStatus: DoormanServerStatus) {
@Context lateinit var request: HttpServletRequest @Context lateinit var request: HttpServletRequest
/** /**
* Accept stream of [PKCS10CertificationRequest] from user and persists in [CertificationRequestStorage] for approval. * Accept stream of [PKCS10CertificationRequest] from user and persists in [CertificationRequestStorage] for approval.

View File

@ -7,22 +7,19 @@ import com.r3.corda.doorman.persistence.CertificationRequestStorage
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
import com.r3.corda.doorman.persistence.JiraCertificateRequestStorage import com.r3.corda.doorman.persistence.JiraCertificateRequestStorage
import net.corda.core.createDirectories import net.corda.core.createDirectories
import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.*
import net.corda.core.crypto.X509Utilities.CACertAndKey import net.corda.core.crypto.KeyStoreUtilities.loadKeyStore
import net.corda.core.crypto.KeyStoreUtilities.loadOrCreateKeyStore
import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA
import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY import net.corda.core.crypto.X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA_PRIVATE_KEY
import net.corda.core.crypto.X509Utilities.addOrReplaceKey
import net.corda.core.crypto.X509Utilities.createIntermediateCert import net.corda.core.crypto.X509Utilities.createIntermediateCert
import net.corda.core.crypto.X509Utilities.createServerCert import net.corda.core.crypto.X509Utilities.createServerCert
import net.corda.core.crypto.X509Utilities.loadCertificateAndKey
import net.corda.core.crypto.X509Utilities.loadKeyStore
import net.corda.core.crypto.X509Utilities.loadOrCreateKeyStore
import net.corda.core.crypto.X509Utilities.saveKeyStore
import net.corda.core.seconds import net.corda.core.seconds
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.eclipse.jetty.server.Server import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector import org.eclipse.jetty.server.ServerConnector
@ -45,7 +42,7 @@ import kotlin.system.exitProcess
* The server will require keystorePath, keystore password and key password via command line input. * The server will require keystorePath, keystore password and key password via command line input.
* The Intermediate CA certificate,Intermediate CA private key and Root CA Certificate should use alias name specified in [X509Utilities] * The Intermediate CA certificate,Intermediate CA private key and Root CA Certificate should use alias name specified in [X509Utilities]
*/ */
class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CACertAndKey, val rootCACert: Certificate, val storage: CertificationRequestStorage) : Closeable { class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CertificateAndKey, val rootCACert: Certificate, val storage: CertificationRequestStorage) : Closeable {
val serverStatus = DoormanServerStatus() val serverStatus = DoormanServerStatus()
companion object { companion object {
@ -147,9 +144,9 @@ private fun DoormanParameters.generateRootKeyPair() {
exitProcess(1) exitProcess(1)
} }
val selfSignCert = X509Utilities.createSelfSignedCACert(CORDA_ROOT_CA) val selfSignCert = X509Utilities.createSelfSignedCACert(X500Name(CORDA_ROOT_CA))
rootStore.addOrReplaceKey(CORDA_ROOT_CA_PRIVATE_KEY, selfSignCert.keyPair.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert.certificate)) rootStore.addOrReplaceKey(CORDA_ROOT_CA_PRIVATE_KEY, selfSignCert.keyPair.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert.certificate))
saveKeyStore(rootStore, rootStorePath, rootKeystorePassword) rootStore.save(rootStorePath, rootKeystorePassword)
println("Root CA keypair and certificate stored in $rootStorePath.") println("Root CA keypair and certificate stored in $rootStorePath.")
println(loadKeyStore(rootStorePath, rootKeystorePassword).getCertificate(CORDA_ROOT_CA_PRIVATE_KEY).publicKey) println(loadKeyStore(rootStorePath, rootKeystorePassword).getCertificate(CORDA_ROOT_CA_PRIVATE_KEY).publicKey)
@ -162,7 +159,7 @@ private fun DoormanParameters.generateCAKeyPair() {
val rootPrivateKeyPassword = rootPrivateKeyPassword ?: readPassword("Root Private Key Password: ") val rootPrivateKeyPassword = rootPrivateKeyPassword ?: readPassword("Root Private Key Password: ")
val rootKeyStore = loadKeyStore(rootStorePath, rootKeystorePassword) val rootKeyStore = loadKeyStore(rootStorePath, rootKeystorePassword)
val rootKeyAndCert = loadCertificateAndKey(rootKeyStore, rootPrivateKeyPassword, CORDA_ROOT_CA_PRIVATE_KEY) val rootKeyAndCert = rootKeyStore.getCertificateAndKey(rootPrivateKeyPassword, CORDA_ROOT_CA_PRIVATE_KEY)
val keystorePassword = keystorePassword ?: readPassword("Keystore Password: ") val keystorePassword = keystorePassword ?: readPassword("Keystore Password: ")
val caPrivateKeyPassword = caPrivateKeyPassword ?: readPassword("CA Private Key Password: ") val caPrivateKeyPassword = caPrivateKeyPassword ?: readPassword("CA Private Key Password: ")
@ -177,10 +174,10 @@ private fun DoormanParameters.generateCAKeyPair() {
exitProcess(1) exitProcess(1)
} }
val intermediateKeyAndCert = createIntermediateCert(CORDA_INTERMEDIATE_CA, rootKeyAndCert) val intermediateKeyAndCert = createIntermediateCert(X500Name(CORDA_INTERMEDIATE_CA), rootKeyAndCert)
keyStore.addOrReplaceKey(CORDA_INTERMEDIATE_CA_PRIVATE_KEY, intermediateKeyAndCert.keyPair.private, keyStore.addOrReplaceKey(CORDA_INTERMEDIATE_CA_PRIVATE_KEY, intermediateKeyAndCert.keyPair.private,
caPrivateKeyPassword.toCharArray(), arrayOf(intermediateKeyAndCert.certificate, rootKeyAndCert.certificate)) caPrivateKeyPassword.toCharArray(), arrayOf(intermediateKeyAndCert.certificate, rootKeyAndCert.certificate))
saveKeyStore(keyStore, keystorePath, keystorePassword) keyStore.save(keystorePath, keystorePassword)
println("Intermediate CA keypair and certificate stored in $keystorePath.") println("Intermediate CA keypair and certificate stored in $keystorePath.")
println(loadKeyStore(keystorePath, keystorePassword).getCertificate(CORDA_INTERMEDIATE_CA_PRIVATE_KEY).publicKey) println(loadKeyStore(keystorePath, keystorePassword).getCertificate(CORDA_INTERMEDIATE_CA_PRIVATE_KEY).publicKey)
} }
@ -190,9 +187,10 @@ private fun DoormanParameters.startDoorman() {
// Get password from console if not in config. // Get password from console if not in config.
val keystorePassword = keystorePassword ?: readPassword("Keystore Password: ") val keystorePassword = keystorePassword ?: readPassword("Keystore Password: ")
val caPrivateKeyPassword = caPrivateKeyPassword ?: readPassword("CA Private Key Password: ") val caPrivateKeyPassword = caPrivateKeyPassword ?: readPassword("CA Private Key Password: ")
val keystore = X509Utilities.loadKeyStore(keystorePath, keystorePassword)
val keystore = loadOrCreateKeyStore(keystorePath, keystorePassword)
val rootCACert = keystore.getCertificateChain(CORDA_INTERMEDIATE_CA_PRIVATE_KEY).last() val rootCACert = keystore.getCertificateChain(CORDA_INTERMEDIATE_CA_PRIVATE_KEY).last()
val caCertAndKey = X509Utilities.loadCertificateAndKey(keystore, caPrivateKeyPassword, CORDA_INTERMEDIATE_CA_PRIVATE_KEY) val caCertAndKey = keystore.getCertificateAndKey(caPrivateKeyPassword, CORDA_INTERMEDIATE_CA_PRIVATE_KEY)
// Create DB connection. // Create DB connection.
val (datasource, database) = configureDatabase(dataSourceProperties) val (datasource, database) = configureDatabase(dataSourceProperties)
@ -218,7 +216,7 @@ private fun DoormanParameters.startDoorman() {
fun main(args: Array<String>) { fun main(args: Array<String>) {
try { try {
// TODO : Remove config overrides and solely use config file after testnet is finalized. // TODO : Remove config overrides and solely use config file after testnet is finalized.
DoormanParameters(*args).run { parseParameters(*args).run {
when (mode) { when (mode) {
DoormanParameters.Mode.ROOT_KEYGEN -> generateRootKeyPair() DoormanParameters.Mode.ROOT_KEYGEN -> generateRootKeyPair()
DoormanParameters.Mode.CA_KEYGEN -> generateCAKeyPair() DoormanParameters.Mode.CA_KEYGEN -> generateCAKeyPair()

View File

@ -24,7 +24,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
init { init {
// Create table if not exists. // Create table if not exists.
databaseTransaction(database) { database.transaction {
SchemaUtils.create(DataTable) SchemaUtils.create(DataTable)
} }
} }
@ -32,7 +32,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
override fun saveRequest(certificationData: CertificationRequestData): String { override fun saveRequest(certificationData: CertificationRequestData): String {
val legalName = certificationData.request.subject.commonName val legalName = certificationData.request.subject.commonName
val requestId = SecureHash.randomSHA256().toString() val requestId = SecureHash.randomSHA256().toString()
databaseTransaction(database) { database.transaction {
val duplicate = DataTable.select { val duplicate = DataTable.select {
// A duplicate legal name is one where a previously approved, or currently pending, request has the same legal name. // A duplicate legal name is one where a previously approved, or currently pending, request has the same legal name.
// A rejected request with the same legal name doesn't count as a duplicate // A rejected request with the same legal name doesn't count as a duplicate
@ -65,7 +65,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
} }
override fun getResponse(requestId: String): CertificateResponse { override fun getResponse(requestId: String): CertificateResponse {
return databaseTransaction(database) { return database.transaction {
val response = DataTable val response = DataTable
.select { DataTable.requestId eq requestId and DataTable.processTimestamp.isNotNull() } .select { DataTable.requestId eq requestId and DataTable.processTimestamp.isNotNull() }
.map { Pair(it[DataTable.certificate], it[DataTable.rejectReason]) } .map { Pair(it[DataTable.certificate], it[DataTable.rejectReason]) }
@ -84,7 +84,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
} }
override fun approveRequest(requestId: String, generateCertificate: CertificationRequestData.() -> Certificate) { override fun approveRequest(requestId: String, generateCertificate: CertificationRequestData.() -> Certificate) {
databaseTransaction(database) { database.transaction {
val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() } val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
if (request != null) { if (request != null) {
withFinalizables { finalizables -> withFinalizables { finalizables ->
@ -98,7 +98,7 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
} }
override fun rejectRequest(requestId: String, rejectReason: String) { override fun rejectRequest(requestId: String, rejectReason: String) {
databaseTransaction(database) { database.transaction {
val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() } val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
if (request != null) { if (request != null) {
DataTable.update({ DataTable.requestId eq requestId }) { DataTable.update({ DataTable.requestId eq requestId }) {
@ -110,13 +110,13 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
} }
override fun getRequest(requestId: String): CertificationRequestData? { override fun getRequest(requestId: String): CertificationRequestData? {
return databaseTransaction(database) { return database.transaction {
singleRequestWhere { DataTable.requestId eq requestId } singleRequestWhere { DataTable.requestId eq requestId }
} }
} }
override fun getPendingRequestIds(): List<String> { override fun getPendingRequestIds(): List<String> {
return databaseTransaction(database) { return database.transaction {
DataTable.select { DataTable.processTimestamp.isNull() }.map { it[DataTable.requestId] } DataTable.select { DataTable.processTimestamp.isNull() }.map { it[DataTable.requestId] }
} }
} }

View File

@ -9,29 +9,32 @@ 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 = javaClass.getResource("/node.conf").path
private val invalidConfigPath = javaClass.getResource("/node_fail.conf").path
@Test @Test
fun `parse mode flag arg correctly`() { fun `parse mode flag arg correctly`() {
assertEquals(DoormanParameters.Mode.CA_KEYGEN, DoormanParameters("--keygen").mode) assertEquals(DoormanParameters.Mode.CA_KEYGEN, parseParameters("--keygen", "--configFile", validConfigPath).mode)
assertEquals(DoormanParameters.Mode.ROOT_KEYGEN, DoormanParameters("--rootKeygen").mode) assertEquals(DoormanParameters.Mode.ROOT_KEYGEN, parseParameters("--rootKeygen", "--configFile", validConfigPath).mode)
assertEquals(DoormanParameters.Mode.DOORMAN, DoormanParameters().mode) assertEquals(DoormanParameters.Mode.DOORMAN, parseParameters("--configFile", validConfigPath).mode)
} }
@Test @Test
fun `command line arg should override config file`() { fun `command line arg should override config file`() {
val params = DoormanParameters("--keystorePath", testDummyPath, "--port", "1000", "--configFile", javaClass.getResource("/node.conf").path) val params = parseParameters("--keystorePath", testDummyPath, "--port", "1000", "--configFile", validConfigPath)
assertEquals(testDummyPath, params.keystorePath.toString()) assertEquals(testDummyPath, params.keystorePath.toString())
assertEquals(1000, params.port) assertEquals(1000, params.port)
val params2 = DoormanParameters("--configFile", javaClass.getResource("/node.conf").path) val params2 = parseParameters("--configFile", validConfigPath)
assertEquals(Paths.get("/opt/doorman/certificates/caKeystore.jks"), params2.keystorePath) assertEquals(Paths.get("/opt/doorman/certificates/caKeystore.jks"), params2.keystorePath)
assertEquals(8080, params2.port) assertEquals(8080, params2.port)
} }
@Test @Test
fun `should fail when config missing`() { fun `should fail when config missing`() {
// dataSourceProperties is missing from node_fail.conf and it should fail when accessed, and shouldn't use default from reference.conf. // dataSourceProperties is missing from node_fail.conf and it should fail during parsing, and shouldn't use default from reference.conf.
val params = DoormanParameters("--keygen", "--keystorePath", testDummyPath, "--configFile", javaClass.getResource("/node_fail.conf").path) assertFailsWith<ConfigException.Missing> {
assertFailsWith<ConfigException.Missing> { params.dataSourceProperties } parseParameters("--keygen", "--keystorePath", testDummyPath, "--configFile", invalidConfigPath)
}
} }
} }

View File

@ -5,11 +5,11 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.doorman.persistence.CertificateResponse import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestData import com.r3.corda.doorman.persistence.CertificationRequestData
import com.r3.corda.doorman.persistence.CertificationRequestStorage import com.r3.corda.doorman.persistence.CertificationRequestStorage
import net.corda.core.crypto.CertificateStream import net.corda.core.crypto.*
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.core.crypto.X509Utilities
import org.apache.commons.io.IOUtils import org.apache.commons.io.IOUtils
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After import org.junit.After
@ -26,8 +26,8 @@ import javax.ws.rs.core.MediaType
import kotlin.test.assertEquals import kotlin.test.assertEquals
class DoormanServiceTest { class DoormanServiceTest {
private val rootCA = X509Utilities.createSelfSignedCACert("Corda Node Root CA") private val rootCA = X509Utilities.createSelfSignedCACert(X500Name("CN=Corda Node Root CA,L=London"))
private val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA") private val intermediateCA = X509Utilities.createSelfSignedCACert(X500Name("CN=Corda Node Intermediate CA,L=London"))
private lateinit var doormanServer: DoormanServer private lateinit var doormanServer: DoormanServer
private fun startSigningServer(storage: CertificationRequestStorage) { private fun startSigningServer(storage: CertificationRequestStorage) {
@ -50,8 +50,8 @@ class DoormanServiceTest {
startSigningServer(storage) startSigningServer(storage)
val keyPair = X509Utilities.generateECDSAKeyPairForSSL() val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val request = X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair) val request = X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName"), keyPair)
// Post request to signing server via http. // Post request to signing server via http.
assertEquals(id, submitRequest(request)) assertEquals(id, submitRequest(request))
@ -62,7 +62,7 @@ class DoormanServiceTest {
@Test @Test
fun `retrieve certificate`() { fun `retrieve certificate`() {
val keyPair = X509Utilities.generateECDSAKeyPairForSSL() val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val id = SecureHash.randomSHA256().toString() val id = SecureHash.randomSHA256().toString()
// Mock Storage behaviour. // Mock Storage behaviour.
@ -74,7 +74,7 @@ class DoormanServiceTest {
on { approveRequest(eq(id), any()) }.then { on { approveRequest(eq(id), any()) }.then {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val certGen = it.arguments[1] as ((CertificationRequestData) -> Certificate) val certGen = it.arguments[1] as ((CertificationRequestData) -> Certificate)
val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair)) val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName,L=London"), keyPair))
certificateStore[id] = certGen(request) certificateStore[id] = certGen(request)
Unit Unit
} }

View File

@ -3,10 +3,13 @@ package com.r3.corda.doorman.internal.persistence
import com.r3.corda.doorman.persistence.CertificateResponse import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestData import com.r3.corda.doorman.persistence.CertificationRequestData
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.node.utilities.configureDatabase import net.corda.node.utilities.configureDatabase
import net.corda.testing.node.makeTestDataSourceProperties import net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
@ -18,7 +21,7 @@ import kotlin.test.assertNotNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
class DBCertificateRequestStorageTest { class DBCertificateRequestStorageTest {
private val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA") private val intermediateCA = X509Utilities.createSelfSignedCACert(X500Name("CN=Corda Node Intermediate CA"))
private var closeDb: Closeable? = null private var closeDb: Closeable? = null
private lateinit var storage: DBCertificateRequestStorage private lateinit var storage: DBCertificateRequestStorage
@ -106,28 +109,12 @@ class DBCertificateRequestStorageTest {
assertThat(storage.getResponse(requestId2)).isInstanceOf(CertificateResponse.Ready::class.java) assertThat(storage.getResponse(requestId2)).isInstanceOf(CertificateResponse.Ready::class.java)
} }
@Test
fun `request with equals symbol in legal name`() {
val requestId = storage.saveRequest(createRequest("Bank=A").first)
assertThat(storage.getPendingRequestIds()).isEmpty()
val response = storage.getResponse(requestId) as CertificateResponse.Unauthorised
assertThat(response.message).contains("=")
}
@Test
fun `request with comma in legal name`() {
val requestId = storage.saveRequest(createRequest("Bank,A").first)
assertThat(storage.getPendingRequestIds()).isEmpty()
val response = storage.getResponse(requestId) as CertificateResponse.Unauthorised
assertThat(response.message).contains(",")
}
private fun createRequest(legalName: String): Pair<CertificationRequestData, KeyPair> { private fun createRequest(legalName: String): Pair<CertificationRequestData, KeyPair> {
val keyPair = X509Utilities.generateECDSAKeyPairForSSL() val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val request = CertificationRequestData( val request = CertificationRequestData(
"hostname", "hostname",
"0.0.0.0", "0.0.0.0",
X509Utilities.createCertificateSigningRequest(legalName, "London", "admin@test.com", keyPair)) X509Utilities.createCertificateSigningRequest(X500Name("CN=$legalName"), keyPair))
return Pair(request, keyPair) return Pair(request, keyPair)
} }

View File

@ -11,7 +11,6 @@ import java.net.URL
import java.security.cert.Certificate import java.security.cert.Certificate
import java.util.* import java.util.*
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import javax.ws.rs.core.MediaType
class HTTPNetworkRegistrationService(val server: URL) : NetworkRegistrationService { class HTTPNetworkRegistrationService(val server: URL) : NetworkRegistrationService {
companion object { companion object {
@ -46,7 +45,7 @@ class HTTPNetworkRegistrationService(val server: URL) : NetworkRegistrationServi
val conn = URL("$server/api/certificate").openConnection() as HttpURLConnection val conn = URL("$server/api/certificate").openConnection() as HttpURLConnection
conn.doOutput = true conn.doOutput = true
conn.requestMethod = "POST" conn.requestMethod = "POST"
conn.setRequestProperty("Content-Type", MediaType.APPLICATION_OCTET_STREAM) conn.setRequestProperty("Content-Type", MediaType.OCTET_STREAM.toString())
conn.setRequestProperty("Client-Version", clientVersion) conn.setRequestProperty("Client-Version", clientVersion)
conn.outputStream.write(request.encoded) conn.outputStream.write(request.encoded)

View File

@ -3,21 +3,33 @@
package com.r3.enclaves.txverify package com.r3.enclaves.txverify
import com.esotericsoftware.minlog.Log import com.esotericsoftware.minlog.Log
import net.corda.core.contracts.* import net.corda.core.contracts.Attachment
import net.corda.core.crypto.* import net.corda.core.contracts.StateRef
import net.corda.core.contracts.TransactionResolutionException
import net.corda.core.contracts.TransactionState
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party
import net.corda.core.node.ServicesForResolution import net.corda.core.node.ServicesForResolution
import net.corda.core.node.services.AttachmentStorage import net.corda.core.node.services.AttachmentStorage
import net.corda.core.node.services.AttachmentsStorageService import net.corda.core.node.services.AttachmentsStorageService
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.serialization.* import net.corda.core.serialization.CordaSerializable
import net.corda.core.serialization.SerializedBytes
import net.corda.core.serialization.createTestKryo
import net.corda.core.serialization.deserialize
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import org.bouncycastle.asn1.x500.X500Name
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
import java.nio.file.Path import java.nio.file.Path
import java.security.PublicKey
// This file implements the functionality of the SGX transaction verification enclave. // This file implements the functionality of the SGX transaction verification enclave.
private class ServicesForVerification(dependenciesList: List<WireTransaction>, attachments: Array<ByteArray>) : ServicesForResolution, IdentityService, AttachmentsStorageService, AttachmentStorage { private class ServicesForVerification(dependenciesList: List<WireTransaction>, attachments: Array<ByteArray>) : ServicesForResolution, IdentityService, AttachmentsStorageService, AttachmentStorage {
override val attachmentsClassLoaderEnabled: Boolean
get() = TODO("not implemented")
override var automaticallyExtractAttachments: Boolean override var automaticallyExtractAttachments: Boolean
get() = throw UnsupportedOperationException() get() = throw UnsupportedOperationException()
set(value) = throw UnsupportedOperationException() set(value) = throw UnsupportedOperationException()
@ -37,10 +49,12 @@ private class ServicesForVerification(dependenciesList: List<WireTransaction>, a
} }
// Identities: this stuff will all change in future so we don't bother implementing it now. // Identities: this stuff will all change in future so we don't bother implementing it now.
override fun registerIdentity(party: Party) = throw UnsupportedOperationException() override fun registerIdentity(party: net.corda.core.identity.Party) = TODO("not implemented")
override fun partyFromKey(key: CompositeKey): Party? = null
override fun partyFromName(name: String): Party? = null override fun partyFromKey(key: PublicKey): net.corda.core.identity.Party? = null
override fun partyFromAnonymous(party: AnonymousParty) = null override fun partyFromName(name: String): net.corda.core.identity.Party? = null
override fun partyFromX500Name(principal: X500Name): net.corda.core.identity.Party? = null
override fun partyFromAnonymous(party: AnonymousParty): net.corda.core.identity.Party? = null
// TODO: Implement attachments. // TODO: Implement attachments.
override val attachments: AttachmentStorage = this override val attachments: AttachmentStorage = this