mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +00:00
Uses config file instead of passing in arguments
Added H2 DB persistence Added background thread to approve request periodically
This commit is contained in:
parent
b9b90dffcd
commit
549a952cc0
@ -23,10 +23,25 @@ task buildCertSignerJAR(type: FatCapsule, dependsOn: 'jar') {
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
resources {
|
||||
srcDir "../config/dev"
|
||||
}
|
||||
}
|
||||
test {
|
||||
resources {
|
||||
srcDir "../config/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||
|
||||
compile project(":core")
|
||||
compile project(":node")
|
||||
testCompile project(":test-utils")
|
||||
|
||||
// Log4J: logging framework (with SLF4J bindings)
|
||||
compile "org.apache.logging.log4j:log4j-slf4j-impl:${log4j_version}"
|
||||
@ -46,6 +61,9 @@ dependencies {
|
||||
// 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"
|
||||
|
||||
// Unit testing helpers.
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile "org.assertj:assertj-core:${assertj_version}"
|
||||
|
@ -2,11 +2,16 @@ package com.r3corda.netpermission
|
||||
|
||||
import com.google.common.net.HostAndPort
|
||||
import com.r3corda.core.crypto.X509Utilities
|
||||
import com.r3corda.core.utilities.LogHelper
|
||||
import com.r3corda.core.utilities.debug
|
||||
import com.r3corda.core.utilities.loggerFor
|
||||
import com.r3corda.netpermission.internal.CertificateSigningService
|
||||
import com.r3corda.netpermission.internal.persistence.InMemoryCertificationRequestStorage
|
||||
import com.r3corda.netpermission.internal.persistence.DBCertificateRequestStorage
|
||||
import com.r3corda.node.services.config.ConfigHelper
|
||||
import com.r3corda.node.services.config.getProperties
|
||||
import com.r3corda.node.utilities.configureDatabase
|
||||
import joptsimple.ArgumentAcceptingOptionSpec
|
||||
import joptsimple.OptionParser
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.eclipse.jetty.server.Server
|
||||
import org.eclipse.jetty.server.ServerConnector
|
||||
import org.eclipse.jetty.server.handler.HandlerCollection
|
||||
@ -17,6 +22,7 @@ import org.glassfish.jersey.servlet.ServletContainer
|
||||
import java.io.Closeable
|
||||
import java.net.InetSocketAddress
|
||||
import java.nio.file.Paths
|
||||
import kotlin.concurrent.thread
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
/**
|
||||
@ -25,6 +31,7 @@ import kotlin.system.exitProcess
|
||||
* The Intermediate CA certificate,Intermediate CA private key and Root CA Certificate should use alias name specified in [X509Utilities]
|
||||
*/
|
||||
class CertificateSigningServer(val webServerAddr: HostAndPort, val certSigningService: CertificateSigningService) : Closeable {
|
||||
|
||||
companion object {
|
||||
val log = loggerFor<CertificateSigningServer>()
|
||||
fun Server.hostAndPort(): HostAndPort {
|
||||
@ -68,20 +75,11 @@ class CertificateSigningServer(val webServerAddr: HostAndPort, val certSigningSe
|
||||
|
||||
object ParamsSpec {
|
||||
val parser = OptionParser()
|
||||
val host = parser.accepts("host", "The hostname permissioning server will be running on.")
|
||||
.withRequiredArg().defaultsTo("localhost")
|
||||
val port = parser.accepts("port", "The port number permissioning server will be running on.")
|
||||
.withRequiredArg().ofType(Int::class.java).defaultsTo(0)
|
||||
val keystorePath = parser.accepts("keystore", "The path to the keyStore containing and root certificate, intermediate CA certificate and private key.")
|
||||
.withRequiredArg().required()
|
||||
val storePassword = parser.accepts("storePassword", "Keystore's password.")
|
||||
.withRequiredArg().required()
|
||||
val caKeyPassword = parser.accepts("caKeyPassword", "Intermediate CA private key password.")
|
||||
.withRequiredArg().required()
|
||||
val basedir: ArgumentAcceptingOptionSpec<String>? = parser.accepts("basedir", "Overriding configuration file path.")
|
||||
.withRequiredArg()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
LogHelper.setLevel(CertificateSigningServer::class)
|
||||
val log = CertificateSigningServer.log
|
||||
log.info("Starting certificate signing server.")
|
||||
try {
|
||||
@ -91,17 +89,48 @@ fun main(args: Array<String>) {
|
||||
ParamsSpec.parser.printHelpOn(System.out)
|
||||
exitProcess(1)
|
||||
}.run {
|
||||
// Load keystore from input path, default to Dev keystore from jar resource if path not defined.
|
||||
val storePassword = valueOf(ParamsSpec.storePassword)
|
||||
val keyPassword = valueOf(ParamsSpec.caKeyPassword)
|
||||
val keystore = X509Utilities.loadKeyStore(Paths.get(valueOf(ParamsSpec.keystorePath)).normalize(), storePassword)
|
||||
val intermediateCACertAndKey = X509Utilities.loadCertificateAndKey(keystore, keyPassword, X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY)
|
||||
val basedir = Paths.get(valueOf(ParamsSpec.basedir) ?: ".")
|
||||
val config = ConfigHelper.loadConfig(basedir)
|
||||
|
||||
val keystore = X509Utilities.loadKeyStore(Paths.get(config.getString("keystorePath")).normalize(), config.getString("keyStorePassword"))
|
||||
val intermediateCACertAndKey = X509Utilities.loadCertificateAndKey(keystore, config.getString("caKeyPassword"), X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY)
|
||||
val rootCA = keystore.getCertificateChain(X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY).last()
|
||||
|
||||
// TODO: Create a proper request storage using database or other storage technology.
|
||||
val service = CertificateSigningService(intermediateCACertAndKey, rootCA, InMemoryCertificationRequestStorage())
|
||||
// Create DB connection.
|
||||
val (datasource, database) = configureDatabase(config.getProperties("dataSourceProperties"))
|
||||
|
||||
CertificateSigningServer(HostAndPort.fromParts(valueOf(ParamsSpec.host), valueOf(ParamsSpec.port)), service).use {
|
||||
val storage = DBCertificateRequestStorage(database)
|
||||
val service = CertificateSigningService(intermediateCACertAndKey, rootCA, storage)
|
||||
|
||||
// Background thread approving all request periodically.
|
||||
var stopSigner = false
|
||||
val certSinger = if (config.getBoolean("approveAll")) {
|
||||
thread {
|
||||
while (!stopSigner) {
|
||||
Thread.sleep(1000)
|
||||
for (id in storage.pendingRequestIds()) {
|
||||
storage.saveCertificate(id, {
|
||||
JcaPKCS10CertificationRequest(it.request).run {
|
||||
X509Utilities.createServerCert(subject, publicKey, intermediateCACertAndKey,
|
||||
if (it.ipAddr == it.hostName) listOf() else listOf(it.hostName), listOf(it.ipAddr))
|
||||
}
|
||||
})
|
||||
log.debug { "Approved $id" }
|
||||
}
|
||||
}
|
||||
log.debug { "Certificate Signer thread stopped." }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
CertificateSigningServer(HostAndPort.fromParts(config.getString("host"), config.getInt("port")), service).use {
|
||||
Runtime.getRuntime().addShutdownHook(thread(false) {
|
||||
stopSigner = true
|
||||
certSinger?.join()
|
||||
it.close()
|
||||
datasource.close()
|
||||
})
|
||||
it.server.join()
|
||||
}
|
||||
}
|
||||
|
@ -53,14 +53,8 @@ class CertificateSigningService(val intermediateCACertAndKey: X509Utilities.CACe
|
||||
@Path("certificate/{var}")
|
||||
@Produces(MediaType.APPLICATION_OCTET_STREAM)
|
||||
fun retrieveCert(@PathParam("var") requestId: String): Response {
|
||||
val data = storage.getApprovedRequest(requestId)
|
||||
return if (data != null) {
|
||||
val clientCert = storage.getOrElseCreateCertificate(requestId) {
|
||||
JcaPKCS10CertificationRequest(data.request).run {
|
||||
X509Utilities.createServerCert(subject, publicKey, intermediateCACertAndKey,
|
||||
if (data.ipAddr == data.hostName) listOf() else listOf(data.hostName), listOf(data.ipAddr))
|
||||
}
|
||||
}
|
||||
val clientCert = storage.getCertificate(requestId)
|
||||
return if (clientCert != null) {
|
||||
// Write certificate chain to a zip stream and extract the bit array output.
|
||||
ByteArrayOutputStream().use {
|
||||
ZipOutputStream(it).use {
|
||||
|
@ -2,6 +2,7 @@ package com.r3corda.netpermission.internal.persistence
|
||||
|
||||
import org.bouncycastle.pkcs.PKCS10CertificationRequest
|
||||
import java.security.cert.Certificate
|
||||
|
||||
/**
|
||||
* Provide certificate signing request storage for the certificate signing server.
|
||||
*/
|
||||
@ -12,16 +13,25 @@ interface CertificationRequestStorage {
|
||||
fun saveRequest(certificationData: CertificationData): String
|
||||
|
||||
/**
|
||||
* Retrieve approved certificate singing request and Host/IP information using [requestId].
|
||||
* Returns [CertificationData] if request has been approved, else returns null.
|
||||
* Retrieve certificate singing request and Host/IP information using [requestId].
|
||||
*/
|
||||
fun getApprovedRequest(requestId: String): CertificationData?
|
||||
fun getRequest(requestId: String): CertificationData?
|
||||
|
||||
/**
|
||||
* Retrieve client certificate with provided [requestId].
|
||||
* Generate new certificate and store in storage using provided [certificateGenerator] if certificate does not exist.
|
||||
*/
|
||||
fun getOrElseCreateCertificate(requestId: String, certificateGenerator: () -> Certificate): Certificate
|
||||
fun getCertificate(requestId: String): Certificate?
|
||||
|
||||
/**
|
||||
* Generate new certificate and store in storage using provided [certificateGenerator].
|
||||
*/
|
||||
fun saveCertificate(requestId: String, certificateGenerator: (CertificationData) -> Certificate)
|
||||
|
||||
/**
|
||||
* Retrieve list of request IDs waiting for approval.
|
||||
* TODO : This is use for the background thread to approve request automatically without KYC checks, should be removed after testnet.
|
||||
*/
|
||||
fun pendingRequestIds(): List<String>
|
||||
}
|
||||
|
||||
data class CertificationData(val hostName: String, val ipAddr: String, val request: PKCS10CertificationRequest)
|
@ -0,0 +1,79 @@
|
||||
package com.r3corda.netpermission.internal.persistence
|
||||
|
||||
import com.r3corda.core.crypto.SecureHash
|
||||
import com.r3corda.node.utilities.databaseTransaction
|
||||
import com.r3corda.node.utilities.deserializeFromBlob
|
||||
import com.r3corda.node.utilities.localDateTime
|
||||
import com.r3corda.node.utilities.serializeToBlob
|
||||
import org.jetbrains.exposed.sql.*
|
||||
import java.security.cert.Certificate
|
||||
import java.time.LocalDateTime
|
||||
|
||||
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)
|
||||
// TODO : Do we need to store this in column? or is it ok with blob.
|
||||
val request = blob("request")
|
||||
val requestTimestamp = localDateTime("request_timestamp")
|
||||
val approvedTimestamp = localDateTime("approved_timestamp").nullable()
|
||||
val certificate = blob("certificate").nullable()
|
||||
}
|
||||
|
||||
init {
|
||||
// Create table if not exists.
|
||||
databaseTransaction(database) {
|
||||
SchemaUtils.create(DataTable)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCertificate(requestId: String): Certificate? {
|
||||
return databaseTransaction(database) { DataTable.select { DataTable.requestId.eq(requestId) }.map { it[DataTable.certificate] }.filterNotNull().map { deserializeFromBlob<Certificate>(it) }.firstOrNull() }
|
||||
}
|
||||
|
||||
override fun saveCertificate(requestId: String, certificateGenerator: (CertificationData) -> Certificate) {
|
||||
databaseTransaction(database) {
|
||||
val finalizables = mutableListOf<() -> Unit>()
|
||||
try {
|
||||
getRequest(requestId)?.let {
|
||||
val clientCert = certificateGenerator(it)
|
||||
DataTable.update({ DataTable.requestId eq requestId }) {
|
||||
it[approvedTimestamp] = LocalDateTime.now()
|
||||
it[certificate] = serializeToBlob(clientCert, finalizables)
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
finalizables.forEach { it() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRequest(requestId: String): CertificationData? {
|
||||
return databaseTransaction(database) { DataTable.select { DataTable.requestId eq requestId }.map { CertificationData(it[DataTable.hostName], it[DataTable.ipAddress], deserializeFromBlob(it[DataTable.request])) }.firstOrNull() }
|
||||
}
|
||||
|
||||
override fun saveRequest(certificationData: CertificationData): String {
|
||||
return databaseTransaction(database) {
|
||||
val finalizables = mutableListOf<() -> Unit>()
|
||||
try {
|
||||
val requestId = SecureHash.randomSHA256().toString()
|
||||
DataTable.insert {
|
||||
it[DataTable.requestId] = requestId
|
||||
it[hostName] = certificationData.hostName
|
||||
it[ipAddress] = certificationData.ipAddr
|
||||
it[DataTable.request] = serializeToBlob(certificationData.request, finalizables)
|
||||
it[requestTimestamp] = LocalDateTime.now()
|
||||
}
|
||||
requestId
|
||||
} finally {
|
||||
finalizables.forEach { it() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun pendingRequestIds(): List<String> {
|
||||
return databaseTransaction(database) { DataTable.select { DataTable.approvedTimestamp.isNull() }.map { it[DataTable.requestId] } }
|
||||
}
|
||||
}
|
@ -5,14 +5,24 @@ import java.security.cert.Certificate
|
||||
import java.util.*
|
||||
|
||||
class InMemoryCertificationRequestStorage : CertificationRequestStorage {
|
||||
val requestStore = HashMap<String, CertificationData>()
|
||||
val certificateStore = HashMap<String, Certificate>()
|
||||
private val requestStore = HashMap<String, CertificationData>()
|
||||
private val certificateStore = HashMap<String, Certificate>()
|
||||
|
||||
override fun getOrElseCreateCertificate(requestId: String, certificateGenerator: () -> Certificate): Certificate {
|
||||
return certificateStore.getOrPut(requestId, certificateGenerator)
|
||||
override fun pendingRequestIds(): List<String> {
|
||||
return requestStore.keys.filter { !certificateStore.keys.contains(it) }
|
||||
}
|
||||
|
||||
override fun getApprovedRequest(requestId: String): CertificationData? {
|
||||
override fun getCertificate(requestId: String): Certificate? {
|
||||
return certificateStore[requestId]
|
||||
}
|
||||
|
||||
override fun saveCertificate(requestId: String, certificateGenerator: (CertificationData) -> Certificate) {
|
||||
requestStore[requestId]?.let {
|
||||
certificateStore.putIfAbsent(requestId, certificateGenerator(it))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRequest(requestId: String): CertificationData? {
|
||||
return requestStore[requestId]
|
||||
}
|
||||
|
||||
|
14
netpermission/src/main/resources/reference.conf
Normal file
14
netpermission/src/main/resources/reference.conf
Normal file
@ -0,0 +1,14 @@
|
||||
host = localhost
|
||||
port = 0
|
||||
keystorePath = ${basedir}"/certificates/keystore.jks"
|
||||
keyStorePassword = "password"
|
||||
caKeyPassword = "password"
|
||||
approveAll = true
|
||||
|
||||
dataSourceProperties {
|
||||
dataSourceClassName = org.h2.jdbcx.JdbcDataSource
|
||||
"dataSource.url" = "jdbc:h2:file:"${basedir}"/persistence;DB_CLOSE_ON_EXIT=FALSE;LOCK_TIMEOUT=10000;MVCC=true;MV_STORE=true;WRITE_DELAY=0;AUTO_SERVER_PORT="${h2port}
|
||||
"dataSource.user" = sa
|
||||
"dataSource.password" = ""
|
||||
}
|
||||
h2port = 0
|
@ -4,11 +4,11 @@ import com.google.common.net.HostAndPort
|
||||
import com.nhaarman.mockito_kotlin.*
|
||||
import com.r3corda.core.crypto.SecureHash
|
||||
import com.r3corda.core.crypto.X509Utilities
|
||||
import com.r3corda.core.seconds
|
||||
import com.r3corda.netpermission.CertificateSigningServer.Companion.hostAndPort
|
||||
import com.r3corda.netpermission.internal.CertificateSigningService
|
||||
import com.r3corda.netpermission.internal.persistence.CertificationData
|
||||
import com.r3corda.netpermission.internal.persistence.CertificationRequestStorage
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.junit.Test
|
||||
import sun.security.x509.X500Name
|
||||
import java.io.IOException
|
||||
@ -20,11 +20,14 @@ import java.security.cert.X509Certificate
|
||||
import java.util.*
|
||||
import java.util.zip.ZipInputStream
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class CertificateSigningServiceTest {
|
||||
val rootCA = X509Utilities.createSelfSignedCACert("Corda Node Root CA")
|
||||
val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA")
|
||||
|
||||
private fun getSigningServer(storage: CertificationRequestStorage): CertificateSigningServer {
|
||||
val rootCA = X509Utilities.createSelfSignedCACert("Corda Node Root CA")
|
||||
val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA")
|
||||
return CertificateSigningServer(HostAndPort.fromParts("localhost", 0), CertificateSigningService(intermediateCA, rootCA.certificate, storage))
|
||||
}
|
||||
|
||||
@ -60,13 +63,18 @@ class CertificateSigningServiceTest {
|
||||
fun testRetrieveCertificate() {
|
||||
val keyPair = X509Utilities.generateECDSAKeyPairForSSL()
|
||||
val id = SecureHash.randomSHA256().toString()
|
||||
var count = 0
|
||||
|
||||
// Mock Storage behaviour.
|
||||
val certificateStore = mutableMapOf<String, Certificate>()
|
||||
val storage: CertificationRequestStorage = mock {
|
||||
on { getApprovedRequest(eq(id)) }.then {
|
||||
if (count < 5) null else CertificationData("", "", X509Utilities.createCertificateSigningRequest("LegalName",
|
||||
"London", "admin@test.com", keyPair))
|
||||
on { getCertificate(eq(id)) }.then { certificateStore[id] }
|
||||
on { saveCertificate(eq(id), any()) }.then {
|
||||
val certGen = it.arguments[1] as (CertificationData) -> Certificate
|
||||
val request = CertificationData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair))
|
||||
certificateStore[id] = certGen(request)
|
||||
Unit
|
||||
}
|
||||
on { getOrElseCreateCertificate(eq(id), any()) }.thenAnswer { (it.arguments[1] as () -> Certificate)() }
|
||||
on { pendingRequestIds() }.then { listOf(id) }
|
||||
}
|
||||
|
||||
getSigningServer(storage).use {
|
||||
@ -91,15 +99,20 @@ class CertificateSigningServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
var certificates = poll()
|
||||
assertNull(poll())
|
||||
assertNull(poll())
|
||||
|
||||
while (certificates == null) {
|
||||
Thread.sleep(1.seconds.toMillis())
|
||||
count++
|
||||
certificates = poll()
|
||||
}
|
||||
storage.saveCertificate(id, {
|
||||
JcaPKCS10CertificationRequest(it.request).run {
|
||||
X509Utilities.createServerCert(subject, publicKey, intermediateCA,
|
||||
if (it.ipAddr == it.hostName) listOf() else listOf(it.hostName), listOf(it.ipAddr))
|
||||
}
|
||||
})
|
||||
|
||||
val certificates = assertNotNull(poll())
|
||||
|
||||
verify(storage, times(3)).getCertificate(any())
|
||||
|
||||
verify(storage, times(6)).getApprovedRequest(any())
|
||||
assertEquals(3, certificates.size)
|
||||
|
||||
(certificates.first() as X509Certificate).run {
|
||||
|
@ -0,0 +1,81 @@
|
||||
package com.r3corda.netpermission.internal.persistence
|
||||
|
||||
import com.r3corda.core.crypto.X509Utilities
|
||||
import com.r3corda.node.utilities.configureDatabase
|
||||
import com.r3corda.testing.node.makeTestDataSourceProperties
|
||||
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class DBCertificateRequestStorageTest {
|
||||
|
||||
val intermediateCA = X509Utilities.createSelfSignedCACert("Corda Node Intermediate CA")
|
||||
|
||||
@Test
|
||||
fun testSaveRequest() {
|
||||
val keyPair = X509Utilities.generateECDSAKeyPairForSSL()
|
||||
val request = CertificationData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair))
|
||||
|
||||
val (connection, db) = configureDatabase(makeTestDataSourceProperties())
|
||||
connection.use {
|
||||
val storage = DBCertificateRequestStorage(db)
|
||||
val requestId = storage.saveRequest(request)
|
||||
|
||||
assertNotNull(storage.getRequest(requestId)).apply {
|
||||
assertEquals(request.hostName, hostName)
|
||||
assertEquals(request.ipAddr, ipAddr)
|
||||
assertEquals(request.request, this.request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testPendingRequest() {
|
||||
val keyPair = X509Utilities.generateECDSAKeyPairForSSL()
|
||||
val request = CertificationData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair))
|
||||
|
||||
val (connection, db) = configureDatabase(makeTestDataSourceProperties())
|
||||
connection.use {
|
||||
val storage = DBCertificateRequestStorage(db)
|
||||
val requestId = storage.saveRequest(request)
|
||||
storage.pendingRequestIds().apply {
|
||||
assertTrue(isNotEmpty())
|
||||
assertEquals(1, size)
|
||||
assertEquals(requestId, first())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSaveCertificate() {
|
||||
val keyPair = X509Utilities.generateECDSAKeyPairForSSL()
|
||||
val request = CertificationData("", "", X509Utilities.createCertificateSigningRequest("LegalName", "London", "admin@test.com", keyPair))
|
||||
|
||||
val (connection, db) = configureDatabase(makeTestDataSourceProperties())
|
||||
connection.use {
|
||||
val storage = DBCertificateRequestStorage(db)
|
||||
// Add request to DB.
|
||||
val requestId = storage.saveRequest(request)
|
||||
// Pending request should equals to 1.
|
||||
assertEquals(1, storage.pendingRequestIds().size)
|
||||
// Certificate should be empty.
|
||||
assertNull(storage.getCertificate(requestId))
|
||||
// Store certificate to DB.
|
||||
storage.saveCertificate(requestId, {
|
||||
JcaPKCS10CertificationRequest(it.request).run {
|
||||
X509Utilities.createServerCert(subject, publicKey, intermediateCA,
|
||||
if (it.ipAddr == it.hostName) listOf() else listOf(it.hostName), listOf(it.ipAddr))
|
||||
}
|
||||
})
|
||||
// Check certificate is stored in DB correctly.
|
||||
assertNotNull(storage.getCertificate(requestId)).apply {
|
||||
assertEquals(keyPair.public, this.publicKey)
|
||||
}
|
||||
// Pending request should be empty.
|
||||
assertTrue(storage.pendingRequestIds().isEmpty())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user