mirror of
https://github.com/corda/corda.git
synced 2025-01-18 10:46:38 +00:00
ENT-5888: Resurrect node_hash_to_key (#6776)
This commit is contained in:
parent
551b3f0811
commit
401d8b8856
@ -30,6 +30,7 @@ class MigrationNamedCacheFactory(private val metricRegistry: MetricRegistry?,
|
||||
"PersistentIdentityService_keyToPartyAndCert" -> caffeine.maximumSize(defaultCacheSize)
|
||||
"PersistentIdentityService_nameToParty" -> caffeine.maximumSize(defaultCacheSize)
|
||||
"PersistentIdentityService_keyToParty" -> caffeine.maximumSize(defaultCacheSize)
|
||||
"PersistentIdentityService_hashToKey" -> caffeine.maximumSize(defaultCacheSize)
|
||||
"PersistentNetworkMap_nodesByKey" -> caffeine.maximumSize(defaultCacheSize)
|
||||
"PersistentNetworkMap_idByLegalName" -> caffeine.maximumSize(defaultCacheSize)
|
||||
"BasicHSMKeyManagementService_keys" -> caffeine.maximumSize(defaultCacheSize)
|
||||
|
@ -1,102 +0,0 @@
|
||||
package net.corda.node.migration
|
||||
|
||||
import liquibase.change.custom.CustomTaskChange
|
||||
import liquibase.database.Database
|
||||
import liquibase.database.jvm.JdbcConnection
|
||||
import liquibase.exception.ValidationErrors
|
||||
import liquibase.resource.ResourceAccessor
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.core.utilities.contextLogger
|
||||
import net.corda.nodeapi.internal.crypto.X509CertificateFactory
|
||||
import java.sql.ResultSet
|
||||
|
||||
class NodeIdentitiesNoCertMigration : CustomTaskChange {
|
||||
companion object {
|
||||
private val logger = contextLogger()
|
||||
|
||||
const val UNRESOLVED = "Unresolved"
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun execute(database: Database) {
|
||||
val connection = database.connection as JdbcConnection
|
||||
|
||||
logger.info("Preparing to migrate node_identities_no_cert.")
|
||||
|
||||
val nodeKeysByHash = mutableMapOf<String, ByteArray>()
|
||||
connection.queryAll("SELECT pk_hash, identity_value FROM node_identities") { resultSet ->
|
||||
val hash = resultSet.getString(1)
|
||||
val certificateBytes = resultSet.getBytes(2)
|
||||
nodeKeysByHash[hash] = certificateBytes.toKeyBytes()
|
||||
}
|
||||
|
||||
val nodeKeyHashesByName = mutableMapOf<String, String>()
|
||||
connection.queryAll("SELECT name, pk_hash FROM node_named_identities") { resultSet ->
|
||||
val name = resultSet.getString(1)
|
||||
val hash = resultSet.getString(2)
|
||||
nodeKeyHashesByName[name] = hash
|
||||
}
|
||||
|
||||
logger.info("Starting to migrate node_identities_no_cert.")
|
||||
|
||||
var count = 0
|
||||
connection.queryAll("SELECT pk_hash, name FROM node_identities_no_cert") { resultSet ->
|
||||
val hash = resultSet.getString(1)
|
||||
val name = resultSet.getString(2)
|
||||
|
||||
val partyKeyHash = nodeKeysByHash[hash]?.let { hash }
|
||||
?: nodeKeyHashesByName[name]
|
||||
?: UNRESOLVED.also { logger.warn("Unable to find party key hash for [$name] [$hash]") }
|
||||
|
||||
val key = nodeKeysByHash[hash]
|
||||
?: connection.query("SELECT public_key FROM v_our_key_pairs WHERE public_key_hash = ?", hash)
|
||||
?: connection.query("SELECT public_key FROM node_hash_to_key WHERE pk_hash = ?", hash)
|
||||
?: UNRESOLVED.toByteArray().also { logger.warn("Unable to find key for [$name] [$hash]") }
|
||||
|
||||
connection.prepareStatement("UPDATE node_identities_no_cert SET party_pk_hash = ?, public_key = ? WHERE pk_hash = ?").use {
|
||||
it.setString(1, partyKeyHash)
|
||||
it.setBytes(2, key)
|
||||
it.setString(3, hash)
|
||||
it.executeUpdate()
|
||||
}
|
||||
count++
|
||||
}
|
||||
|
||||
logger.info("Migrated $count node_identities_no_cert entries.")
|
||||
}
|
||||
|
||||
private fun JdbcConnection.queryAll(statement: String, action: (ResultSet) -> Unit) = createStatement().use {
|
||||
it.executeQuery(statement).use { resultSet ->
|
||||
while (resultSet.next()) {
|
||||
action.invoke(resultSet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun JdbcConnection.query(statement: String, key: String): ByteArray? = prepareStatement(statement).use {
|
||||
it.setString(1, key)
|
||||
it.executeQuery().use { resultSet ->
|
||||
if (resultSet.next()) resultSet.getBytes(1) else null
|
||||
}
|
||||
}
|
||||
|
||||
private fun ByteArray.toKeyBytes(): ByteArray {
|
||||
val certPath = X509CertificateFactory().delegate.generateCertPath(inputStream())
|
||||
val partyAndCertificate = PartyAndCertificate(certPath)
|
||||
return partyAndCertificate.party.owningKey.encoded
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
}
|
||||
|
||||
override fun setFileOpener(resourceAccessor: ResourceAccessor?) {
|
||||
}
|
||||
|
||||
override fun getConfirmationMessage(): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun validate(database: Database?): ValidationErrors? {
|
||||
return null
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@ import net.corda.core.utilities.contextLogger
|
||||
import net.corda.core.utilities.debug
|
||||
import net.corda.node.internal.schemas.NodeInfoSchemaV1
|
||||
import net.corda.node.services.api.IdentityServiceInternal
|
||||
import net.corda.node.services.keys.BasicHSMKeyManagementService
|
||||
import net.corda.node.services.network.NotaryUpdateListener
|
||||
import net.corda.node.services.persistence.PublicKeyHashToExternalId
|
||||
import net.corda.node.services.persistence.WritablePublicKeyToOwningIdentityCache
|
||||
@ -81,15 +82,13 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
}
|
||||
|
||||
/**
|
||||
* Link anonymous public key to well known party (substituting well-known party public key with its hash).
|
||||
* Public key for well-known party is linked to itself.
|
||||
* [Party] with owning key replaced by its hash.
|
||||
*/
|
||||
private data class KeyToParty(val publicKey: PublicKey, val name: CordaX500Name, val partyPublicKeyHash: String) {
|
||||
constructor(party: Party, publicKey: PublicKey = party.owningKey) : this(publicKey, party.name, party.owningKey.toStringShort())
|
||||
val party get() = Party(name, publicKey)
|
||||
private data class PartyWithHash(val name: CordaX500Name, val owningKeyHash: String) {
|
||||
constructor(party: Party) : this(party.name, party.owningKey.toStringShort())
|
||||
}
|
||||
|
||||
private fun createKeyToPartyMap(cacheFactory: NamedCacheFactory): AppendOnlyPersistentMap<String, KeyToParty,
|
||||
private fun createKeyToPartyMap(cacheFactory: NamedCacheFactory): AppendOnlyPersistentMap<String, PartyWithHash,
|
||||
PersistentPublicKeyHashToParty, String> {
|
||||
return AppendOnlyPersistentMap(
|
||||
cacheFactory = cacheFactory,
|
||||
@ -98,11 +97,11 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
fromPersistentEntity = {
|
||||
Pair(
|
||||
it.publicKeyHash,
|
||||
KeyToParty(Crypto.decodePublicKey(it.publicKey), CordaX500Name.parse(it.name), it.partyPublicKeyHash)
|
||||
PartyWithHash(CordaX500Name.parse(it.name), it.owningKeyHash)
|
||||
)
|
||||
},
|
||||
toPersistentEntity = { key: String, value: KeyToParty ->
|
||||
PersistentPublicKeyHashToParty(key, value.name.toString(), value.partyPublicKeyHash, value.publicKey.encoded)
|
||||
toPersistentEntity = { key: String, value: PartyWithHash ->
|
||||
PersistentPublicKeyHashToParty(key, value.name.toString(), value.owningKeyHash)
|
||||
},
|
||||
persistentEntityClass = PersistentPublicKeyHashToParty::class.java)
|
||||
}
|
||||
@ -118,6 +117,24 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
)
|
||||
}
|
||||
|
||||
fun createHashToKeyMap(cacheFactory: NamedCacheFactory): AppendOnlyPersistentMap<String, PublicKey, PersistentHashToPublicKey,
|
||||
String> {
|
||||
return AppendOnlyPersistentMap(
|
||||
cacheFactory = cacheFactory,
|
||||
name = "PersistentIdentityService_hashToKey",
|
||||
toPersistentEntityKey = { it },
|
||||
fromPersistentEntity = {
|
||||
Pair(
|
||||
it.publicKeyHash,
|
||||
Crypto.decodePublicKey(it.publicKey)
|
||||
)
|
||||
},
|
||||
toPersistentEntity = { key: String, value: PublicKey ->
|
||||
PersistentHashToPublicKey(key, value.encoded)
|
||||
},
|
||||
persistentEntityClass = PersistentHashToPublicKey::class.java)
|
||||
}
|
||||
|
||||
private fun mapToKey(party: PartyAndCertificate) = party.owningKey.toStringShort()
|
||||
}
|
||||
|
||||
@ -136,7 +153,6 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
@Entity
|
||||
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}identities_no_cert")
|
||||
class PersistentPublicKeyHashToParty(
|
||||
@Suppress("Unused")
|
||||
@Id
|
||||
@Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = false)
|
||||
var publicKeyHash: String = "",
|
||||
@ -144,8 +160,16 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
@Column(name = "name", length = 128, nullable = false)
|
||||
var name: String = "",
|
||||
|
||||
@Column(name = "party_pk_hash", length = MAX_HASH_HEX_SIZE, nullable = false)
|
||||
var partyPublicKeyHash: String = "",
|
||||
@Column(name = "owning_pk_hash", length = MAX_HASH_HEX_SIZE, nullable = false)
|
||||
var owningKeyHash: String = ""
|
||||
)
|
||||
|
||||
@Entity
|
||||
@javax.persistence.Table(name = "${NODE_DATABASE_PREFIX}hash_to_key")
|
||||
class PersistentHashToPublicKey(
|
||||
@Id
|
||||
@Column(name = "pk_hash", length = MAX_HASH_HEX_SIZE, nullable = false)
|
||||
var publicKeyHash: String = "",
|
||||
|
||||
@Type(type = "corda-blob")
|
||||
@Column(name = "public_key", nullable = false)
|
||||
@ -175,6 +199,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
private val keyToPartyAndCert = createKeyToPartyAndCertMap(cacheFactory)
|
||||
private val keyToParty = createKeyToPartyMap(cacheFactory)
|
||||
private val nameToParty = createNameToPartyMap(cacheFactory)
|
||||
private val hashToKey = createHashToKeyMap(cacheFactory)
|
||||
|
||||
fun start(
|
||||
trustRoot: X509Certificate,
|
||||
@ -196,7 +221,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
identities.forEach {
|
||||
val key = mapToKey(it)
|
||||
keyToPartyAndCert.addWithDuplicatesAllowed(key, it, false)
|
||||
keyToParty.addWithDuplicatesAllowed(it.owningKey.toStringShort(), KeyToParty(it.party), false)
|
||||
keyToParty.addWithDuplicatesAllowed(it.owningKey.toStringShort(), PartyWithHash(it.party), false)
|
||||
nameToParty.asMap()[it.name] = Optional.of(it.party)
|
||||
}
|
||||
log.debug("Identities loaded")
|
||||
@ -250,7 +275,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
// keyToParty is already registered via KMS freshKeyInternal()
|
||||
} else {
|
||||
keyToPartyAndCert.addWithDuplicatesAllowed(key, identity, false)
|
||||
keyToParty.addWithDuplicatesAllowed(identity.owningKey.toStringShort(), KeyToParty(identity.party), false)
|
||||
keyToParty.addWithDuplicatesAllowed(identity.owningKey.toStringShort(), PartyWithHash(identity.party), false)
|
||||
}
|
||||
val parentId = identityCertChain[1].publicKey.toStringShort()
|
||||
keyToPartyAndCert[parentId]
|
||||
@ -265,16 +290,11 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
keyToPartyAndCert[owningKey.toStringShort()]
|
||||
}
|
||||
|
||||
override fun partyFromKey(key: PublicKey): Party? = database.transaction {
|
||||
keyToParty[key.toStringShort()]?.let {
|
||||
if (it.partyPublicKeyHash == key.toStringShort()) {
|
||||
// Well-known party is linked to itself.
|
||||
it.party
|
||||
} else {
|
||||
// Anonymous party is linked to well-known party.
|
||||
keyToParty[it.partyPublicKeyHash]?.party
|
||||
}
|
||||
}
|
||||
override fun partyFromKey(key: PublicKey): Party? {
|
||||
return certificateFromKey(key)?.takeIf { CertRole.extract(it.certificate)?.isWellKnown == false }?.party ?:
|
||||
database.transaction {
|
||||
keyToParty[key.toStringShort()]
|
||||
}?.let { wellKnownPartyFromX500Name(it.name) }
|
||||
}
|
||||
|
||||
// We give the caller a copy of the data set to avoid any locking problems
|
||||
@ -333,20 +353,21 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
|
||||
override fun registerKey(publicKey: PublicKey, party: Party, externalId: UUID?) {
|
||||
return database.transaction {
|
||||
val publicKeyHash = publicKey.toStringShort()
|
||||
// EVERY key should be mapped to a Party in the "keyToName" table. Therefore if there is already a record in that table for the
|
||||
// specified key then it's either our key which has been stored prior or another node's key which we have previously mapped.
|
||||
val existingEntryForKey = keyToParty[publicKey.toStringShort()]
|
||||
val existingEntryForKey = keyToParty[publicKeyHash]
|
||||
if (existingEntryForKey == null) {
|
||||
// Update the three tables as necessary. We definitely store the public key and map it to a party and we optionally update
|
||||
// the public key to external ID mapping table. This block will only ever be reached when registering keys generated on
|
||||
// other because when a node generates its own keys "registerKeyToParty" is automatically called by
|
||||
// KeyManagementService.freshKey.
|
||||
registerKeyToParty(publicKey, party)
|
||||
hashToKey[publicKeyHash] = publicKey
|
||||
if (externalId != null) {
|
||||
registerKeyToExternalId(publicKey, externalId)
|
||||
}
|
||||
} else {
|
||||
val publicKeyHash = publicKey.toStringShort()
|
||||
log.info("An existing entry for $publicKeyHash already exists.")
|
||||
if (party.name != existingEntryForKey.name) {
|
||||
throw IllegalStateException("The public publicKey $publicKeyHash is already assigned to a different party than the " +
|
||||
@ -360,7 +381,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
fun registerKeyToParty(publicKey: PublicKey, party: Party = ourParty) {
|
||||
return database.transaction {
|
||||
log.info("Linking: ${publicKey.hash} to ${party.name}")
|
||||
keyToParty[publicKey.toStringShort()] = KeyToParty(party, publicKey)
|
||||
keyToParty[publicKey.toStringShort()] = PartyWithHash(party)
|
||||
if (party == ourParty) {
|
||||
_pkToIdCache[publicKey] = KeyOwningIdentity.UnmappedIdentity
|
||||
}
|
||||
@ -376,12 +397,12 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
return _pkToIdCache[publicKey].uuid
|
||||
}
|
||||
|
||||
override fun publicKeysForExternalId(externalId: UUID): Iterable<PublicKey> {
|
||||
private fun publicKeysForExternalId(externalId: UUID, table: Class<*>): List<PublicKey> {
|
||||
return database.transaction {
|
||||
val query = session.createQuery(
|
||||
"""
|
||||
select a.publicKey
|
||||
from ${PersistentPublicKeyHashToParty::class.java.name} a, ${PublicKeyHashToExternalId::class.java.name} b
|
||||
from ${table.name} a, ${PublicKeyHashToExternalId::class.java.name} b
|
||||
where b.externalId = :uuid
|
||||
and b.publicKeyHash = a.publicKeyHash
|
||||
""",
|
||||
@ -392,6 +413,16 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
|
||||
}
|
||||
}
|
||||
|
||||
override fun publicKeysForExternalId(externalId: UUID): Iterable<PublicKey> {
|
||||
// If the externalId was created by this node then we'll find the keys in the KMS, otherwise they'll be in the IdentityService.
|
||||
val keys = publicKeysForExternalId(externalId, BasicHSMKeyManagementService.PersistentKey::class.java)
|
||||
return if (keys.isEmpty()) {
|
||||
publicKeysForExternalId(externalId, PersistentHashToPublicKey::class.java)
|
||||
} else {
|
||||
keys
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNewNotaryList(notaries: List<NotaryInfo>) {
|
||||
notaryIdentityCache = HashSet(notaries.map { it.identity })
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ class NodeSchemaService(private val extraSchemas: Set<MappedSchema> = emptySet()
|
||||
P2PMessageDeduplicator.ProcessedMessage::class.java,
|
||||
PersistentIdentityService.PersistentPublicKeyHashToCertificate::class.java,
|
||||
PersistentIdentityService.PersistentPublicKeyHashToParty::class.java,
|
||||
PersistentIdentityService.PersistentHashToPublicKey::class.java,
|
||||
ContractUpgradeServiceImpl.DBContractUpgrade::class.java,
|
||||
DBNetworkParametersStorage.PersistentNetworkParameters::class.java,
|
||||
PublicKeyHashToExternalId::class.java
|
||||
|
@ -48,6 +48,7 @@ open class DefaultNamedCacheFactory protected constructor(private val metricRegi
|
||||
name == "PersistentIdentityService_keyToPartyAndCert" -> caffeine.maximumSize(defaultCacheSize)
|
||||
name == "PersistentIdentityService_nameToParty" -> caffeine.maximumSize(defaultCacheSize)
|
||||
name == "PersistentIdentityService_keyToParty" -> caffeine.maximumSize(defaultCacheSize)
|
||||
name == "PersistentIdentityService_hashToKey" -> caffeine.maximumSize(defaultCacheSize)
|
||||
name == "PersistentNetworkMap_nodesByKey" -> caffeine.maximumSize(defaultCacheSize)
|
||||
name == "PersistentNetworkMap_idByLegalName" -> caffeine.maximumSize(defaultCacheSize)
|
||||
name == "PersistentKeyManagementService_keys" -> caffeine.maximumSize(defaultCacheSize)
|
||||
|
@ -4,51 +4,18 @@
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.5.xsd"
|
||||
logicalFilePath="migration/node-services.changelog-init.xml">
|
||||
|
||||
<changeSet author="R3.Corda" id="identity_service_key_rotation-start" dbms="!postgresql">
|
||||
<changeSet author="R3.Corda" id="identity_service_key_rotation">
|
||||
<addColumn tableName="node_identities_no_cert">
|
||||
<column name="party_pk_hash" type="NVARCHAR(130)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="public_key" type="blob">
|
||||
<column name="owning_pk_hash" type="NVARCHAR(130)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<createView viewName="v_our_key_pairs">
|
||||
select public_key_hash, public_key from node_our_key_pairs
|
||||
</createView>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="R3.Corda" id="identity_service_key_rotation-start-postgres" dbms="postgresql">
|
||||
<addColumn tableName="node_identities_no_cert">
|
||||
<column name="party_pk_hash" type="NVARCHAR(130)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
<column name="public_key" type="varbinary(64000)">
|
||||
<constraints nullable="true"/>
|
||||
</column>
|
||||
</addColumn>
|
||||
<createView viewName="v_our_key_pairs">
|
||||
select public_key_hash, lo_get(public_key) public_key from node_our_key_pairs
|
||||
</createView>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="R3.Corda" id="identity_service_key_rotation-custom">
|
||||
<customChange class="net.corda.node.migration.NodeIdentitiesNoCertMigration"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="R3.Corda" id="identity_service_key_rotation-end" dbms="!postgresql">
|
||||
<addNotNullConstraint tableName="node_identities_no_cert" columnName="party_pk_hash" columnDataType="NVARCHAR(130)"/>
|
||||
<addNotNullConstraint tableName="node_identities_no_cert" columnName="public_key" columnDataType="blob"/>
|
||||
<dropTable tableName="node_hash_to_key"/>
|
||||
<sql>
|
||||
UPDATE node_identities_no_cert SET owning_pk_hash =
|
||||
(SELECT pk_hash FROM node_named_identities WHERE node_named_identities.name = node_identities_no_cert.name)
|
||||
</sql>
|
||||
<sql>UPDATE node_identities_no_cert SET owning_pk_hash = 'unresolved' WHERE owning_pk_hash IS NULL</sql>
|
||||
<addNotNullConstraint tableName="node_identities_no_cert" columnName="owning_pk_hash" columnDataType="NVARCHAR(130)"/>
|
||||
<dropTable tableName="node_named_identities"/>
|
||||
<dropView viewName="v_our_key_pairs"/>
|
||||
</changeSet>
|
||||
|
||||
<changeSet author="R3.Corda" id="identity_service_key_rotation-end-postgres" dbms="postgresql">
|
||||
<addNotNullConstraint tableName="node_identities_no_cert" columnName="party_pk_hash" columnDataType="NVARCHAR(130)"/>
|
||||
<addNotNullConstraint tableName="node_identities_no_cert" columnName="public_key" columnDataType="varbinary(64000)"/>
|
||||
<dropTable tableName="node_hash_to_key"/>
|
||||
<dropTable tableName="node_named_identities"/>
|
||||
<dropView viewName="v_our_key_pairs"/>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
|
@ -12,13 +12,8 @@ import net.corda.core.crypto.Crypto
|
||||
import net.corda.core.crypto.toStringShort
|
||||
import net.corda.core.identity.CordaX500Name
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.identity.PartyAndCertificate
|
||||
import net.corda.coretesting.internal.rigorousMock
|
||||
import net.corda.node.migration.NodeIdentitiesNoCertMigration.Companion.UNRESOLVED
|
||||
import net.corda.node.services.api.SchemaService
|
||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||
import net.corda.nodeapi.internal.crypto.X509Utilities
|
||||
import net.corda.nodeapi.internal.crypto.x509Certificates
|
||||
import net.corda.nodeapi.internal.persistence.CordaPersistence
|
||||
import net.corda.nodeapi.internal.persistence.DatabaseConfig
|
||||
import net.corda.nodeapi.internal.persistence.contextTransactionOrNull
|
||||
@ -66,27 +61,10 @@ class IdenityServiceKeyRotationMigrationTest {
|
||||
entries.forEach { session.persist(it) }
|
||||
}
|
||||
|
||||
private fun PartyAndCertificate.dbCertificate() = IdentityTestSchemaV1.NodeIdentities(owningKey.toStringShort(), certPath.encoded)
|
||||
|
||||
private fun Party.dbParty() = IdentityTestSchemaV1.NodeIdentitiesNoCert(owningKey.toStringShort(), name.toString())
|
||||
|
||||
private fun TestIdentity.dbName() = IdentityTestSchemaV1.NodeNamedIdentities(name.toString(), publicKey.toStringShort())
|
||||
|
||||
private fun TestIdentity.dbKey() = IdentityTestSchemaV1.NodeHashToKey(publicKey.toStringShort(), publicKey.encoded)
|
||||
|
||||
private fun TestIdentity.dbKeyPair() =
|
||||
KMSTestSchemaV1.PersistentKey(publicKey.toStringShort(), publicKey.encoded, keyPair.private.encoded)
|
||||
|
||||
private fun TestIdentity.generateConfidentialIdentityWithCert(): PartyAndCertificate {
|
||||
val certificate = X509Utilities.createCertificate(
|
||||
CertificateType.CONFIDENTIAL_LEGAL_IDENTITY,
|
||||
identity.certificate,
|
||||
keyPair,
|
||||
name.x500Principal,
|
||||
Crypto.generateKeyPair().public)
|
||||
return PartyAndCertificate(X509Utilities.buildCertPath(certificate, identity.certPath.x509Certificates))
|
||||
}
|
||||
|
||||
@Test(timeout = 300_000)
|
||||
fun `test migration`() {
|
||||
val alice = TestIdentity(ALICE_NAME, 70)
|
||||
@ -94,67 +72,43 @@ class IdenityServiceKeyRotationMigrationTest {
|
||||
val charlie = TestIdentity(CHARLIE_NAME, 90)
|
||||
|
||||
val alice2 = TestIdentity(ALICE_NAME, 71)
|
||||
val alice3 = TestIdentity(ALICE_NAME, 72)
|
||||
val aliceCiWithCert = alice.generateConfidentialIdentityWithCert()
|
||||
|
||||
val bob2 = TestIdentity(BOB_NAME, 81)
|
||||
val bob3 = TestIdentity(BOB_NAME, 82)
|
||||
|
||||
val charlie2 = TestIdentity(CHARLIE_NAME, 91)
|
||||
val charlie3 = TestIdentity(CHARLIE_NAME, 92)
|
||||
val charlieCiWithCert = charlie.generateConfidentialIdentityWithCert()
|
||||
|
||||
persist(alice.identity.dbCertificate(), alice.party.dbParty(), alice.dbName())
|
||||
persist(charlie.identity.dbCertificate(), charlie.party.dbParty())
|
||||
persist(bob.identity.dbCertificate(), bob.party.dbParty(), bob.dbName())
|
||||
persist(alice.party.dbParty(), alice.dbName())
|
||||
persist(charlie.party.dbParty())
|
||||
persist(bob.party.dbParty(), bob.dbName())
|
||||
|
||||
persist(alice2.party.dbParty(), alice2.dbKeyPair())
|
||||
persist(alice3.party.dbParty())
|
||||
persist(aliceCiWithCert.dbCertificate(), aliceCiWithCert.party.dbParty())
|
||||
|
||||
persist(bob2.party.dbParty(), bob2.dbKey())
|
||||
persist(bob3.party.dbParty())
|
||||
|
||||
persist(charlie2.party.dbParty(), charlie2.dbKey())
|
||||
persist(charlie3.party.dbParty())
|
||||
persist(charlieCiWithCert.dbCertificate(), charlieCiWithCert.party.dbParty())
|
||||
persist(alice2.party.dbParty())
|
||||
persist(bob2.party.dbParty())
|
||||
persist(charlie2.party.dbParty())
|
||||
|
||||
Liquibase("migration/node-core.changelog-v20.xml", object : ClassLoaderResourceAccessor() {
|
||||
override fun getResourcesAsStream(path: String) = super.getResourcesAsStream(path)?.firstOrNull()?.let { setOf(it) }
|
||||
}, liquibaseDB).update(Contexts().toString())
|
||||
|
||||
val dummyKey = Crypto.generateKeyPair().public
|
||||
val results = mutableMapOf<String, List<*>>()
|
||||
val results = mutableMapOf<String, Pair<CordaX500Name, String>>()
|
||||
|
||||
cordaDB.transaction {
|
||||
connection.prepareStatement("SELECT pk_hash, name, party_pk_hash, public_key FROM node_identities_no_cert").use { ps ->
|
||||
connection.prepareStatement("SELECT pk_hash, name, owning_pk_hash FROM node_identities_no_cert").use { ps ->
|
||||
ps.executeQuery().use { rs ->
|
||||
while (rs.next()) {
|
||||
val partyKeyHash = rs.getString(3).takeUnless { it == UNRESOLVED } ?: dummyKey.toStringShort()
|
||||
val key = if (UNRESOLVED.toByteArray().contentEquals(rs.getBytes(4))) {
|
||||
dummyKey
|
||||
} else {
|
||||
Crypto.decodePublicKey(rs.getBytes(4))
|
||||
}
|
||||
results[rs.getString(1)] = listOf(key, CordaX500Name.parse(rs.getString(2)), partyKeyHash)
|
||||
val partyKeyHash = rs.getString(3).takeUnless { it == "unresolved" } ?: dummyKey.toStringShort()
|
||||
results[rs.getString(1)] = CordaX500Name.parse(rs.getString(2)) to partyKeyHash
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(11, results.size)
|
||||
assertEquals(6, results.size)
|
||||
|
||||
listOf(alice.identity, bob.identity, charlie.identity, aliceCiWithCert, charlieCiWithCert).forEach {
|
||||
assertEquals(results[it.owningKey.toStringShort()], listOf(it.owningKey, it.party.name, it.owningKey.toStringShort()))
|
||||
}
|
||||
assertEquals(results[alice.publicKey.toStringShort()], ALICE_NAME to alice.publicKey.toStringShort())
|
||||
assertEquals(results[bob.publicKey.toStringShort()], BOB_NAME to bob.publicKey.toStringShort())
|
||||
assertEquals(results[charlie.publicKey.toStringShort()], CHARLIE_NAME to dummyKey.toStringShort())
|
||||
|
||||
assertEquals(results[alice2.publicKey.toStringShort()], listOf(alice2.publicKey, ALICE_NAME, alice.publicKey.toStringShort()))
|
||||
assertEquals(results[alice3.publicKey.toStringShort()], listOf(dummyKey, ALICE_NAME, alice.publicKey.toStringShort()))
|
||||
|
||||
assertEquals(results[bob2.publicKey.toStringShort()], listOf(bob2.publicKey, BOB_NAME, bob.publicKey.toStringShort()))
|
||||
assertEquals(results[bob3.publicKey.toStringShort()], listOf(dummyKey, BOB_NAME, bob.publicKey.toStringShort()))
|
||||
|
||||
assertEquals(results[charlie2.publicKey.toStringShort()], listOf(charlie2.publicKey, CHARLIE_NAME, dummyKey.toStringShort()))
|
||||
assertEquals(results[charlie3.publicKey.toStringShort()], listOf(dummyKey, CHARLIE_NAME, dummyKey.toStringShort()))
|
||||
assertEquals(results[alice2.publicKey.toStringShort()], ALICE_NAME to alice.publicKey.toStringShort())
|
||||
assertEquals(results[bob2.publicKey.toStringShort()], BOB_NAME to bob.publicKey.toStringShort())
|
||||
assertEquals(results[charlie2.publicKey.toStringShort()], CHARLIE_NAME to dummyKey.toStringShort())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user