Upgrade doorman to Corda 0.16-SNAPSHOT (#36)

* * Upgrade to Corda 0.16
* validate X500 name using `validateX500Name` method
* read email from CSR instead of X500Name
* replaced Exposed with hibernate

* * removed unused class

* * make test helper method private

* address PR issue
This commit is contained in:
Patrick Kuo 2017-09-11 11:32:04 +01:00 committed by GitHub
parent a75dd409aa
commit 41dd1a71de
9 changed files with 231 additions and 128 deletions

View File

@ -1,7 +1,7 @@
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 = '0.12.1'
corda_dependency_version = '0.16-SNAPSHOT'
}
version "$corda_dependency_version"
@ -40,10 +40,10 @@ task buildDoormanJAR(type: FatCapsule, dependsOn: 'jar') {
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
compile "net.corda:core:$corda_dependency_version"
compile "net.corda:node:$corda_dependency_version"
compile "net.corda:node-api:$corda_dependency_version"
testCompile "net.corda:test-utils:$corda_dependency_version"
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"
// Log4J: logging framework (with SLF4J bindings)
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"

View File

@ -3,10 +3,10 @@ package com.r3.corda.doorman
import com.r3.corda.doorman.OptionParserHelper.toConfigWithOptions
import com.typesafe.config.ConfigFactory
import com.typesafe.config.ConfigParseOptions
import net.corda.core.div
import net.corda.node.utilities.getPath
import net.corda.core.internal.div
import net.corda.nodeapi.config.parseAs
import java.nio.file.Path
import java.nio.file.Paths
import java.util.*
data class DoormanParameters(val basedir: Path,
@ -17,6 +17,7 @@ data class DoormanParameters(val basedir: Path,
val host: String,
val port: Int,
val dataSourceProperties: Properties,
val databaseProperties: Properties? = null,
val keygen: Boolean = false,
val rootKeygen: Boolean = false,
val jiraConfig: JiraConfig? = null,
@ -55,9 +56,9 @@ fun parseParameters(vararg args: String): DoormanParameters {
}
val configFile = if (argConfig.hasPath("configFile")) {
argConfig.getPath("configFile")
Paths.get(argConfig.getString("configFile"))
} else {
argConfig.getPath("basedir") / "node.conf"
Paths.get(argConfig.getString("basedir")) / "node.conf"
}
val config = argConfig.withFallback(ConfigFactory.parseFile(configFile.toFile(), ConfigParseOptions.defaults().setAllowMissing(true))).resolve()
return config.parseAs<DoormanParameters>()

View File

@ -3,10 +3,10 @@ package com.r3.corda.doorman
import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestData
import com.r3.corda.doorman.persistence.CertificationRequestStorage
import net.corda.core.crypto.CertificateAndKeyPair
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_ROOT_CA
import net.corda.core.utilities.CertificateAndKeyPair
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
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.codehaus.jackson.map.ObjectMapper
import java.io.ByteArrayOutputStream

View File

@ -5,22 +5,22 @@ import com.google.common.net.HostAndPort
import com.r3.corda.doorman.DoormanServer.Companion.logger
import com.r3.corda.doorman.persistence.CertificationRequestStorage
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.persistence.JiraCertificateRequestStorage
import net.corda.core.createDirectories
import net.corda.core.crypto.*
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_ROOT_CA
import net.corda.core.crypto.X509Utilities.createCertificate
import net.corda.core.seconds
import net.corda.core.crypto.Crypto
import net.corda.core.internal.createDirectories
import net.corda.core.utilities.CertificateAndKeyPair
import net.corda.core.utilities.loggerFor
import net.corda.node.utilities.configureDatabase
import net.corda.core.utilities.seconds
import net.corda.core.utilities.withCommonName
import net.corda.node.utilities.*
import net.corda.node.utilities.X509Utilities.CORDA_INTERMEDIATE_CA
import net.corda.node.utilities.X509Utilities.CORDA_ROOT_CA
import net.corda.node.utilities.X509Utilities.createCertificate
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.path.CertPath
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
@ -38,6 +38,7 @@ import java.time.Instant
import kotlin.concurrent.thread
import kotlin.system.exitProcess
/**
* DoormanServer runs on Jetty server and provide certificate signing service via http.
* The server will require keystorePath, keystore password and key password via command line input.
@ -88,8 +89,13 @@ class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CertificateAnd
// please see [sun.security.x509.X500Name.isWithinSubtree()] for more information.
// We assume all attributes in the subject name has been checked prior approval.
// TODO: add validation to subject name.
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject))), arrayOf())
createCertificate(CertificateType.CLIENT_CA, caCertAndKey.certificate, caCertAndKey.keyPair, request.subject, request.publicKey, nameConstraints = nameConstraints).toX509Certificate()
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject.withCommonName(null)))), arrayOf())
createCertificate(CertificateType.CLIENT_CA,
caCertAndKey.certificate,
caCertAndKey.keyPair,
request.subject.withCommonName(X509Utilities.CORDA_CLIENT_CA_CN),
request.publicKey,
nameConstraints = nameConstraints).toX509Certificate()
}
logger.info("Approved request $id")
serverStatus.lastApprovalTime = Instant.now()
@ -151,7 +157,7 @@ private fun DoormanParameters.generateRootKeyPair() {
val selfSignKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Corda Root CA, O=R3, OU=Corda, L=London, C=GB"), selfSignKey)
rootStore.addOrReplaceKey(CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), CertPath(arrayOf(selfSignCert)))
rootStore.addOrReplaceKey(CORDA_ROOT_CA, selfSignKey.private, rootPrivateKeyPassword.toCharArray(), arrayOf(selfSignCert))
rootStore.save(rootStorePath, rootKeystorePassword)
println("Root CA keypair and certificate stored in $rootStorePath.")
@ -183,7 +189,7 @@ private fun DoormanParameters.generateCAKeyPair() {
val intermediateKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val intermediateCert = createCertificate(CertificateType.INTERMEDIATE_CA, rootKeyAndCert.certificate, rootKeyAndCert.keyPair, X500Name("CN=Corda Intermediate CA, O=R3, OU=Corda, L=London, C=GB"), intermediateKey.public)
keyStore.addOrReplaceKey(CORDA_INTERMEDIATE_CA, intermediateKey.private,
caPrivateKeyPassword.toCharArray(), CertPath(arrayOf(intermediateCert, rootKeyAndCert.certificate)))
caPrivateKeyPassword.toCharArray(), arrayOf(intermediateCert, rootKeyAndCert.certificate))
keyStore.save(keystorePath, keystorePassword)
println("Intermediate CA keypair and certificate stored in $keystorePath.")
println(loadKeyStore(keystorePath, keystorePassword).getCertificate(CORDA_INTERMEDIATE_CA).publicKey)
@ -199,7 +205,10 @@ private fun DoormanParameters.startDoorman() {
val rootCACert = keystore.getCertificateChain(CORDA_INTERMEDIATE_CA).last()
val caCertAndKey = keystore.getCertificateAndKeyPair(CORDA_INTERMEDIATE_CA, caPrivateKeyPassword)
// Create DB connection.
val database = configureDatabase(dataSourceProperties).second
val database = configureDatabase(dataSourceProperties, databaseProperties, { DoormanSchemaService() }, createIdentityService = {
// Identity service not needed doorman, corda persistence is not very generic.
throw UnsupportedOperationException()
})
val requestStorage = DBCertificateRequestStorage(database)

View File

@ -2,85 +2,110 @@ package com.r3.corda.doorman.persistence
import com.r3.corda.doorman.CertificateUtilities
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.commonName
import net.corda.node.utilities.instant
import net.corda.node.utilities.transaction
import org.apache.commons.io.IOUtils
import net.corda.core.utilities.validateX500Name
import net.corda.core.utilities.withCommonName
import net.corda.node.utilities.CordaPersistence
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.jetbrains.exposed.sql.*
import java.security.cert.Certificate
import java.time.Instant
import javax.sql.rowset.serial.SerialBlob
import javax.persistence.*
import javax.persistence.criteria.CriteriaBuilder
import javax.persistence.criteria.Path
import javax.persistence.criteria.Predicate
// TODO Relax the uniqueness requirement to be on the entire X.500 subject rather than just the legal name
class DBCertificateRequestStorage(private val database: Database) : CertificationRequestStorage {
private object DataTable : Table("certificate_signing_request") {
val requestId = varchar("request_id", 64).index().primaryKey()
val hostName = varchar("hostName", 100)
val ipAddress = varchar("ip_address", 15)
val legalName = varchar("legal_name", 256)
// TODO : Do we need to store this in column? or is it ok with blob.
val request = blob("request")
val requestTimestamp = instant("request_timestamp")
val processTimestamp = instant("process_timestamp").nullable()
val certificate = blob("certificate").nullable()
val rejectReason = varchar("reject_reason", 256).nullable()
}
class DBCertificateRequestStorage(private val database: CordaPersistence) : CertificationRequestStorage {
@Entity
@Table(name = "certificate_signing_request")
class CertificateSigningRequest(
@Id
@Column(name = "request_id", length = 64)
var requestId: String = "",
init {
// Create table if not exists.
database.transaction {
SchemaUtils.create(DataTable)
}
}
@Column(name = "host_name", length = 100)
var hostName: String = "",
@Column(name = "ip_address", length = 15)
var ipAddress: String = "",
@Column(name = "legal_name", length = 256)
var legalName: String = "",
@Lob
@Column
var request: ByteArray = ByteArray(0),
@Column(name = "request_timestamp")
var requestTimestamp: Instant = Instant.now(),
@Column(name = "process_timestamp", nullable = true)
var processTimestamp: Instant? = null,
@Lob
@Column(nullable = true)
var certificate: ByteArray? = null,
@Column(name = "reject_reason", length = 256, nullable = true)
var rejectReason: String? = null
)
override fun saveRequest(certificationData: CertificationRequestData): String {
val legalName = certificationData.request.subject.commonName
val legalName = certificationData.request.subject.withCommonName(null)
val requestId = SecureHash.randomSHA256().toString()
database.transaction {
val duplicate = DataTable.select {
// 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
DataTable.legalName eq legalName and (DataTable.certificate.isNotNull() or DataTable.processTimestamp.isNull())
}.any()
val rejectReason = if (duplicate) {
"Duplicate legal name"
} else if ("[=,]".toRegex() in legalName) {
"Legal name cannot contain '=' or ','"
} else {
null
}
val now = Instant.now()
DataTable.insert {
it[this.requestId] = requestId
it[hostName] = certificationData.hostName
it[ipAddress] = certificationData.ipAddress
it[this.legalName] = legalName
it[request] = SerialBlob(certificationData.request.encoded)
it[requestTimestamp] = now
if (rejectReason != null) {
it[this.rejectReason] = rejectReason
it[processTimestamp] = now
val query = session.criteriaBuilder.run {
val criteriaQuery = createQuery(CertificateSigningRequest::class.java)
criteriaQuery.from(CertificateSigningRequest::class.java).run {
val nameEq = equal(get<String>(CertificateSigningRequest::legalName.name), legalName.toString())
val certNotNull = isNotNull(get<String>(CertificateSigningRequest::certificate.name))
val processTimeIsNull = isNull(get<String>(CertificateSigningRequest::processTimestamp.name))
criteriaQuery.where(and(nameEq, or(certNotNull, processTimeIsNull)))
}
}
val duplicate = session.createQuery(query).resultList.isNotEmpty()
val rejectReason = if (duplicate) {
"Duplicate legal name"
} else {
try {
validateX500Name(legalName)
null
} catch (e: IllegalArgumentException) {
"Name validation failed with exception : ${e.message}"
}
}
val now = Instant.now()
val request = CertificateSigningRequest(
requestId,
certificationData.hostName,
certificationData.ipAddress,
legalName.toString(),
certificationData.request.encoded,
now,
rejectReason = rejectReason,
processTimestamp = rejectReason?.let { now }
)
session.save(request)
}
return requestId
}
override fun getResponse(requestId: String): CertificateResponse {
return database.transaction {
val response = DataTable
.select { DataTable.requestId eq requestId and DataTable.processTimestamp.isNotNull() }
.map { Pair(it[DataTable.certificate]?.let { IOUtils.toByteArray(it.binaryStream) }, it[DataTable.rejectReason]) }
.singleOrNull()
val response = singleRequestWhere { builder, path ->
val eq = builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), requestId)
val timeNotNull = builder.isNotNull(path.get<Instant>(CertificateSigningRequest::processTimestamp.name))
builder.and(eq, timeNotNull)
}
if (response == null) {
CertificateResponse.NotReady
} else {
val (certificate, rejectReason) = response
val certificate = response.certificate
if (certificate != null) {
CertificateResponse.Ready(CertificateUtilities.toX509Certificate(certificate))
} else {
CertificateResponse.Unauthorised(rejectReason!!)
CertificateResponse.Unauthorised(response.rejectReason!!)
}
}
}
@ -88,46 +113,67 @@ class DBCertificateRequestStorage(private val database: Database) : Certificatio
override fun approveRequest(requestId: String, generateCertificate: CertificationRequestData.() -> Certificate) {
database.transaction {
val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
val request = singleRequestWhere { builder, path ->
val eq = builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), requestId)
val timeIsNull = builder.isNull(path.get<Instant>(CertificateSigningRequest::processTimestamp.name))
builder.and(eq, timeIsNull)
}
if (request != null) {
DataTable.update({ DataTable.requestId eq requestId }) {
it[certificate] = SerialBlob(request.generateCertificate().encoded)
it[processTimestamp] = Instant.now()
}
request.certificate = request.toRequestData().generateCertificate().encoded
request.processTimestamp = Instant.now()
session.save(request)
}
}
}
override fun rejectRequest(requestId: String, rejectReason: String) {
database.transaction {
val request = singleRequestWhere { DataTable.requestId eq requestId and DataTable.processTimestamp.isNull() }
val request = singleRequestWhere { builder, path ->
val eq = builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), requestId)
val timeIsNull = builder.isNull(path.get<Instant>(CertificateSigningRequest::processTimestamp.name))
builder.and(eq, timeIsNull)
}
if (request != null) {
DataTable.update({ DataTable.requestId eq requestId }) {
it[this.rejectReason] = rejectReason
it[processTimestamp] = Instant.now()
}
request.rejectReason = rejectReason
request.processTimestamp = Instant.now()
session.save(request)
}
}
}
override fun getRequest(requestId: String): CertificationRequestData? {
return database.transaction {
singleRequestWhere { DataTable.requestId eq requestId }
}
singleRequestWhere { builder, path ->
builder.equal(path.get<String>(CertificateSigningRequest::requestId.name), requestId)
}
}?.toRequestData()
}
override fun getPendingRequestIds(): List<String> {
return database.transaction {
DataTable.select { DataTable.processTimestamp.isNull() }.map { it[DataTable.requestId] }
val builder = session.criteriaBuilder
val query = builder.createQuery(String::class.java).run {
from(CertificateSigningRequest::class.java).run {
select(get(CertificateSigningRequest::requestId.name))
where(builder.isNull(get<String>(CertificateSigningRequest::processTimestamp.name)))
}
}
session.createQuery(query).resultList
}
}
override fun getApprovedRequestIds(): List<String> = emptyList()
private fun singleRequestWhere(where: SqlExpressionBuilder.() -> Op<Boolean>): CertificationRequestData? {
return DataTable
.select(where)
.map { CertificationRequestData(it[DataTable.hostName], it[DataTable.ipAddress], PKCS10CertificationRequest(IOUtils.toByteArray(it[DataTable.request].binaryStream))) }
.singleOrNull()
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 CertificateSigningRequest.toRequestData() = CertificationRequestData(hostName, ipAddress, PKCS10CertificationRequest(request))
}

View File

@ -0,0 +1,21 @@
package com.r3.corda.doorman.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 {
// Entities for compulsory services
object DoormanServices
object DoormanServicesV1 : MappedSchema(schemaFamily = DoormanServices.javaClass, version = 1,
mappedTypes = listOf(DBCertificateRequestStorage.CertificateSigningRequest::class.java))
override val schemaOptions: Map<MappedSchema, SchemaService.SchemaOptions> = mapOf(Pair(DoormanServicesV1, SchemaService.SchemaOptions()))
override fun selectSchemas(state: ContractState): Iterable<MappedSchema> = setOf(DoormanServicesV1)
override fun generateMappedObject(state: ContractState, schema: MappedSchema): PersistentState = throw UnsupportedOperationException()
}

View File

@ -5,9 +5,11 @@ import com.atlassian.jira.rest.client.api.domain.Field
import com.atlassian.jira.rest.client.api.domain.IssueType
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.commonName
import net.corda.core.utilities.country
import net.corda.core.utilities.locality
import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.organisation
import net.corda.node.utilities.X509Utilities
import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.util.io.pem.PemObject
@ -39,14 +41,16 @@ class JiraCertificateRequestStorage(val delegate: CertificationRequestStorage,
JcaPEMWriter(request).use {
it.writeObject(PemObject("CERTIFICATE REQUEST", certificationData.request.encoded))
}
val commonName = certificationData.request.subject.commonName
val email = certificationData.request.subject.getRDNs(BCStyle.EmailAddress).firstOrNull()?.first?.value
val nearestCity = certificationData.request.subject.getRDNs(BCStyle.L).firstOrNull()?.first?.value
val organisation = certificationData.request.subject.organisation
val nearestCity = certificationData.request.subject.locality
val country = certificationData.request.subject.country
val email = certificationData.request.getAttributes(BCStyle.E).firstOrNull()?.attrValues?.firstOrNull()?.toString()
val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
.setProjectKey(projectCode)
.setDescription("Legal Name: $commonName\nNearest City: $nearestCity\nEmail: $email\n\n{code}$request{code}")
.setSummary(commonName)
.setDescription("Organisation: $organisation\nNearest City: $nearestCity\nCountry: $country\nEmail: $email\n\n{code}$request{code}")
.setSummary(organisation)
.setFieldValue(requestIdField.id, requestId)
// This will block until the issue is created.
jiraClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()

View File

@ -5,8 +5,13 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestData
import com.r3.corda.doorman.persistence.CertificationRequestStorage
import net.corda.core.crypto.*
import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.CertificateAndKeyPair
import net.corda.node.utilities.CertificateStream
import net.corda.node.utilities.CertificateType
import net.corda.node.utilities.X509Utilities
import net.corda.node.utilities.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import org.apache.commons.io.IOUtils
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
@ -57,7 +62,7 @@ class DoormanServiceTest {
startSigningServer(storage)
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val request = X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName"), keyPair)
val request = X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName"), "my@mail.com", keyPair)
// Post request to signing server via http.
assertEquals(id, submitRequest(request))
@ -80,7 +85,7 @@ class DoormanServiceTest {
on { approveRequest(eq(id), any()) }.then {
@Suppress("UNCHECKED_CAST")
val certGen = it.arguments[1] as ((CertificationRequestData) -> Certificate)
val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName,L=London"), keyPair))
val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName,L=London"), "my@mail.com", keyPair))
certificateStore[id] = certGen(request)
Unit
}
@ -126,7 +131,7 @@ class DoormanServiceTest {
on { approveRequest(eq(id), any()) }.then {
@Suppress("UNCHECKED_CAST")
val certGen = it.arguments[1] as ((CertificationRequestData) -> Certificate)
val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName,L=London"), keyPair))
val request = CertificationRequestData("", "", X509Utilities.createCertificateSigningRequest(X500Name("CN=LegalName,L=London"), "my@mail.com", keyPair))
certificateStore[id] = certGen(request)
Unit
}

View File

@ -3,15 +3,16 @@ package com.r3.corda.doorman.internal.persistence
import com.r3.corda.doorman.persistence.CertificateResponse
import com.r3.corda.doorman.persistence.CertificationRequestData
import com.r3.corda.doorman.persistence.DBCertificateRequestStorage
import com.r3.corda.doorman.persistence.DoormanSchemaService
import com.r3.corda.doorman.toX509Certificate
import net.corda.core.crypto.CertificateType
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.core.crypto.SecureHash
import net.corda.core.utilities.getX500Name
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 net.corda.testing.node.makeTestDataSourceProperties
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints
@ -19,29 +20,27 @@ import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After
import org.junit.Before
import org.junit.Test
import java.io.Closeable
import java.security.KeyPair
import java.util.*
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class DBCertificateRequestStorageTest {
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val intermediateCACert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Corda Node Intermediate CA"), intermediateCAKey)
private var closeDb: Closeable? = null
private val intermediateCACert = X509Utilities.createSelfSignedCACertificate(getX500Name(CN = "Corda Node Intermediate CA", O = "R3 Ltd", L = "London", C = "GB"), intermediateCAKey)
private lateinit var storage: DBCertificateRequestStorage
private lateinit var persistence: CordaPersistence
@Before
fun startDb() {
configureDatabase(makeTestDataSourceProperties()).apply {
closeDb = first
storage = DBCertificateRequestStorage(second)
}
persistence = configureDatabase(makeTestDataSourceProperties(), makeTestDatabaseProperties(), { DoormanSchemaService() }, createIdentityService = { throw UnsupportedOperationException() })
storage = DBCertificateRequestStorage(persistence)
}
@After
fun closeDb() {
closeDb?.close()
persistence.close()
}
@Test
@ -132,11 +131,11 @@ class DBCertificateRequestStorageTest {
}
private fun createRequest(legalName: String): Pair<CertificationRequestData, KeyPair> {
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val request = CertificationRequestData(
"hostname",
"0.0.0.0",
X509Utilities.createCertificateSigningRequest(X500Name("CN=$legalName"), keyPair))
X509Utilities.createCertificateSigningRequest(getX500Name(O = legalName, L = "London", C = "GB"), "my@mail.com", keyPair))
return Pair(request, keyPair)
}
@ -148,4 +147,22 @@ class DBCertificateRequestStorageTest {
}
}
}
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
}
}