Replace X509Certificate with X509CertificateHolder

Replace X509Certificate with X509CertificateHolder for consistency in implementation of how X.509 certificates
are managed. Using the Java standard class entails the actual implementing class being one of several options
depending how a certificate is built, which makes serialization/deserialization with Kryo inconsistent as some
of these forms cannot be directly built from outside restricted classes.
This commit is contained in:
Ross Nicoll 2017-05-24 17:41:59 +01:00
parent 0fd897527a
commit c003ec0042
25 changed files with 257 additions and 122 deletions

View File

@ -9,6 +9,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1EncodableVector import org.bouncycastle.asn1.ASN1EncodableVector
import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.DERSequence import org.bouncycastle.asn1.DERSequence
import org.bouncycastle.asn1.bc.BCObjectIdentifiers import org.bouncycastle.asn1.bc.BCObjectIdentifiers
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
@ -19,8 +20,9 @@ import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers import org.bouncycastle.asn1.x9.X9ObjectIdentifiers
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.X509v3CertificateBuilder
import org.bouncycastle.cert.bc.BcX509ExtensionUtils import org.bouncycastle.cert.bc.BcX509ExtensionUtils
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
@ -29,6 +31,8 @@ import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.operator.ContentSigner
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
import org.bouncycastle.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
@ -42,7 +46,6 @@ import java.math.BigInteger
import java.security.* import java.security.*
import java.security.KeyFactory import java.security.KeyFactory
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.security.cert.X509Certificate
import java.security.spec.InvalidKeySpecException import java.security.spec.InvalidKeySpecException
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
@ -559,20 +562,25 @@ object Crypto {
} }
/** /**
* Use bouncy castle utilities to sign completed X509 certificate with CA cert private key. * Build a partial X.509 certificate ready for signing.
*
* @param issuer name of the issuing entity.
* @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/ */
fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair, fun createCertificate(certificateType: CertificateType, issuer: X500Name,
subject: X500Name, subjectPublicKey: PublicKey, subject: X500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>, validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509Certificate { nameConstraints: NameConstraints? = null): X509v3CertificateBuilder {
val signatureScheme = findSignatureScheme(issuerKeyPair.private)
val provider = providerMap[signatureScheme.providerName]
val serial = BigInteger.valueOf(random63BitValue()) val serial = BigInteger.valueOf(random63BitValue())
val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } }) val keyPurposes = DERSequence(ASN1EncodableVector().apply { certificateType.purposes.forEach { add(it) } })
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(subjectPublicKey.encoded))
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)
.addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(SubjectPublicKeyInfo.getInstance(subjectPublicKey.encoded))) .addExtension(Extension.subjectKeyIdentifier, false, BcX509ExtensionUtils().createSubjectKeyIdentifier(subjectPublicKeyInfo))
.addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA)) .addExtension(Extension.basicConstraints, certificateType.isCA, BasicConstraints(certificateType.isCA))
.addExtension(Extension.keyUsage, false, certificateType.keyUsage) .addExtension(Extension.keyUsage, false, certificateType.keyUsage)
.addExtension(Extension.extendedKeyUsage, false, keyPurposes) .addExtension(Extension.extendedKeyUsage, false, keyPurposes)
@ -580,11 +588,52 @@ object Crypto {
if (nameConstraints != null) { if (nameConstraints != null) {
builder.addExtension(Extension.nameConstraints, true, nameConstraints) builder.addExtension(Extension.nameConstraints, true, nameConstraints)
} }
return builder
}
/**
* Build and sign an X.509 certificate with the given signer.
*
* @param issuer name of the issuing entity.
* @param issuerSigner content signer to sign the certificate with.
* @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerSigner: ContentSigner,
subject: X500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
return builder.build(issuerSigner).apply {
require(isValidOn(Date()))
}
}
/**
* Build and sign an X.509 certificate with CA cert private key.
*
* @param issuer name of the issuing entity.
* @param issuerKeyPair the public & private key to sign the certificate with.
* @param subject name of the certificate subject.
* @param subjectPublicKey public key of the certificate subject.
* @param validityWindow the time period the certificate is valid for.
* @param nameConstraints any name constraints to impose on certificates signed by the generated certificate.
*/
fun createCertificate(certificateType: CertificateType, issuer: X500Name, issuerKeyPair: KeyPair,
subject: X500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Date, Date>,
nameConstraints: NameConstraints? = null): X509CertificateHolder {
val signatureScheme = findSignatureScheme(issuerKeyPair.private)
val provider = providerMap[signatureScheme.providerName]
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
return JcaX509CertificateConverter().setProvider(provider).getCertificate(builder.build(signer)).apply { return builder.build(signer).apply {
checkValidity(Date()) require(isValidOn(Date()))
verify(issuerKeyPair.public, provider) require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public)))
} }
} }
@ -671,6 +720,19 @@ object Crypto {
} }
} }
/**
* Convert a public key to a supported implementation. This method is usually required to retrieve a key from an
* [X509CertificateHolder].
*
* @param key a public key.
* @return a supported implementation of the input public key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key.
*/
fun toSupportedPublicKey(key: SubjectPublicKeyInfo): PublicKey {
return Crypto.decodePublicKey(key.encoded)
}
/** /**
* Convert a public key to a supported implementation. This can be used to convert a SUN's EC key to an BC key. * Convert a public key to a supported implementation. This can be used to convert a SUN's EC key to an BC key.
* This method is usually required to retrieve a key (via its corresponding cert) from JKS keystores that by default return SUN implementations. * This method is usually required to retrieve a key (via its corresponding cert) from JKS keystores that by default return SUN implementations.

View File

@ -22,6 +22,7 @@ val NULL_PARTY = AnonymousParty(NullPublicKey)
// TODO: Clean up this duplication between Null and Dummy public key // TODO: Clean up this duplication between Null and Dummy public key
@CordaSerializable @CordaSerializable
@Deprecated("Has encoding format problems, consider entropyToKeyPair() instead")
class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> { class DummyPublicKey(val s: String) : PublicKey, Comparable<PublicKey> {
override fun getAlgorithm() = "DUMMY" override fun getAlgorithm() = "DUMMY"
override fun getEncoded() = s.toByteArray() override fun getEncoded() = s.toByteArray()

View File

@ -3,6 +3,10 @@ package net.corda.core.crypto
import net.corda.core.exists import net.corda.core.exists
import net.corda.core.read import net.corda.core.read
import net.corda.core.write import net.corda.core.write
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.path.CertPath
import org.bouncycastle.crypto.util.PublicKeyFactory
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -67,6 +71,19 @@ object KeyStoreUtilities {
} }
} }
/**
* Helper extension method to add, or overwrite any key data in store.
* @param alias name to record the private key and certificate chain under.
* @param key cryptographic key to store.
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored,
* but for SSL purposes this is recommended.
* @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
*/
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: CertPath) {
val converter = JcaX509CertificateConverter()
addOrReplaceKey(alias, key, password, chain.certificates.map { it -> converter.getCertificate(it) }.toTypedArray<Certificate>())
}
/** /**
* Helper extension method to add, or overwrite any key data in store. * Helper extension method to add, or overwrite any key data in store.
* @param alias name to record the private key and certificate chain under. * @param alias name to record the private key and certificate chain under.
@ -122,8 +139,9 @@ fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertif
* @param keyPassword The password for the PrivateKey (not the store access password). * @param keyPassword The password for the PrivateKey (not the store access password).
*/ */
fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair { fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
val cert = getCertificate(alias) as X509Certificate val cert = getX509Certificate(alias)
return CertificateAndKeyPair(cert, KeyPair(Crypto.toSupportedPublicKey(cert.publicKey), getSupportedKey(alias, keyPassword))) val publicKey = Crypto.toSupportedPublicKey(cert.subjectPublicKeyInfo)
return CertificateAndKeyPair(cert, KeyPair(publicKey, getSupportedKey(alias, keyPassword)))
} }
/** /**
@ -131,7 +149,10 @@ fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): Certi
* @param alias The name to lookup the Key and Certificate chain from. * @param alias The name to lookup the Key and Certificate chain from.
* @return The X509Certificate found in the KeyStore under the specified alias. * @return The X509Certificate found in the KeyStore under the specified alias.
*/ */
fun KeyStore.getX509Certificate(alias: String): X509Certificate = getCertificate(alias) as X509Certificate fun KeyStore.getX509Certificate(alias: String): X509CertificateHolder {
val encoded = getCertificate(alias)?.encoded ?: throw IllegalArgumentException("No certificate under alias \"${alias}\"")
return X509CertificateHolder(encoded)
}
/** /**
* Extract a private key from a KeyStore file assuming storage alias is known. * Extract a private key from a KeyStore file assuming storage alias is known.

View File

@ -1,16 +1,17 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.identity.Party import net.corda.core.mapToArray
import net.corda.core.node.ServiceHub
import org.bouncycastle.asn1.ASN1Encodable import org.bouncycastle.asn1.ASN1Encodable
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.X500NameBuilder import org.bouncycastle.asn1.x500.X500NameBuilder
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.*
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.util.io.pem.PemReader import org.bouncycastle.util.io.pem.PemReader
import java.io.ByteArrayInputStream
import java.io.FileReader import java.io.FileReader
import java.io.FileWriter import java.io.FileWriter
import java.io.InputStream import java.io.InputStream
@ -62,7 +63,7 @@ object X509Utilities {
* @param after duration to roll forward returned end date relative to current date. * @param after duration to roll forward returned end date relative to current date.
* @param parent if provided certificate whose validity should bound the date interval returned. * @param parent if provided certificate whose validity should bound the date interval returned.
*/ */
private fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509Certificate? = null): Pair<Date, Date> { fun getCertificateValidityWindow(before: Duration, after: Duration, parent: X509CertificateHolder? = null): Pair<Date, Date> {
val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS) val startOfDayUTC = Instant.now().truncatedTo(ChronoUnit.DAYS)
val notBefore = max(startOfDayUTC - before, parent?.notBefore) val notBefore = max(startOfDayUTC - before, parent?.notBefore)
val notAfter = min(startOfDayUTC + after, parent?.notAfter) val notAfter = min(startOfDayUTC + after, parent?.notAfter)
@ -104,10 +105,9 @@ object X509Utilities {
* Create a de novo root self-signed X509 v3 CA cert. * Create a de novo root self-signed X509 v3 CA cert.
*/ */
@JvmStatic @JvmStatic
fun createSelfSignedCACertificate(subject: X500Name, keyPair: KeyPair, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509Certificate { fun createSelfSignedCACertificate(subject: X500Name, keyPair: KeyPair, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW): X509CertificateHolder {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
val cert = Crypto.createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window) return Crypto.createCertificate(CertificateType.ROOT_CA, subject, keyPair, subject, keyPair.public, window)
return cert
} }
/** /**
@ -122,13 +122,12 @@ object X509Utilities {
*/ */
@JvmStatic @JvmStatic
fun createCertificate(certificateType: CertificateType, fun createCertificate(certificateType: CertificateType,
issuerCertificate: X509Certificate, issuerKeyPair: KeyPair, issuerCertificate: X509CertificateHolder, issuerKeyPair: KeyPair,
subject: X500Name, subjectPublicKey: PublicKey, subject: X500Name, subjectPublicKey: PublicKey,
validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW, validityWindow: Pair<Duration, Duration> = DEFAULT_VALIDITY_WINDOW,
nameConstraints: NameConstraints? = null): X509Certificate { nameConstraints: NameConstraints? = null): X509CertificateHolder {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate) val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, issuerCertificate)
val cert = Crypto.createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints) return Crypto.createCertificate(certificateType, issuerCertificate.subject, issuerKeyPair, subject, subjectPublicKey, window, nameConstraints)
return cert
} }
/** /**
@ -139,17 +138,19 @@ object X509Utilities {
* @param certificates certificates in the path. * @param certificates certificates in the path.
* @param revocationEnabled whether revocation of certificates in the path should be checked. * @param revocationEnabled whether revocation of certificates in the path should be checked.
*/ */
fun createCertificatePath(trustedRoot: X509Certificate, vararg certificates: X509Certificate, revocationEnabled: Boolean): CertPath { fun createCertificatePath(trustedRoot: X509CertificateHolder, vararg certificates: X509CertificateHolder, revocationEnabled: Boolean): CertPath {
val certFactory = CertificateFactory.getInstance("X509") val certFactory = CertificateFactory.getInstance("X509")
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null))) val trustedRootX509 = certFactory.generateCertificate(ByteArrayInputStream(trustedRoot.encoded)) as X509Certificate
val params = PKIXParameters(setOf(TrustAnchor(trustedRootX509, null)))
params.isRevocationEnabled = revocationEnabled params.isRevocationEnabled = revocationEnabled
return certFactory.generateCertPath(certificates.toList()) return certFactory.generateCertPath(certificates.map { certFactory.generateCertificate(ByteArrayInputStream(it.encoded)) }.toList())
} }
fun validateCertificateChain(trustedRoot: X509Certificate, vararg certificates: Certificate) { fun validateCertificateChain(trustedRoot: X509CertificateHolder, vararg certificates: Certificate) {
require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } require(certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
val converter = JcaX509CertificateConverter()
val certFactory = CertificateFactory.getInstance("X509") val certFactory = CertificateFactory.getInstance("X509")
val params = PKIXParameters(setOf(TrustAnchor(trustedRoot, null))) val params = PKIXParameters(setOf(TrustAnchor(converter.getCertificate(trustedRoot), null)))
params.isRevocationEnabled = false params.isRevocationEnabled = false
val certPath = certFactory.generateCertPath(certificates.toList()) val certPath = certFactory.generateCertPath(certificates.toList())
val pathValidator = CertPathValidator.getInstance("PKIX") val pathValidator = CertPathValidator.getInstance("PKIX")
@ -162,7 +163,7 @@ object X509Utilities {
* @param filename Target filename. * @param filename Target filename.
*/ */
@JvmStatic @JvmStatic
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, filename: Path) { fun saveCertificateAsPEMFile(x509Certificate: X509CertificateHolder, filename: Path) {
FileWriter(filename.toFile()).use { FileWriter(filename.toFile()).use {
JcaPEMWriter(it).use { JcaPEMWriter(it).use {
it.writeObject(x509Certificate) it.writeObject(x509Certificate)
@ -176,11 +177,12 @@ object X509Utilities {
* @return The X509Certificate that was encoded in the file. * @return The X509Certificate that was encoded in the file.
*/ */
@JvmStatic @JvmStatic
fun loadCertificateFromPEMFile(filename: Path): X509Certificate { fun loadCertificateFromPEMFile(filename: Path): X509CertificateHolder {
val reader = PemReader(FileReader(filename.toFile())) val reader = PemReader(FileReader(filename.toFile()))
val pemObject = reader.readPemObject() val pemObject = reader.readPemObject()
return CertificateStream(pemObject.content.inputStream()).nextCertificate().apply { val cert = X509CertificateHolder(pemObject.content)
checkValidity() return cert.apply {
isValidOn(Date())
} }
} }
@ -221,7 +223,7 @@ object X509Utilities {
CORDA_CLIENT_CA, CORDA_CLIENT_CA,
clientKey.private, clientKey.private,
keyPass, keyPass,
arrayOf(clientCACert, intermediateCACert, rootCACert)) org.bouncycastle.cert.path.CertPath(arrayOf(clientCACert, intermediateCACert, rootCACert)))
clientCAKeystore.save(clientCAKeystorePath, storePassword) clientCAKeystore.save(clientCAKeystorePath, storePassword)
val tlsKeystore = KeyStoreUtilities.loadOrCreateKeyStore(sslKeyStorePath, storePassword) val tlsKeystore = KeyStoreUtilities.loadOrCreateKeyStore(sslKeyStorePath, storePassword)
@ -229,7 +231,7 @@ object X509Utilities {
CORDA_CLIENT_TLS, CORDA_CLIENT_TLS,
tlsKey.private, tlsKey.private,
keyPass, keyPass,
arrayOf(clientTLSCert, clientCACert, intermediateCACert, rootCACert)) org.bouncycastle.cert.path.CertPath(arrayOf(clientTLSCert, clientCACert, intermediateCACert, rootCACert)))
tlsKeystore.save(sslKeyStorePath, storePassword) tlsKeystore.save(sslKeyStorePath, storePassword)
} }
@ -286,7 +288,7 @@ class CertificateStream(val input: InputStream) {
fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate
} }
data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair) data class CertificateAndKeyPair(val certificate: X509CertificateHolder, val keyPair: KeyPair)
enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) { enum class CertificateType(val keyUsage: KeyUsage, vararg val purposes: KeyPurposeId, val isCA: Boolean) {
ROOT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true), ROOT_CA(KeyUsage(KeyUsage.digitalSignature or KeyUsage.keyCertSign or KeyUsage.cRLSign), KeyPurposeId.id_kp_serverAuth, KeyPurposeId.id_kp_clientAuth, KeyPurposeId.anyExtendedKeyUsage, isCA = true),

View File

@ -6,6 +6,7 @@ import net.corda.core.identity.Party
import net.corda.core.utilities.ProgressTracker import net.corda.core.utilities.ProgressTracker
import net.corda.core.utilities.unwrap import net.corda.core.utilities.unwrap
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -15,7 +16,7 @@ import java.security.cert.X509Certificate
*/ */
object TxKeyFlow { object TxKeyFlow {
abstract class AbstractIdentityFlow(val otherSide: Party, val revocationEnabled: Boolean): FlowLogic<Map<Party, AnonymousIdentity>>() { abstract class AbstractIdentityFlow(val otherSide: Party, val revocationEnabled: Boolean): FlowLogic<Map<Party, AnonymousIdentity>>() {
fun validateIdentity(untrustedIdentity: Pair<X509Certificate, CertPath>): AnonymousIdentity { fun validateIdentity(untrustedIdentity: Pair<X509CertificateHolder, CertPath>): AnonymousIdentity {
val (wellKnownCert, certPath) = untrustedIdentity val (wellKnownCert, certPath) = untrustedIdentity
val theirCert = certPath.certificates.last() val theirCert = certPath.certificates.last()
// TODO: Don't trust self-signed certificates // TODO: Don't trust self-signed certificates
@ -24,7 +25,7 @@ object TxKeyFlow {
if (certName == otherSide.name) { if (certName == otherSide.name) {
val anonymousParty = AnonymousParty(theirCert.publicKey) val anonymousParty = AnonymousParty(theirCert.publicKey)
serviceHub.identityService.registerPath(wellKnownCert, anonymousParty, certPath) serviceHub.identityService.registerPath(wellKnownCert, anonymousParty, certPath)
AnonymousIdentity(certPath, theirCert, anonymousParty) AnonymousIdentity(certPath, X509CertificateHolder(theirCert.encoded), anonymousParty)
} else } else
throw IllegalStateException("Expected certificate subject to be ${otherSide.name} but found ${certName}") throw IllegalStateException("Expected certificate subject to be ${otherSide.name} but found ${certName}")
} else } else
@ -50,7 +51,7 @@ object TxKeyFlow {
override fun call(): Map<Party, AnonymousIdentity> { override fun call(): Map<Party, AnonymousIdentity> {
progressTracker.currentStep = AWAITING_KEY progressTracker.currentStep = AWAITING_KEY
val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled) val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled)
val theirIdentity = receive<Pair<X509Certificate, CertPath>>(otherSide).unwrap { validateIdentity(it) } val theirIdentity = receive<Pair<X509CertificateHolder, CertPath>>(otherSide).unwrap { validateIdentity(it) }
send(otherSide, myIdentityFragment) send(otherSide, myIdentityFragment)
return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)), return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)),
Pair(serviceHub.myInfo.legalIdentity, theirIdentity)) Pair(serviceHub.myInfo.legalIdentity, theirIdentity))
@ -78,7 +79,7 @@ object TxKeyFlow {
progressTracker.currentStep = SENDING_KEY progressTracker.currentStep = SENDING_KEY
val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled) val myIdentityFragment = serviceHub.keyManagementService.freshKeyAndCert(serviceHub.myInfo.legalIdentity, revocationEnabled)
send(otherSide, myIdentityFragment) send(otherSide, myIdentityFragment)
val theirIdentity = receive<Pair<X509Certificate, CertPath>>(otherSide).unwrap { validateIdentity(it) } val theirIdentity = receive<Pair<X509CertificateHolder, CertPath>>(otherSide).unwrap { validateIdentity(it) }
return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)), return mapOf(Pair(otherSide, AnonymousIdentity(myIdentityFragment)),
Pair(serviceHub.myInfo.legalIdentity, theirIdentity)) Pair(serviceHub.myInfo.legalIdentity, theirIdentity))
} }
@ -86,9 +87,9 @@ object TxKeyFlow {
data class AnonymousIdentity( data class AnonymousIdentity(
val certPath: CertPath, val certPath: CertPath,
val certificate: X509Certificate, val certificate: X509CertificateHolder,
val identity: AnonymousParty) { val identity: AnonymousParty) {
constructor(myIdentity: Pair<X509Certificate, CertPath>) : this(myIdentity.second, constructor(myIdentity: Pair<X509CertificateHolder, CertPath>) : this(myIdentity.second,
myIdentity.first, myIdentity.first,
AnonymousParty(myIdentity.second.certificates.last().publicKey)) AnonymousParty(myIdentity.second.certificates.last().publicKey))
} }

View File

@ -26,8 +26,8 @@ import java.security.PublicKey
* *
* @see CompositeKey * @see CompositeKey
*/ */
class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) { open class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
constructor(certAndKey: CertificateAndKeyPair) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public) constructor(certAndKey: CertificateAndKeyPair) : this(certAndKey.certificate.subject, certAndKey.keyPair.public)
override fun toString() = name.toString() override fun toString() = name.toString()
override fun nameOrNull(): X500Name? = name override fun nameOrNull(): X500Name? = name

