mirror of
https://github.com/corda/corda.git
synced 2025-05-31 14:40:52 +00:00
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:
parent
0fd897527a
commit
c003ec0042
@ -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.
|
||||||
|
@ -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()
|
||||||
|
@ -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.
|
||||||
|
@ -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),
|
||||||
|
@ -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))
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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" }
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -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 {
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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 }
|
||||||
|
@ -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) }
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user