Merged in pat-doorman-name-constraints (pull request #34)

Doorman now issue CA to client with name constraint.

* Doorman now issue CA to client with name constraint.

* address PR issues

Approved-by: Matthew Nesbit <matthew.nesbit@r3cev.com>
This commit is contained in:
Patrick Kuo 2017-06-08 09:30:59 +00:00 committed by Mike Hearn
parent cdb222cff2
commit ae691ab4e0
3 changed files with 58 additions and 9 deletions

View File

@ -83,7 +83,12 @@ class DoormanServer(webServerAddr: HostAndPort, val caCertAndKey: CertificateAnd
for (id in storage.getApprovedRequestIds()) {
storage.approveRequest(id) {
val request = JcaPKCS10CertificationRequest(request)
createCertificate(CertificateType.CLIENT_CA, caCertAndKey.certificate, caCertAndKey.keyPair, request.subject, request.publicKey, nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, request.subject))), arrayOf()))
// The sub certs issued by the client must satisfy this directory name (or legal name in Corda) constraints, sub certs' directory name must be within client CA's name's subtree,
// 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)
}
logger.info("Approved request $id")
serverStatus.lastApprovalTime = Instant.now()
@ -193,7 +198,7 @@ private fun DoormanParameters.startDoorman() {
val rootCACert = keystore.getCertificateChain(CORDA_INTERMEDIATE_CA).last()
val caCertAndKey = keystore.getCertificateAndKeyPair(caPrivateKeyPassword, CORDA_INTERMEDIATE_CA)
// Create DB connection.
val (datasource, database) = configureDatabase(dataSourceProperties)
val database = configureDatabase(dataSourceProperties).second
val requestStorage = DBCertificateRequestStorage(database)

View File

@ -10,6 +10,9 @@ import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import org.apache.commons.io.IOUtils
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
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After
@ -29,7 +32,7 @@ class DoormanServiceTest {
private val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val rootCACert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Corda Node Root CA,L=London"), rootCAKey)
private val intermediateCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
private val intermediateCACert = X509Utilities.createSelfSignedCACertificate(X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey)
private val intermediateCACert = X509Utilities.createCertificate(CertificateType.INTERMEDIATE_CA, rootCACert, rootCAKey, X500Name("CN=Corda Node Intermediate CA,L=London"), intermediateCAKey.public)
private lateinit var doormanServer: DoormanServer
private fun startSigningServer(storage: CertificationRequestStorage) {
@ -108,6 +111,48 @@ class DoormanServiceTest {
}
}
@Test
fun `retrieve certificate and create valid TLS certificate`() {
val keyPair = Crypto.generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val id = SecureHash.randomSHA256().toString()
// Mock Storage behaviour.
val certificateStore = mutableMapOf<String, Certificate>()
val storage = mock<CertificationRequestStorage> {
on { getResponse(eq(id)) }.then {
certificateStore[id]?.let { CertificateResponse.Ready(it) } ?: CertificateResponse.NotReady
}
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))
certificateStore[id] = certGen(request)
Unit
}
on { getPendingRequestIds() }.then { listOf(id) }
}
startSigningServer(storage)
assertThat(pollForResponse(id)).isEqualTo(PollResponse.NotReady)
storage.approveRequest(id) {
JcaPKCS10CertificationRequest(request).run {
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, X500Name("CN=LegalName, L=London")))), arrayOf())
X509Utilities.createCertificate(CertificateType.CLIENT_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints)
}
}
val certificates = (pollForResponse(id) as PollResponse.Ready).certChain
verify(storage, times(2)).getResponse(any())
assertEquals(3, certificates.size)
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, certificates.first(), keyPair, X500Name("CN=LegalName,L=London"), sslKey.public)
X509Utilities.validateCertificateChain(certificates.last(), sslCert, *certificates.toTypedArray())
}
@Test
fun `request not authorised`() {
val id = SecureHash.randomSHA256().toString()

View File

@ -11,6 +11,9 @@ 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
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest
import org.junit.After
import org.junit.Before
@ -139,12 +142,8 @@ class DBCertificateRequestStorageTest {
private fun approveRequest(requestId: String) {
storage.approveRequest(requestId) {
JcaPKCS10CertificationRequest(request).run {
X509Utilities.createCertificate(
CertificateType.TLS,
intermediateCACert,
intermediateCAKey,
subject,
publicKey)
val nameConstraints = NameConstraints(arrayOf(GeneralSubtree(GeneralName(GeneralName.directoryName, subject))), arrayOf())
X509Utilities.createCertificate(CertificateType.CLIENT_CA, intermediateCACert, intermediateCAKey, subject, publicKey, nameConstraints = nameConstraints)
}
}
}