View File

@ -6,6 +6,7 @@ import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -30,7 +31,7 @@ interface IdentityService {
*/ */
// TODO: Move this into internal identity service once available // TODO: Move this into internal identity service once available
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun registerPath(trustedRoot: X509Certificate, anonymousParty: AnonymousParty, path: CertPath) fun registerPath(trustedRoot: X509CertificateHolder, anonymousParty: AnonymousParty, path: CertPath)
/** /**
* Asserts that an anonymous party maps to the given full party, by looking up the certificate chain associated with * Asserts that an anonymous party maps to the given full party, by looking up the certificate chain associated with

View File

@ -3,10 +3,12 @@ package net.corda.core.node.services
import co.paralleluniverse.fibers.Suspendable import co.paralleluniverse.fibers.Suspendable
import com.google.common.util.concurrent.ListenableFuture import com.google.common.util.concurrent.ListenableFuture
import net.corda.core.contracts.* import net.corda.core.contracts.*
import net.corda.core.crypto.* import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SecureHash
import net.corda.core.crypto.keys
import net.corda.core.flows.FlowException import net.corda.core.flows.FlowException
import net.corda.core.identity.AbstractParty import net.corda.core.identity.AbstractParty
import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.vault.PageSpecification import net.corda.core.node.services.vault.PageSpecification
import net.corda.core.node.services.vault.QueryCriteria import net.corda.core.node.services.vault.QueryCriteria
@ -17,6 +19,8 @@ import net.corda.core.toFuture
import net.corda.core.transactions.LedgerTransaction import net.corda.core.transactions.LedgerTransaction
import net.corda.core.transactions.TransactionBuilder import net.corda.core.transactions.TransactionBuilder
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import rx.Observable import rx.Observable
import java.io.InputStream import java.io.InputStream
import java.security.PublicKey import java.security.PublicKey
@ -396,7 +400,7 @@ interface KeyManagementService {
* @return X.509 certificate and path to the trust root. * @return X.509 certificate and path to the trust root.
*/ */
@Suspendable @Suspendable
fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath>
/** Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the data. /** Using the provided signing [PublicKey] internally looks up the matching [PrivateKey] and signs the data.
* @param bytes The data to sign over using the chosen key. * @param bytes The data to sign over using the chosen key.

View File

@ -18,6 +18,7 @@ import net.corda.core.utilities.NonEmptySetSerializer
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey
@ -102,11 +103,8 @@ object DefaultKryoCustomizer {
register(CertPath::class.java, CertPathSerializer) register(CertPath::class.java, CertPathSerializer)
register(X509CertPath::class.java, CertPathSerializer) register(X509CertPath::class.java, CertPathSerializer)
// TODO: We shouldn't need to serialize raw certificates, and if we do then we need a cleaner solution
// than this mess.
val x509CertObjectClazz = Class.forName("org.bouncycastle.jcajce.provider.asymmetric.x509.X509CertificateObject")
register(x509CertObjectClazz, X509CertificateSerializer)
register(X500Name::class.java, X500NameSerializer) register(X500Name::class.java, X500NameSerializer)
register(X509CertificateHolder::class.java, X509CertificateSerializer)
register(BCECPrivateKey::class.java, PrivateKeySerializer) register(BCECPrivateKey::class.java, PrivateKeySerializer)
register(BCECPublicKey::class.java, PublicKeySerializer) register(BCECPublicKey::class.java, PublicKeySerializer)

View File

@ -20,6 +20,7 @@ import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1InputStream import org.bouncycastle.asn1.ASN1InputStream
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
@ -632,16 +633,15 @@ object CertPathSerializer : Serializer<CertPath>() {
} }
/** /**
* For serialising an [CX509Certificate] in an X.500 standard format. * For serialising an [CX509CertificateHolder] in an X.500 standard format.
*/ */
@ThreadSafe @ThreadSafe
object X509CertificateSerializer : Serializer<X509Certificate>() { object X509CertificateSerializer : Serializer<X509CertificateHolder>() {
val factory = CertificateFactory.getInstance("X.509") override fun read(kryo: Kryo, input: Input, type: Class<X509CertificateHolder>): X509CertificateHolder {
override fun read(kryo: Kryo, input: Input, type: Class<X509Certificate>): X509Certificate { return X509CertificateHolder(input.readBytes())
return factory.generateCertificate(input) as X509Certificate
} }
override fun write(kryo: Kryo, output: Output, obj: X509Certificate) { override fun write(kryo: Kryo, output: Output, obj: X509CertificateHolder) {
output.writeBytes(obj.encoded) output.writeBytes(obj.encoded)
} }
} }

View File

@ -1,22 +1,23 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.mapToArray
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Test import org.junit.Test
import java.security.KeyStore import java.security.KeyStore
import java.security.cert.CertPathValidator import java.security.cert.*
import java.security.cert.CertPathValidatorException
import java.security.cert.CertificateFactory
import java.security.cert.PKIXParameters
import kotlin.test.assertFailsWith import kotlin.test.assertFailsWith
import kotlin.test.assertTrue import kotlin.test.assertTrue
class X509NameConstraintsTest { class X509NameConstraintsTest {
private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> { private fun makeKeyStores(subjectName: X500Name, nameConstraints: NameConstraints): Pair<KeyStore, KeyStore> {
val converter = JcaX509CertificateConverter()
val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootKeys = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = X509Utilities.createSelfSignedCACertificate(X509Utilities.getDevX509Name("Corda Root CA"), rootKeys) val rootCACert = X509Utilities.createSelfSignedCACertificate(X509Utilities.getDevX509Name("Corda Root CA"), rootKeys)
@ -29,14 +30,15 @@ class X509NameConstraintsTest {
val keyPass = "password" val keyPass = "password"
val trustStore = KeyStore.getInstance(KeyStoreUtilities.KEYSTORE_TYPE) val trustStore = KeyStore.getInstance(KeyStoreUtilities.KEYSTORE_TYPE)
trustStore.load(null, keyPass.toCharArray()) trustStore.load(null, keyPass.toCharArray())
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, converter.getCertificate(rootCACert))
val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val tlsKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientCAKeyPair, subjectName, tlsKey.public) val tlsCert = X509Utilities.createCertificate(CertificateType.TLS, clientCACert, clientCAKeyPair, subjectName, tlsKey.public)
val keyStore = KeyStore.getInstance(KeyStoreUtilities.KEYSTORE_TYPE) val keyStore = KeyStore.getInstance(KeyStoreUtilities.KEYSTORE_TYPE)
keyStore.load(null, keyPass.toCharArray()) keyStore.load(null, keyPass.toCharArray())
keyStore.addOrReplaceKey(X509Utilities.CORDA_CLIENT_TLS, tlsKey.private, keyPass.toCharArray(), arrayOf(tlsCert, clientCACert, intermediateCACert, rootCACert)) keyStore.addOrReplaceKey(X509Utilities.CORDA_CLIENT_TLS, tlsKey.private, keyPass.toCharArray(),
listOf(tlsCert, clientCACert, intermediateCACert, rootCACert).mapToArray<X509CertificateHolder, Certificate>(converter::getCertificate))
return Pair(keyStore, trustStore) return Pair(keyStore, trustStore)
} }

View File

@ -5,29 +5,35 @@ import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME import net.corda.core.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.core.crypto.X509Utilities.createSelfSignedCACertificate import net.corda.core.crypto.X509Utilities.createSelfSignedCACertificate
import net.corda.core.div import net.corda.core.div
import net.corda.core.mapToArray
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.getTestX509Name import net.corda.testing.getTestX509Name
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.BasicConstraints
import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.KeyUsage
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.io.DataInputStream import java.io.DataInputStream
import java.io.DataOutputStream import java.io.DataOutputStream
import java.io.IOException import java.io.IOException
import java.math.BigInteger
import java.net.InetAddress import java.net.InetAddress
import java.net.InetSocketAddress import java.net.InetSocketAddress
import java.nio.file.Path import java.nio.file.Path
import java.security.KeyStore import java.security.KeyStore
import java.security.PrivateKey import java.security.PrivateKey
import java.security.SecureRandom import java.security.SecureRandom
import java.security.cert.Certificate
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
import javax.net.ssl.* import javax.net.ssl.*
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.test.assertEquals import kotlin.test.*
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertTrue
class X509UtilitiesTest { class X509UtilitiesTest {
@Rule @Rule
@ -38,12 +44,14 @@ class X509UtilitiesTest {
fun `create valid self-signed CA certificate`() { fun `create valid self-signed CA certificate`() {
val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = createSelfSignedCACertificate(getTestX509Name("Test Cert"), caKey) val caCert = createSelfSignedCACertificate(getTestX509Name("Test Cert"), caKey)
assertTrue { caCert.subjectDN.name.contains("CN=Test Cert") } // using our subject common name assertTrue { caCert.subject.commonName == "Test Cert" } // using our subject common name
assertEquals(caCert.issuerDN, caCert.subjectDN) //self-signed assertEquals(caCert.issuer, caCert.subject) //self-signed
caCert.checkValidity(Date()) // throws on verification problems caCert.isValidOn(Date()) // throws on verification problems
caCert.verify(caKey.public) // throws on verification problems caCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
assertTrue { caCert.keyUsage[5] } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) val basicConstraints = BasicConstraints.getInstance(caCert.getExtension(Extension.basicConstraints).parsedValue)
assertTrue { caCert.basicConstraints > 0 } // This returns the signing path length Would be -1 for non-CA certificate val keyUsage = KeyUsage.getInstance(caCert.getExtension(Extension.keyUsage).parsedValue)
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
assertNull(basicConstraints.pathLenConstraint) // No length constraint specified on this CA certificate
} }
@Test @Test
@ -60,29 +68,33 @@ class X509UtilitiesTest {
fun `create valid server certificate chain`() { fun `create valid server certificate chain`() {
val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val caKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = createSelfSignedCACertificate(getTestX509Name("Test CA Cert"), caKey) val caCert = createSelfSignedCACertificate(getTestX509Name("Test CA Cert"), caKey)
val subjectDN = getTestX509Name("Server Cert") val subject = getTestX509Name("Server Cert")
val keyPair = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subjectDN, keyPair.public) val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public)
assertTrue { serverCert.subjectDN.name.contains("CN=Server Cert") } // using our subject common name assertTrue { serverCert.subject.toString().contains("CN=Server Cert") } // using our subject common name
assertEquals(caCert.issuerDN, serverCert.issuerDN) // Issued by our CA cert assertEquals(caCert.issuer, serverCert.issuer) // Issued by our CA cert
serverCert.checkValidity(Date()) // throws on verification problems serverCert.isValidOn(Date()) // throws on verification problems
serverCert.verify(caKey.public) // throws on verification problems serverCert.isSignatureValid(JcaContentVerifierProviderBuilder().build(caKey.public)) // throws on verification problems
assertFalse { serverCert.keyUsage[5] } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) val basicConstraints = BasicConstraints.getInstance(serverCert.getExtension(Extension.basicConstraints).parsedValue)
assertTrue { serverCert.basicConstraints == -1 } // This returns the signing path length should be -1 for non-CA certificate val keyUsage = KeyUsage.getInstance(serverCert.getExtension(Extension.keyUsage).parsedValue)
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property)
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate
} }
@Test @Test
fun `storing EdDSA key in java keystore`() { fun `storing EdDSA key in java keystore`() {
val tmpKeyStore = tempFile("keystore.jks") val tmpKeyStore = tempFile("keystore.jks")
val converter = JcaX509CertificateConverter()
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) val keyPair = generateKeyPair(EDDSA_ED25519_SHA512)
val selfSignCert = createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) val selfSignCert = createSelfSignedCACertificate(X500Name("CN=Test"), keyPair)
assertEquals(selfSignCert.publicKey, keyPair.public) assertTrue(Arrays.equals(selfSignCert.subjectPublicKeyInfo.encoded, keyPair.public.encoded))
// Save the EdDSA private key with self sign cert in the keystore. // Save the EdDSA private key with self sign cert in the keystore.
val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tmpKeyStore, "keystorepass") val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert)) keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(),
listOf(selfSignCert).mapToArray(converter::getCertificate))
keyStore.save(tmpKeyStore, "keystorepass") keyStore.save(tmpKeyStore, "keystorepass")
// Load the keystore from file and make sure keys are intact. // Load the keystore from file and make sure keys are intact.
@ -105,8 +117,10 @@ class X509UtilitiesTest {
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public) val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, X500Name("CN=TestEdDSA"), edDSAKeypair.public)
// Save the EdDSA private key with cert chains. // Save the EdDSA private key with cert chains.
val converter = JcaX509CertificateConverter()
val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tmpKeyStore, "keystorepass") val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert)) keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(),
listOf(ecDSACert, edDSACert).mapToArray(converter::getCertificate))
keyStore.save(tmpKeyStore, "keystorepass") keyStore.save(tmpKeyStore, "keystorepass")
// Load the keystore from file and make sure keys are intact. // Load the keystore from file and make sure keys are intact.
@ -182,23 +196,24 @@ class X509UtilitiesTest {
val serverKeyStore = KeyStoreUtilities.loadKeyStore(tmpServerKeyStore, "serverstorepass") val serverKeyStore = KeyStoreUtilities.loadKeyStore(tmpServerKeyStore, "serverstorepass")
val serverCertAndKey = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, "serverkeypass") val serverCertAndKey = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, "serverkeypass")
serverCertAndKey.certificate.checkValidity(Date()) serverCertAndKey.certificate.isValidOn(Date())
serverCertAndKey.certificate.verify(caCertAndKey.certificate.publicKey) serverCertAndKey.certificate.isSignatureValid(JcaContentVerifierProviderBuilder().build(caCertAndKey.certificate.subjectPublicKeyInfo))
assertTrue { serverCertAndKey.certificate.subjectDN.name.contains(MEGA_CORP.name.commonName) } assertTrue { serverCertAndKey.certificate.subject.toString().contains(MEGA_CORP.name.commonName) }
// Load back server certificate // Load back server certificate
val sslKeyStore = KeyStoreUtilities.loadKeyStore(tmpSSLKeyStore, "serverstorepass") val sslKeyStore = KeyStoreUtilities.loadKeyStore(tmpSSLKeyStore, "serverstorepass")
val sslCertAndKey = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, "serverkeypass") val sslCertAndKey = sslKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_TLS, "serverkeypass")
sslCertAndKey.certificate.checkValidity(Date()) sslCertAndKey.certificate.isValidOn(Date())
sslCertAndKey.certificate.verify(serverCertAndKey.certificate.publicKey) sslCertAndKey.certificate.isSignatureValid(JcaContentVerifierProviderBuilder().build(serverCertAndKey.certificate.subjectPublicKeyInfo))
assertTrue { sslCertAndKey.certificate.subjectDN.name.contains(MEGA_CORP.name.commonName) } assertTrue { sslCertAndKey.certificate.subject.toString().contains(MEGA_CORP.name.commonName) }
// Now sign something with private key and verify against certificate public key // Now sign something with private key and verify against certificate public key
val testData = "123456".toByteArray() val testData = "123456".toByteArray()
val signature = Crypto.doSign(DEFAULT_TLS_SIGNATURE_SCHEME, serverCertAndKey.keyPair.private, testData) val signature = Crypto.doSign(DEFAULT_TLS_SIGNATURE_SCHEME, serverCertAndKey.keyPair.private, testData)
assertTrue { Crypto.isValid(DEFAULT_TLS_SIGNATURE_SCHEME, serverCertAndKey.certificate.publicKey, signature, testData) } val publicKey = Crypto.toSupportedPublicKey(serverCertAndKey.certificate.subjectPublicKeyInfo)
assertTrue { Crypto.isValid(DEFAULT_TLS_SIGNATURE_SCHEME, publicKey, signature, testData) }
} }
@Test @Test
@ -330,6 +345,7 @@ class X509UtilitiesTest {
trustStoreFilePath: Path, trustStoreFilePath: Path,
trustStorePassword: String trustStorePassword: String
): KeyStore { ): KeyStore {
val converter = JcaX509CertificateConverter()
val rootCAKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME) val rootCAKey = generateKeyPair(DEFAULT_TLS_SIGNATURE_SCHEME)
val rootCACert = createSelfSignedCACertificate(X509Utilities.getDevX509Name("Corda Node Root CA"), rootCAKey) val rootCACert = createSelfSignedCACertificate(X509Utilities.getDevX509Name("Corda Node Root CA"), rootCAKey)
@ -339,19 +355,19 @@ class X509UtilitiesTest {
val keyPass = keyPassword.toCharArray() val keyPass = keyPassword.toCharArray()
val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(keyStoreFilePath, storePassword) val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(keyStoreFilePath, storePassword)
keyStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, rootCAKey.private, keyPass, arrayOf(rootCACert)) keyStore.addOrReplaceKey(X509Utilities.CORDA_ROOT_CA, rootCAKey.private, keyPass, arrayOf(converter.getCertificate(rootCACert) as Certificate))
keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA, keyStore.addOrReplaceKey(X509Utilities.CORDA_INTERMEDIATE_CA,
intermediateCAKeyPair.private, intermediateCAKeyPair.private,
keyPass, keyPass,
arrayOf(intermediateCACert, rootCACert)) listOf(intermediateCACert, rootCACert).mapToArray<X509CertificateHolder, Certificate>(converter::getCertificate))
keyStore.save(keyStoreFilePath, storePassword) keyStore.save(keyStoreFilePath, storePassword)
val trustStore = KeyStoreUtilities.loadOrCreateKeyStore(trustStoreFilePath, trustStorePassword) val trustStore = KeyStoreUtilities.loadOrCreateKeyStore(trustStoreFilePath, trustStorePassword)
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, rootCACert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_ROOT_CA, converter.getCertificate(rootCACert))
trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, intermediateCACert) trustStore.addOrReplaceCertificate(X509Utilities.CORDA_INTERMEDIATE_CA, converter.getCertificate(intermediateCACert))
trustStore.save(trustStoreFilePath, trustStorePassword) trustStore.save(trustStoreFilePath, trustStorePassword)
@ -360,10 +376,11 @@ class X509UtilitiesTest {
@Test @Test
fun `Get correct private key type from Keystore`() { fun `Get correct private key type from Keystore`() {
val converter = JcaX509CertificateConverter()
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val selfSignCert = createSelfSignedCACertificate(X500Name("CN=Test"), keyPair) val selfSignCert = createSelfSignedCACertificate(X500Name("CN=Test"), keyPair)
val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert)) keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(converter.getCertificate(selfSignCert)))
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")

