CORDA-2009 update to BC 1.60 (security fixes) (#3974)

* update to BC 1.60 (security fixes)

* adding key combination keystore/cert tests
This commit is contained in:
Konstantinos Chalkias 2018-09-20 13:11:32 +01:00 committed by GitHub
parent 6775ffa2e6
commit 38c85d1711
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 151 additions and 64 deletions

View File

@ -5,7 +5,7 @@ kotlinVersion=1.2.51
platformVersion=4 platformVersion=4
guavaVersion=25.1-jre guavaVersion=25.1-jre
proguardVersion=6.0.3 proguardVersion=6.0.3
bouncycastleVersion=1.57 bouncycastleVersion=1.60
typesafeConfigVersion=1.3.1 typesafeConfigVersion=1.3.1
jsr305Version=3.0.2 jsr305Version=3.0.2
artifactoryPluginVersion=4.7.3 artifactoryPluginVersion=4.7.3

View File

@ -1,9 +1,13 @@
package net.corda.nodeapi.internal.crypto package net.corda.nodeapi.internal.crypto
import net.corda.core.crypto.Crypto import net.corda.core.crypto.*
import net.corda.core.crypto.Crypto.COMPOSITE_KEY
import net.corda.core.crypto.Crypto.ECDSA_SECP256K1_SHA256
import net.corda.core.crypto.Crypto.ECDSA_SECP256R1_SHA256
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512 import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.RSA_SHA256
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
import net.corda.core.crypto.Crypto.generateKeyPair import net.corda.core.crypto.Crypto.generateKeyPair
import net.corda.core.crypto.newSecureRandom
import net.corda.core.identity.CordaX500Name import net.corda.core.identity.CordaX500Name
import net.corda.core.internal.div import net.corda.core.internal.div
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
@ -12,6 +16,8 @@ import net.corda.core.serialization.serialize
import net.corda.node.serialization.amqp.AMQPServerSerializationScheme import net.corda.node.serialization.amqp.AMQPServerSerializationScheme
import net.corda.nodeapi.internal.config.MutualSslConfiguration import net.corda.nodeapi.internal.config.MutualSslConfiguration
import net.corda.nodeapi.internal.createDevNodeCa import net.corda.nodeapi.internal.createDevNodeCa
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_IDENTITY_SIGNATURE_SCHEME
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME
import net.corda.nodeapi.internal.protonwrapper.netty.init import net.corda.nodeapi.internal.protonwrapper.netty.init
import net.corda.nodeapi.internal.registerDevP2pCertificates import net.corda.nodeapi.internal.registerDevP2pCertificates
import net.corda.nodeapi.internal.registerDevSigningCertificates import net.corda.nodeapi.internal.registerDevSigningCertificates
@ -24,17 +30,24 @@ import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.TestIdentity import net.corda.testing.core.TestIdentity
import net.corda.testing.internal.stubs.CertificateStoreStubs import net.corda.testing.internal.stubs.CertificateStoreStubs
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.*
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
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 sun.security.rsa.RSAPrivateCrtKeyImpl
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.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.Key
import java.security.KeyPair
import java.security.PrivateKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
@ -53,6 +66,28 @@ class X509UtilitiesTest {
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
"TLS_DHE_RSA_WITH_AES_128_GCM_SHA256" "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256"
) )
// We ensure that all of the algorithms are both used (at least once) as first and second in the following [Pair]s.
// We also add [DEFAULT_TLS_SIGNATURE_SCHEME] and [DEFAULT_IDENTITY_SIGNATURE_SCHEME] combinations for consistency.
val certChainSchemeCombinations = listOf(
Pair(DEFAULT_TLS_SIGNATURE_SCHEME, DEFAULT_TLS_SIGNATURE_SCHEME),
Pair(DEFAULT_IDENTITY_SIGNATURE_SCHEME, DEFAULT_IDENTITY_SIGNATURE_SCHEME),
Pair(DEFAULT_TLS_SIGNATURE_SCHEME, DEFAULT_IDENTITY_SIGNATURE_SCHEME),
Pair(ECDSA_SECP256R1_SHA256, SPHINCS256_SHA256),
Pair(ECDSA_SECP256K1_SHA256, RSA_SHA256),
Pair(EDDSA_ED25519_SHA512, ECDSA_SECP256K1_SHA256),
Pair(RSA_SHA256, EDDSA_ED25519_SHA512),
Pair(SPHINCS256_SHA256, ECDSA_SECP256R1_SHA256)
)
val schemeToKeyTypes = listOf(
// By default, JKS returns SUN EC key.
Triple(ECDSA_SECP256R1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java),
Triple(ECDSA_SECP256K1_SHA256,java.security.interfaces.ECPrivateKey::class.java, org.bouncycastle.jce.interfaces.ECPrivateKey::class.java),
Triple(EDDSA_ED25519_SHA512, EdDSAPrivateKey::class.java, EdDSAPrivateKey::class.java),
// By default, JKS returns SUN RSA key.
Triple(RSA_SHA256, RSAPrivateCrtKeyImpl::class.java, BCRSAPrivateCrtKey::class.java),
Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java)
)
} }
@Rule @Rule
@ -61,7 +96,11 @@ class X509UtilitiesTest {
@Test @Test
fun `create valid self-signed CA certificate`() { fun `create valid self-signed CA certificate`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { validSelfSignedCertificate(it) }
}
private fun validSelfSignedCertificate(signatureScheme: SignatureScheme) {
val caKey = generateKeyPair(signatureScheme)
val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB") val subject = X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB")
val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey) val caCert = X509Utilities.createSelfSignedCACertificate(subject, caKey)
assertEquals(subject, caCert.subjectX500Principal) // using our subject common name assertEquals(subject, caCert.subjectX500Principal) // using our subject common name
@ -78,8 +117,12 @@ class X509UtilitiesTest {
@Test @Test
fun `load and save a PEM file certificate`() { fun `load and save a PEM file certificate`() {
Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { loadSavePEMCert(it) }
}
private fun loadSavePEMCert(signatureScheme: SignatureScheme) {
val tmpCertificateFile = tempFile("cacert.pem") val tmpCertificateFile = tempFile("cacert.pem")
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val caKey = generateKeyPair(signatureScheme)
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey) val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test Cert,O=R3 Ltd,L=London,C=GB"), caKey)
X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile) X509Utilities.saveCertificateAsPEMFile(caCert, tmpCertificateFile)
val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile) val readCertificate = X509Utilities.loadCertificateFromPEMFile(tmpCertificateFile)
@ -88,29 +131,52 @@ class X509UtilitiesTest {
@Test @Test
fun `create valid server certificate chain`() { fun `create valid server certificate chain`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) certChainSchemeCombinations.forEach { createValidServerCertChain(it.first, it.second) }
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey) }
val subject = X500Principal("CN=Server Cert,O=R3 Ltd,L=London,C=GB")
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) private fun createValidServerCertChain(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) {
val serverCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKey, subject, keyPair.public) val (caKeyPair, caCert, _, childCert, _, childSubject)
assertEquals(subject, serverCert.subjectX500Principal) // using our subject common name = genCaAndChildKeysCertsAndSubjects(signatureSchemeRoot, signatureSchemeChild)
assertEquals(caCert.issuerX500Principal, serverCert.issuerX500Principal) // Issued by our CA cert assertEquals(childSubject, childCert.subjectX500Principal) // Using our subject common name.
serverCert.checkValidity(Date()) // throws on verification problems assertEquals(caCert.issuerX500Principal, childCert.issuerX500Principal) // Issued by our CA cert.
serverCert.verify(caKey.public) // throws on verification problems childCert.checkValidity(Date()) // Throws on verification problems.
serverCert.toBc().run { childCert.verify(caKeyPair.public) // Throws on verification problems.
childCert.toBc().run {
val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue) val basicConstraints = BasicConstraints.getInstance(getExtension(Extension.basicConstraints).parsedValue)
val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue) val keyUsage = KeyUsage.getInstance(getExtension(Extension.keyUsage).parsedValue)
assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property) assertFalse { keyUsage.hasUsages(5) } // Bit 5 == keyCertSign according to ASN.1 spec (see full comment on KeyUsage property).
assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate assertNull(basicConstraints.pathLenConstraint) // Non-CA certificate.
} }
} }
private data class CaAndChildKeysCertsAndSubjects(val caKeyPair: KeyPair,
val caCert: X509Certificate,
val childKeyPair: KeyPair,
val childCert: X509Certificate,
val caSubject: X500Principal,
val childSubject: X500Principal)
private fun genCaAndChildKeysCertsAndSubjects(signatureSchemeRoot: SignatureScheme,
signatureSchemeChild: SignatureScheme,
rootSubject: X500Principal = X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"),
childSubject: X500Principal = X500Principal("CN=Test Child Cert,O=R3 Ltd,L=London,C=GB")): CaAndChildKeysCertsAndSubjects {
val caKeyPair = generateKeyPair(signatureSchemeRoot)
val caCert = X509Utilities.createSelfSignedCACertificate(rootSubject, caKeyPair)
val childKeyPair = generateKeyPair(signatureSchemeChild)
val childCert = X509Utilities.createCertificate(CertificateType.TLS, caCert, caKeyPair, childSubject, childKeyPair.public)
return CaAndChildKeysCertsAndSubjects(caKeyPair, caCert, childKeyPair, childCert, rootSubject, childSubject)
}
@Test @Test
fun `create valid server certificate chain includes CRL info`() { fun `create valid server certificate chain includes CRL info`() {
val caKey = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) certChainSchemeCombinations.forEach { createValidServerCertIncludeCRL(it.first, it.second) }
}
private fun createValidServerCertIncludeCRL(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) {
val caKey = generateKeyPair(signatureSchemeRoot)
val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey) val caCert = X509Utilities.createSelfSignedCACertificate(X500Principal("CN=Test CA Cert,O=R3 Ltd,L=London,C=GB"), caKey)
val caSubjectKeyIdentifier = SubjectKeyIdentifier.getInstance(caCert.toBc().getExtension(Extension.subjectKeyIdentifier).parsedValue) val caSubjectKeyIdentifier = SubjectKeyIdentifier.getInstance(caCert.toBc().getExtension(Extension.subjectKeyIdentifier).parsedValue)
val keyPair = generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val keyPair = generateKeyPair(signatureSchemeChild)
val crlDistPoint = "http://test.com" val crlDistPoint = "http://test.com"
val serverCert = X509Utilities.createCertificate( val serverCert = X509Utilities.createCertificate(
CertificateType.TLS, CertificateType.TLS,
@ -128,57 +194,33 @@ class X509UtilitiesTest {
} }
@Test @Test
fun `storing EdDSA key in java keystore`() { fun `storing all supported key types in java keystore`() {
Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { storeKeyToKeystore(it) }
}
private fun storeKeyToKeystore(signatureScheme: SignatureScheme) {
val tmpKeyStore = tempFile("keystore.jks") val tmpKeyStore = tempFile("keystore.jks")
val keyPair = generateKeyPair(EDDSA_ED25519_SHA512) val keyPair = generateKeyPair(signatureScheme)
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded)) assertTrue(Arrays.equals(selfSignCert.publicKey.encoded, keyPair.public.encoded))
// Save the EdDSA private key with self sign cert in the keystore. // Save the private key with self sign cert in the keystore.
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert)) keyStore.setKeyEntry("Key", keyPair.private, "password".toCharArray(), arrayOf(selfSignCert))
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.
val keyStore2 = loadOrCreateKeyStore(tmpKeyStore, "keystorepass") val reloadedKeystore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
val privateKey = keyStore2.getKey("Key", "password".toCharArray()) val reloadedPrivateKey = reloadedKeystore.getKey("Key", "password".toCharArray())
val pubKey = keyStore2.getCertificate("Key").publicKey val reloadedPublicKey = reloadedKeystore.getCertificate("Key").publicKey
assertNotNull(pubKey) assertNotNull(reloadedPublicKey)
assertNotNull(privateKey) assertNotNull(reloadedPrivateKey)
assertEquals(keyPair.public, pubKey) assertEquals(keyPair.public, reloadedPublicKey)
assertEquals(keyPair.private, privateKey) assertEquals(keyPair.private, reloadedPrivateKey)
}
@Test
fun `signing EdDSA key with EcDSA certificate`() {
val tmpKeyStore = tempFile("keystore.jks")
val ecDSAKey = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val ecDSACert = X509Utilities.createSelfSignedCACertificate(testName, ecDSAKey)
val edDSAKeypair = generateKeyPair(EDDSA_ED25519_SHA512)
val edDSACert = X509Utilities.createCertificate(CertificateType.TLS, ecDSACert, ecDSAKey, BOB.name.x500Principal, edDSAKeypair.public)
// Save the EdDSA private key with cert chains.
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", edDSAKeypair.private, "password".toCharArray(), arrayOf(ecDSACert, edDSACert))
keyStore.save(tmpKeyStore, "keystorepass")
// Load the keystore from file and make sure keys are intact.
val keyStore2 = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
val privateKey = keyStore2.getKey("Key", "password".toCharArray())
val certs = keyStore2.getCertificateChain("Key")
val pubKey = certs.last().publicKey
assertEquals(2, certs.size)
assertNotNull(pubKey)
assertNotNull(privateKey)
assertEquals(edDSAKeypair.public, pubKey)
assertEquals(edDSAKeypair.private, privateKey)
} }
@Test @Test
@ -316,7 +358,17 @@ class X509UtilitiesTest {
@Test @Test
fun `get correct private key type from Keystore`() { fun `get correct private key type from Keystore`() {
val keyPair = generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256) schemeToKeyTypes.forEach { getCorrectKeyFromKeystore(it.first, it.second, it.third) }
}
private fun <U, C> getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, uncastedClass: Class<U>, castedClass: Class<C>) {
val keyPair = generateKeyPair(signatureScheme)
val (keyFromKeystore, keyFromKeystoreCasted) = storeAndGetKeysFromKeystore(keyPair)
assertThat(keyFromKeystore).isInstanceOf(uncastedClass)
assertThat(keyFromKeystoreCasted).isInstanceOf(castedClass)
}
private fun storeAndGetKeysFromKeystore(keyPair: KeyPair): Pair<Key, PrivateKey> {
val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB") val testName = X500Principal("CN=Test,O=R3 Ltd,L=London,C=GB")
val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair) val selfSignCert = X509Utilities.createSelfSignedCACertificate(testName, keyPair)
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
@ -324,13 +376,15 @@ class X509UtilitiesTest {
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")
return Pair(keyFromKeystore, keyFromKeystoreCasted)
assertTrue(keyFromKeystore is java.security.interfaces.ECPrivateKey) // by default JKS returns SUN EC key
assertTrue(keyFromKeystoreCasted is org.bouncycastle.jce.interfaces.ECPrivateKey)
} }
@Test @Test
fun `serialize - deserialize X509Certififcate`() { fun `serialize - deserialize X509Certificate`() {
Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { serializeDeserializeX509Cert(it) }
}
private fun serializeDeserializeX509Cert(signatureScheme: SignatureScheme) {
val factory = SerializationFactoryImpl().apply { registerScheme(AMQPServerSerializationScheme()) } val factory = SerializationFactoryImpl().apply { registerScheme(AMQPServerSerializationScheme()) }
val context = SerializationContextImpl(amqpMagic, val context = SerializationContextImpl(amqpMagic,
javaClass.classLoader, javaClass.classLoader,
@ -339,7 +393,7 @@ class X509UtilitiesTest {
true, true,
SerializationContext.UseCase.P2P, SerializationContext.UseCase.P2P,
null) null)
val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)) val expected = X509Utilities.createSelfSignedCACertificate(ALICE.name.x500Principal, generateKeyPair(signatureScheme))
val serialized = expected.serialize(factory, context).bytes val serialized = expected.serialize(factory, context).bytes
val actual = serialized.deserialize<X509Certificate>(factory, context) val actual = serialized.deserialize<X509Certificate>(factory, context)
assertEquals(expected, actual) assertEquals(expected, actual)
@ -347,6 +401,10 @@ class X509UtilitiesTest {
@Test @Test
fun `serialize - deserialize X509CertPath`() { fun `serialize - deserialize X509CertPath`() {
Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { serializeDeserializeX509CertPath(it) }
}
private fun serializeDeserializeX509CertPath(signatureScheme: SignatureScheme) {
val factory = SerializationFactoryImpl().apply { registerScheme(AMQPServerSerializationScheme()) } val factory = SerializationFactoryImpl().apply { registerScheme(AMQPServerSerializationScheme()) }
val context = SerializationContextImpl( val context = SerializationContextImpl(
amqpMagic, amqpMagic,
@ -357,7 +415,7 @@ class X509UtilitiesTest {
SerializationContext.UseCase.P2P, SerializationContext.UseCase.P2P,
null null
) )
val rootCAKey = Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME) val rootCAKey = generateKeyPair(signatureScheme)
val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey) val rootCACert = X509Utilities.createSelfSignedCACertificate(ALICE_NAME.x500Principal, rootCAKey)
val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey) val certificate = X509Utilities.createCertificate(CertificateType.TLS, rootCACert, rootCAKey, BOB_NAME.x500Principal, BOB.publicKey)
val expected = X509Utilities.buildCertPath(certificate, rootCACert) val expected = X509Utilities.buildCertPath(certificate, rootCACert)
@ -365,4 +423,33 @@ class X509UtilitiesTest {
val actual: CertPath = serialized.deserialize(factory, context) val actual: CertPath = serialized.deserialize(factory, context)
assertEquals(expected, actual) assertEquals(expected, actual)
} }
@Test
fun `signing a key type with another key type certificate then store and reload correctly from keystore`() {
certChainSchemeCombinations.forEach { signCertWithOtherKeyTypeAndTestKeystoreReload(it.first, it.second) }
}
private fun signCertWithOtherKeyTypeAndTestKeystoreReload(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) {
val tmpKeyStore = tempFile("keystore.jks")
val (_, caCert, childKeyPair, childCert) = genCaAndChildKeysCertsAndSubjects(signatureSchemeRoot, signatureSchemeChild)
// Save the child private key with cert chains.
val keyStore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
keyStore.setKeyEntry("Key", childKeyPair.private, "password".toCharArray(), arrayOf(caCert, childCert))
keyStore.save(tmpKeyStore, "keystorepass")
// Load the keystore from file and make sure keys are intact.
val reloadedKeystore = loadOrCreateKeyStore(tmpKeyStore, "keystorepass")
val reloadedPrivateKey = reloadedKeystore.getKey("Key", "password".toCharArray())
val reloadedCerts = reloadedKeystore.getCertificateChain("Key")
val reloadedPublicKey = reloadedCerts.last().publicKey
assertEquals(2, reloadedCerts.size)
assertNotNull(reloadedPublicKey)
assertNotNull(reloadedPrivateKey)
assertEquals(childKeyPair.public, reloadedPublicKey)
assertEquals(childKeyPair.private, reloadedPrivateKey)
}
} }