mirror of
https://github.com/corda/corda.git
synced 2025-06-13 04:38:19 +00:00
Added various X509 utilities to remove some of the existing boilerplate. (#2416)
This commit is contained in:
@ -62,7 +62,7 @@ fun X509KeyStore.storeLegalIdentity(alias: String, keyPair: KeyPair = Crypto.gen
|
||||
val identityCertPath = listOf(identityCert) + nodeCaCertPath
|
||||
setPrivateKey(alias, keyPair.private, identityCertPath)
|
||||
save()
|
||||
return PartyAndCertificate(X509CertificateFactory().generateCertPath(identityCertPath))
|
||||
return PartyAndCertificate(X509Utilities.buildCertPath(identityCertPath))
|
||||
}
|
||||
|
||||
fun createDevNetworkMapCa(rootCa: CertificateAndKeyPair = DEV_ROOT_CA): CertificateAndKeyPair {
|
||||
|
@ -13,7 +13,13 @@ import java.security.cert.X509Certificate
|
||||
*/
|
||||
class X509KeyStore private constructor(val internal: KeyStore, private val storePassword: String, private val keyStoreFile: Path? = null) {
|
||||
/** Wrap an existing [KeyStore]. [save] is not supported. */
|
||||
constructor(internal: KeyStore, storePassword: String) : this(internal, storePassword, null)
|
||||
constructor(keyStore: KeyStore, storePassword: String) : this(keyStore, storePassword, null)
|
||||
|
||||
/** Create an empty [KeyStore] using the given password. [save] is not supported. */
|
||||
constructor(storePassword: String) : this(
|
||||
KeyStore.getInstance(KEYSTORE_TYPE).apply { load(null, storePassword.toCharArray()) },
|
||||
storePassword
|
||||
)
|
||||
|
||||
companion object {
|
||||
/**
|
||||
@ -49,12 +55,10 @@ class X509KeyStore private constructor(val internal: KeyStore, private val store
|
||||
}
|
||||
|
||||
fun setPrivateKey(alias: String, key: PrivateKey, certificates: List<X509Certificate>, keyPassword: String = storePassword) {
|
||||
checkWritableToFile()
|
||||
internal.setKeyEntry(alias, key, keyPassword.toCharArray(), certificates.toTypedArray())
|
||||
}
|
||||
|
||||
fun setCertificate(alias: String, certificate: X509Certificate) {
|
||||
checkWritableToFile()
|
||||
internal.setCertificateEntry(alias, certificate)
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import net.corda.core.crypto.SignatureScheme
|
||||
import net.corda.core.crypto.random63BitValue
|
||||
import net.corda.core.internal.CertRole
|
||||
import net.corda.core.internal.reader
|
||||
import net.corda.core.internal.uncheckedCast
|
||||
import net.corda.core.internal.writer
|
||||
import net.corda.core.utilities.days
|
||||
import net.corda.core.utilities.millis
|
||||
@ -93,15 +94,19 @@ object X509Utilities {
|
||||
return createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
|
||||
}
|
||||
|
||||
// TODO Provide an overload which takes in a List or a CertPath
|
||||
@Throws(CertPathValidatorException::class)
|
||||
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) {
|
||||
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: X509Certificate) {
|
||||
validateCertificateChain(trustedRoot, certificates.asList())
|
||||
}
|
||||
|
||||
fun validateCertificateChain(trustedRoot: X509Certificate, certificates: List<X509Certificate>) {
|
||||
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
|
||||
validateCertPath(trustedRoot, buildCertPath(certificates))
|
||||
}
|
||||
|
||||
fun validateCertPath(trustedRoot: X509Certificate, certPath: CertPath) {
|
||||
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null)))
|
||||
params.isRevocationEnabled = false
|
||||
val certPath = X509CertificateFactory().generateCertPath(*certificates)
|
||||
val pathValidator = CertPathValidator.getInstance("PKIX")
|
||||
pathValidator.validate(certPath, params)
|
||||
CertPathValidator.getInstance("PKIX").validate(certPath, params)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -265,6 +270,21 @@ object X509Utilities {
|
||||
fun createCertificateSigningRequest(subject: X500Principal, email: String, keyPair: KeyPair): PKCS10CertificationRequest {
|
||||
return createCertificateSigningRequest(subject, email, keyPair, DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
}
|
||||
|
||||
fun buildCertPath(first: X509Certificate, remaining: List<X509Certificate>): CertPath {
|
||||
val certificates = ArrayList<X509Certificate>(1 + remaining.size)
|
||||
certificates += first
|
||||
certificates += remaining
|
||||
return buildCertPath(certificates)
|
||||
}
|
||||
|
||||
fun buildCertPath(vararg certificates: X509Certificate): CertPath {
|
||||
return X509CertificateFactory().generateCertPath(*certificates)
|
||||
}
|
||||
|
||||
fun buildCertPath(certificates: List<X509Certificate>): CertPath {
|
||||
return X509CertificateFactory().generateCertPath(certificates)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -275,6 +295,16 @@ object X509Utilities {
|
||||
fun X509Certificate.toBc() = X509CertificateHolder(encoded)
|
||||
fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().generateCertificate(encoded.inputStream())
|
||||
|
||||
val CertPath.x509Certificates: List<X509Certificate> get() {
|
||||
require(type == "X.509") { "Not an X.509 cert path: $this" }
|
||||
// We're not mapping the list to avoid creating a new one.
|
||||
return uncheckedCast(certificates)
|
||||
}
|
||||
|
||||
val Certificate.x509: X509Certificate get() = requireNotNull(this as? X509Certificate) { "Not an X.509 certificate: $this" }
|
||||
|
||||
val Array<Certificate>.x509: List<X509Certificate> get() = map { it.x509 }
|
||||
|
||||
/**
|
||||
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||
* so assume this class is not.
|
||||
@ -282,19 +312,11 @@ fun X509CertificateHolder.toJca(): X509Certificate = X509CertificateFactory().ge
|
||||
class X509CertificateFactory {
|
||||
val delegate: CertificateFactory = CertificateFactory.getInstance("X.509")
|
||||
|
||||
fun generateCertificate(input: InputStream): X509Certificate {
|
||||
return delegate.generateCertificate(input) as X509Certificate
|
||||
}
|
||||
fun generateCertificate(input: InputStream): X509Certificate = delegate.generateCertificate(input).x509
|
||||
|
||||
// TODO X509Certificate
|
||||
fun generateCertPath(certificates: List<Certificate>): CertPath {
|
||||
return delegate.generateCertPath(certificates)
|
||||
}
|
||||
fun generateCertPath(vararg certificates: X509Certificate): CertPath = generateCertPath(certificates.asList())
|
||||
|
||||
// TODO X509Certificate
|
||||
fun generateCertPath(vararg certificates: Certificate): CertPath {
|
||||
return delegate.generateCertPath(certificates.asList())
|
||||
}
|
||||
fun generateCertPath(certificates: List<X509Certificate>): CertPath = delegate.generateCertPath(certificates)
|
||||
}
|
||||
|
||||
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean, val role: CertRole?) {
|
||||
|
@ -266,10 +266,10 @@ class X509UtilitiesTest {
|
||||
assertTrue(clientSocket.isConnected)
|
||||
|
||||
// Double check hostname manually
|
||||
val peerChain = clientSocket.session.peerCertificates
|
||||
val peerX500Principal = (peerChain[0] as X509Certificate).subjectX500Principal
|
||||
val peerChain = clientSocket.session.peerCertificates.x509
|
||||
val peerX500Principal = peerChain[0].subjectX500Principal
|
||||
assertEquals(MEGA_CORP.name.x500Principal, peerX500Principal)
|
||||
X509Utilities.validateCertificateChain(rootCa.certificate, *peerChain)
|
||||
X509Utilities.validateCertificateChain(rootCa.certificate, peerChain)
|
||||
val output = DataOutputStream(clientSocket.outputStream)
|
||||
output.writeUTF("Hello World")
|
||||
var timeout = 0
|
||||
@ -338,7 +338,7 @@ class X509UtilitiesTest {
|
||||
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
|
||||
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
|
||||
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
|
||||
val expected = X509CertificateFactory().generateCertPath(certificate, rootCACert)
|
||||
val expected = X509Utilities.buildCertPath(certificate, rootCACert)
|
||||
val serialized = expected.serialize(factory, context).bytes
|
||||
val actual: CertPath = serialized.deserialize(factory, context)
|
||||
assertEquals(expected, actual)
|
||||
|
Reference in New Issue
Block a user