View File

@ -10,6 +10,7 @@ import net.corda.node.services.persistence.NodeAttachmentService
import net.corda.testing.BOB_PUBKEY import net.corda.testing.BOB_PUBKEY
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.cert.X509CertificateHolder
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -142,10 +143,10 @@ class KryoTests {
} }
@Test @Test
fun `serialize - deserialize X509Certififcate`() { fun `serialize - deserialize X509CertififcateHolder`() {
val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val expected: X509CertificateHolder = X509Utilities.createSelfSignedCACertificate(ALICE.name, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
val serialized = expected.serialize(kryo).bytes val serialized = expected.serialize(kryo).bytes
val actual: X509Certificate = serialized.deserialize(kryo) val actual: X509CertificateHolder = serialized.deserialize(kryo)
assertEquals(expected, actual) assertEquals(expected, actual)
} }

View File

@ -19,6 +19,7 @@ import org.assertj.core.api.Assertions.assertThatExceptionOfType
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralSubtree import org.bouncycastle.asn1.x509.GeneralSubtree
import org.bouncycastle.asn1.x509.NameConstraints import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.cert.path.CertPath
import org.junit.Test import org.junit.Test
import java.nio.file.Files import java.nio.file.Files
@ -111,7 +112,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
X509Utilities.CORDA_CLIENT_CA, X509Utilities.CORDA_CLIENT_CA,
clientKey.private, clientKey.private,
keyPass, keyPass,
arrayOf(clientCACert, intermediateCA.certificate, rootCACert)) CertPath(arrayOf(clientCACert, intermediateCA.certificate, rootCACert)))
clientCAKeystore.save(nodeKeystore, keyStorePassword) clientCAKeystore.save(nodeKeystore, keyStorePassword)
val tlsKeystore = KeyStoreUtilities.loadOrCreateKeyStore(sslKeystore, keyStorePassword) val tlsKeystore = KeyStoreUtilities.loadOrCreateKeyStore(sslKeystore, keyStorePassword)
@ -119,7 +120,7 @@ class MQSecurityAsNodeTest : MQSecurityTest() {
X509Utilities.CORDA_CLIENT_TLS, X509Utilities.CORDA_CLIENT_TLS,
tlsKey.private, tlsKey.private,
keyPass, keyPass,
arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)) CertPath(arrayOf(clientTLSCert, clientCACert, intermediateCA.certificate, rootCACert)))
tlsKeystore.save(sslKeystore, keyStorePassword) tlsKeystore.save(sslKeystore, keyStorePassword)
} }
} }

View File

@ -57,6 +57,7 @@ import net.corda.node.utilities.transaction
import org.apache.activemq.artemis.utils.ReusableLatch import org.apache.activemq.artemis.utils.ReusableLatch
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.Database
import org.slf4j.Logger import org.slf4j.Logger
import java.io.IOException import java.io.IOException
@ -671,8 +672,10 @@ private class KeyStoreWrapper(private val storePath: Path, private val storePass
fun save(serviceName: X500Name, privateKeyAlias: String, keyPair: KeyPair) { fun save(serviceName: X500Name, privateKeyAlias: String, keyPair: KeyPair) {
val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, storePassword) val clientCA = keyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA, storePassword)
val converter = JcaX509CertificateConverter()
val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, keyPair.public) val cert = X509Utilities.createCertificate(CertificateType.IDENTITY, clientCA.certificate, clientCA.keyPair, serviceName, keyPair.public)
keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(), arrayOf(cert, *keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA))) keyStore.addOrReplaceKey(privateKeyAlias, keyPair.private, storePassword.toCharArray(),
arrayOf(converter.getCertificate(cert), *keyStore.getCertificateChain(X509Utilities.CORDA_CLIENT_CA)))
keyStore.save(storePath, storePassword) keyStore.save(storePath, storePassword)
} }
} }

View File

@ -11,6 +11,8 @@ import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.core.utilities.loggerFor import net.corda.core.utilities.loggerFor
import net.corda.core.utilities.trace import net.corda.core.utilities.trace
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import java.security.InvalidAlgorithmParameterException import java.security.InvalidAlgorithmParameterException
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.* import java.security.cert.*
@ -82,8 +84,9 @@ class InMemoryIdentityService(identities: Iterable<Party> = emptySet(),
override fun pathForAnonymous(anonymousParty: AnonymousParty): CertPath? = partyToPath[anonymousParty] override fun pathForAnonymous(anonymousParty: AnonymousParty): CertPath? = partyToPath[anonymousParty]
@Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class) @Throws(CertificateExpiredException::class, CertificateNotYetValidException::class, InvalidAlgorithmParameterException::class)
override fun registerPath(trustedRoot: X509Certificate, anonymousParty: AnonymousParty, path: CertPath) { override fun registerPath(trustedRoot: X509CertificateHolder, anonymousParty: AnonymousParty, path: CertPath) {
val expectedTrustAnchor = TrustAnchor(trustedRoot, null) val converter = JcaX509CertificateConverter()
val expectedTrustAnchor = TrustAnchor(converter.getCertificate(trustedRoot), null)
require(path.certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" } require(path.certificates.isNotEmpty()) { "Certificate path must contain at least one certificate" }
val target = path.certificates.last() as X509Certificate val target = path.certificates.last() as X509Certificate
require(target.publicKey == anonymousParty.owningKey) { "Certificate path must end with anonymous party's public key" } require(target.publicKey == anonymousParty.owningKey) { "Certificate path must end with anonymous party's public key" }

View File

@ -9,11 +9,12 @@ import net.corda.core.identity.Party
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.util.* import java.util.*
import javax.annotation.concurrent.ThreadSafe import javax.annotation.concurrent.ThreadSafe
@ -56,7 +57,7 @@ class E2ETestKeyManagementService(val identityService: IdentityService,
return keyPair.public return keyPair.public
} }
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled) override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair { private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
return mutex.locked { return mutex.locked {

View File

@ -1,12 +1,17 @@
package net.corda.node.services.keys package net.corda.node.services.keys
import net.corda.core.crypto.CertificateType import net.corda.core.crypto.CertificateType
import net.corda.core.crypto.ContentSignerBuilder
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.X509Utilities
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.node.services.IdentityService import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import java.security.KeyPair
import java.security.Security
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
@ -23,7 +28,7 @@ import java.security.cert.X509Certificate
fun freshKeyAndCert(keyManagementService: KeyManagementService, fun freshKeyAndCert(keyManagementService: KeyManagementService,
identityService: IdentityService, identityService: IdentityService,
identity: Party, identity: Party,
revocationEnabled: Boolean = false): Pair<X509Certificate, CertPath> { revocationEnabled: Boolean = false): Pair<X509CertificateHolder, CertPath> {
val ourPublicKey = keyManagementService.freshKey() val ourPublicKey = keyManagementService.freshKey()
// FIXME: Use the actual certificate for the identity the flow is presenting themselves as // FIXME: Use the actual certificate for the identity the flow is presenting themselves as
val issuerKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME) val issuerKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME)

View File

@ -10,13 +10,14 @@ import net.corda.core.node.services.IdentityService
import net.corda.core.node.services.KeyManagementService import net.corda.core.node.services.KeyManagementService
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
import net.corda.node.utilities.* import net.corda.node.utilities.*
import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.operator.ContentSigner
import org.jetbrains.exposed.sql.ResultRow import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.statements.InsertStatement import org.jetbrains.exposed.sql.statements.InsertStatement
import java.security.KeyPair import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate
/** /**
* A persistent re-implementation of [E2ETestKeyManagementService] to support node re-start. * A persistent re-implementation of [E2ETestKeyManagementService] to support node re-start.
@ -66,8 +67,7 @@ class PersistentKeyManagementService(val identityService: IdentityService,
} }
return keyPair.public return keyPair.public
} }
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair { private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
return mutex.locked { return mutex.locked {

View File

@ -263,10 +263,9 @@ class ArtemisMessagingServer(override val config: NodeConfiguration,
val trustStore = KeyStoreUtilities.loadKeyStore(config.trustStoreFile, config.trustStorePassword) val trustStore = KeyStoreUtilities.loadKeyStore(config.trustStoreFile, config.trustStorePassword)
val ourCertificate = keyStore.getX509Certificate(CORDA_CLIENT_TLS) val ourCertificate = keyStore.getX509Certificate(CORDA_CLIENT_TLS)
val ourSubjectDN = X500Name(ourCertificate.subjectDN.name)
// This is a sanity check and should not fail unless things have been misconfigured // This is a sanity check and should not fail unless things have been misconfigured
require(ourSubjectDN == config.myLegalName) { require(ourCertificate.subject == config.myLegalName) {
"Legal name does not match with our subject CN: $ourSubjectDN" "Legal name does not match with our subject CN: ${ourCertificate.subject}"
} }
val defaultCertPolicies = mapOf( val defaultCertPolicies = mapOf(
PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch, PEER_ROLE to CertificateChainCheckPolicy.RootMustMatch,
@ -510,7 +509,7 @@ private class VerifyingNettyConnector(configuration: MutableMap<String, Any>,
"Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " + "Peer has wrong subject name in the certificate - expected $expectedLegalName but got ${peerCertificate.subject}. This is either a fatal " +
"misconfiguration by the remote peer or an SSL man-in-the-middle attack!" "misconfiguration by the remote peer or an SSL man-in-the-middle attack!"
} }
X509Utilities.validateCertificateChain(X509CertImpl(session.localCertificates.last().encoded), *session.peerCertificates) X509Utilities.validateCertificateChain(X509CertificateHolder(session.localCertificates.last().encoded), *session.peerCertificates)
server.onTcpConnection(peerLegalName) server.onTcpConnection(peerLegalName)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
connection.close() connection.close()

View File

@ -2,7 +2,9 @@ package net.corda.node.services.network
import com.google.common.annotations.VisibleForTesting import com.google.common.annotations.VisibleForTesting
import net.corda.core.ThreadBox import net.corda.core.ThreadBox
import net.corda.core.crypto.* import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignedData
import net.corda.core.crypto.isFulfilledBy
import net.corda.core.identity.Party import net.corda.core.identity.Party
import net.corda.core.messaging.MessageRecipients import net.corda.core.messaging.MessageRecipients
import net.corda.core.messaging.SingleMessageRecipient import net.corda.core.messaging.SingleMessageRecipient

View File

@ -6,6 +6,8 @@ import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_CA
import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS import net.corda.core.crypto.X509Utilities.CORDA_CLIENT_TLS
import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA import net.corda.core.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.node.services.config.NodeConfiguration import net.corda.node.services.config.NodeConfiguration
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.path.CertPath
import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.util.io.pem.PemObject import org.bouncycastle.util.io.pem.PemObject
import java.io.StringWriter import java.io.StringWriter
@ -40,7 +42,8 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService:
val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair) val selfSignCert = X509Utilities.createSelfSignedCACertificate(config.myLegalName, keyPair)
// Save to the key store. // Save to the key store.
caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(), arrayOf(selfSignCert)) caKeyStore.addOrReplaceKey(SELF_SIGNED_PRIVATE_KEY, keyPair.private, privateKeyPassword.toCharArray(),
CertPath(arrayOf(selfSignCert)))
caKeyStore.save(config.nodeKeystore, keystorePassword) caKeyStore.save(config.nodeKeystore, keystorePassword)
} }
val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword) val keyPair = caKeyStore.getKeyPair(SELF_SIGNED_PRIVATE_KEY, privateKeyPassword)
@ -69,11 +72,13 @@ class NetworkRegistrationHelper(val config: NodeConfiguration, val certService:
println("Node private key and certificate stored in ${config.nodeKeystore}.") println("Node private key and certificate stored in ${config.nodeKeystore}.")
println("Generating SSL certificate for node messaging service.") println("Generating SSL certificate for node messaging service.")
val converter = JcaX509CertificateConverter()
val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val sslKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)
val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA) val caCert = caKeyStore.getX509Certificate(CORDA_CLIENT_CA)
val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, caCert.subject, sslKey.public) val sslCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, keyPair, caCert.subject, sslKey.public)
val sslKeyStore = KeyStoreUtilities.loadOrCreateKeyStore(config.sslKeystore, keystorePassword) val sslKeyStore = KeyStoreUtilities.loadOrCreateKeyStore(config.sslKeystore, keystorePassword)
sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(), arrayOf(sslCert, *certificates)) sslKeyStore.addOrReplaceKey(CORDA_CLIENT_TLS, sslKey.private, privateKeyPassword.toCharArray(),
arrayOf(converter.getCertificate(sslCert), *certificates))
sslKeyStore.save(config.sslKeystore, config.keyStorePassword) sslKeyStore.save(config.sslKeystore, config.keyStorePassword)
println("SSL private key and certificate stored in ${config.sslKeystore}.") println("SSL private key and certificate stored in ${config.sslKeystore}.")
// All done, clean up temp files. // All done, clean up temp files.

View File

@ -5,10 +5,12 @@ import com.nhaarman.mockito_kotlin.eq
import com.nhaarman.mockito_kotlin.mock import com.nhaarman.mockito_kotlin.mock
import net.corda.core.crypto.* import net.corda.core.crypto.*
import net.corda.core.exists import net.corda.core.exists
import net.corda.core.mapToArray
import net.corda.core.utilities.ALICE import net.corda.core.utilities.ALICE
import net.corda.testing.TestNodeConfiguration import net.corda.testing.TestNodeConfiguration
import net.corda.testing.getTestX509Name import net.corda.testing.getTestX509Name
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -29,8 +31,9 @@ class NetworkRegistrationHelperTest {
"CORDA_INTERMEDIATE_CA", "CORDA_INTERMEDIATE_CA",
"CORDA_ROOT_CA") "CORDA_ROOT_CA")
.map { getTestX509Name(it) } .map { getTestX509Name(it) }
val converter = JcaX509CertificateConverter()
val certs = identities.map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) } val certs = identities.map { X509Utilities.createSelfSignedCACertificate(it, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) }
.toTypedArray() .mapToArray(converter::getCertificate)
val certService: NetworkRegistrationService = mock { val certService: NetworkRegistrationService = mock {
on { submitRequest(any()) }.then { id } on { submitRequest(any()) }.then { id }

View File

@ -21,6 +21,7 @@ import net.corda.node.services.vault.NodeVaultService
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.corda.testing.MINI_CORP import net.corda.testing.MINI_CORP
import net.corda.testing.MOCK_VERSION_INFO import net.corda.testing.MOCK_VERSION_INFO
import org.bouncycastle.cert.X509CertificateHolder
import rx.Observable import rx.Observable
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
@ -32,7 +33,6 @@ import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.PublicKey import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.time.Clock import java.time.Clock
import java.util.* import java.util.*
import java.util.jar.JarInputStream import java.util.jar.JarInputStream
@ -91,7 +91,7 @@ class MockKeyManagementService(val identityService: IdentityService,
return k.public return k.public
} }
override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509Certificate, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled) override fun freshKeyAndCert(identity: Party, revocationEnabled: Boolean): Pair<X509CertificateHolder, CertPath> = freshKeyAndCert(this, identityService, identity, revocationEnabled)
private fun getSigningKeyPair(publicKey: PublicKey): KeyPair { private fun getSigningKeyPair(publicKey: PublicKey): KeyPair {
val pk = publicKey.keys.first { keyStore.containsKey(it) } val pk = publicKey.keys.first { keyStore.containsKey(it) }

View File

@ -3,6 +3,7 @@ package net.corda.testing.node
import com.codahale.metrics.MetricRegistry import com.codahale.metrics.MetricRegistry
import com.google.common.net.HostAndPort import com.google.common.net.HostAndPort
import com.google.common.util.concurrent.SettableFuture import com.google.common.util.concurrent.SettableFuture
import net.corda.core.crypto.CertificateAndKeyPair
import net.corda.core.crypto.commonName import net.corda.core.crypto.commonName
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.messaging.RPCOps import net.corda.core.messaging.RPCOps
@ -30,7 +31,9 @@ import kotlin.concurrent.thread
* This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or * This is a bare-bones node which can only send and receive messages. It doesn't register with a network map service or
* any other such task that would make it functional in a network and thus left to the user to do so manually. * any other such task that would make it functional in a network and thus left to the user to do so manually.
*/ */
class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(), rpcAddress: HostAndPort = freeLocalHostAndPort()) : AutoCloseable { class SimpleNode(val config: NodeConfiguration, val address: HostAndPort = freeLocalHostAndPort(),
rpcAddress: HostAndPort = freeLocalHostAndPort(),
networkRoot: CertificateAndKeyPair? = null) : AutoCloseable {
private val databaseWithCloseable: Pair<Closeable, Database> = configureDatabase(config.dataSourceProperties) private val databaseWithCloseable: Pair<Closeable, Database> = configureDatabase(config.dataSourceProperties)
val database: Database get() = databaseWithCloseable.second val database: Database get() = databaseWithCloseable.second