ENT-9583 Public key caching of encoded form (OS) (#7332)

This commit is contained in:
Rick Parker 2023-04-26 17:49:52 +01:00 committed by GitHub
parent f4917e08e1
commit 9ba3919980
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 215 additions and 47 deletions

View File

@ -82,6 +82,7 @@ def patchCore = tasks.register('patchCore', Zip) {
exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class' exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class'
exclude 'net/corda/core/internal/rules/*.class' exclude 'net/corda/core/internal/rules/*.class'
exclude 'net/corda/core/internal/utilities/PrivateInterner*.class' exclude 'net/corda/core/internal/utilities/PrivateInterner*.class'
exclude 'net/corda/core/crypto/internal/PublicKeyCache*.class'
exclude 'net/corda/core/internal/ContractStateClassCache*.class' exclude 'net/corda/core/internal/ContractStateClassCache*.class'
} }

View File

@ -0,0 +1,21 @@
package net.corda.core.crypto.internal
import net.corda.core.utilities.ByteSequence
import java.security.PublicKey
@Suppress("unused")
object PublicKeyCache {
@Suppress("UNUSED_PARAMETER")
fun bytesForCachedPublicKey(key: PublicKey): ByteSequence? {
return null
}
@Suppress("UNUSED_PARAMETER")
fun publicKeyForCachedBytes(bytes: ByteSequence): PublicKey? {
return null
}
fun cachePublicKey(key: PublicKey): PublicKey {
return key
}
}

View File

@ -4,7 +4,14 @@ import net.corda.core.KeepForDJVM
import net.corda.core.serialization.CordaSerializable import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.exactAdd import net.corda.core.utilities.exactAdd
import net.corda.core.utilities.sequence import net.corda.core.utilities.sequence
import org.bouncycastle.asn1.* import org.bouncycastle.asn1.ASN1EncodableVector
import org.bouncycastle.asn1.ASN1Encoding
import org.bouncycastle.asn1.ASN1Integer
import org.bouncycastle.asn1.ASN1Object
import org.bouncycastle.asn1.ASN1Primitive
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.DERBitString
import org.bouncycastle.asn1.DERSequence
import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.security.PublicKey import java.security.PublicKey
@ -162,7 +169,7 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
override fun toASN1Primitive(): ASN1Primitive { override fun toASN1Primitive(): ASN1Primitive {
val vector = ASN1EncodableVector() val vector = ASN1EncodableVector()
vector.add(DERBitString(node.encoded)) vector.add(DERBitString(Crypto.encodePublicKey(node)))
vector.add(ASN1Integer(weight.toLong())) vector.add(ASN1Integer(weight.toLong()))
return DERSequence(vector) return DERSequence(vector)
} }

View File

@ -5,6 +5,7 @@ import net.corda.core.DeleteForDJVM
import net.corda.core.KeepForDJVM import net.corda.core.KeepForDJVM
import net.corda.core.crypto.internal.AliasPrivateKey import net.corda.core.crypto.internal.AliasPrivateKey
import net.corda.core.crypto.internal.Instances.withSignature import net.corda.core.crypto.internal.Instances.withSignature
import net.corda.core.crypto.internal.PublicKeyCache
import net.corda.core.crypto.internal.bouncyCastlePQCProvider import net.corda.core.crypto.internal.bouncyCastlePQCProvider
import net.corda.core.crypto.internal.cordaBouncyCastleProvider import net.corda.core.crypto.internal.cordaBouncyCastleProvider
import net.corda.core.crypto.internal.cordaSecurityProvider import net.corda.core.crypto.internal.cordaSecurityProvider
@ -12,6 +13,7 @@ import net.corda.core.crypto.internal.`id-Curve25519ph`
import net.corda.core.crypto.internal.providerMap import net.corda.core.crypto.internal.providerMap
import net.corda.core.internal.utilities.PrivateInterner import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.ByteSequence
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
@ -281,7 +283,7 @@ object Crypto {
*/ */
@JvmStatic @JvmStatic
fun findSignatureScheme(key: PublicKey): SignatureScheme { fun findSignatureScheme(key: PublicKey): SignatureScheme {
val keyInfo = SubjectPublicKeyInfo.getInstance(key.encoded) val keyInfo = SubjectPublicKeyInfo.getInstance(encodePublicKey(key))
return findSignatureScheme(keyInfo.algorithm) return findSignatureScheme(keyInfo.algorithm)
} }
@ -373,10 +375,17 @@ object Crypto {
*/ */
@JvmStatic @JvmStatic
fun decodePublicKey(encodedKey: ByteArray): PublicKey { fun decodePublicKey(encodedKey: ByteArray): PublicKey {
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: {
val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey)
val keyFactory = keyFactory(signatureScheme) val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm)
return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) val keyFactory = keyFactory(signatureScheme)
convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
}()
}
@JvmStatic
fun encodePublicKey(key: PublicKey): ByteArray {
return PublicKeyCache.bytesForCachedPublicKey(key)?.bytes ?: key.encoded
} }
/** /**
@ -993,7 +1002,8 @@ object Crypto {
} }
private val interner = PrivateInterner<PublicKey>() private val interner = PrivateInterner<PublicKey>()
private fun internPublicKey(key: PublicKey): PublicKey = interner.intern(key) private fun internPublicKey(key: PublicKey): PublicKey = PublicKeyCache.cachePublicKey(interner.intern(key))
private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey { private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey {
return internPublicKey(when (key) { return internPublicKey(when (key) {

View File

@ -13,7 +13,6 @@ import net.corda.core.serialization.CordaSerializable
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.parseAsHex import net.corda.core.utilities.parseAsHex
import net.corda.core.utilities.toHexString import net.corda.core.utilities.toHexString
import java.nio.ByteBuffer
import java.security.MessageDigest import java.security.MessageDigest
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentMap import java.util.concurrent.ConcurrentMap
@ -39,9 +38,10 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
return true return true
} }
// This is an efficient hashCode, because there is no point in performing a hash calculation on a cryptographic hash. override fun hashCode(): Int {
// It just takes the first 4 bytes and transforms them into an Int. // Hash code not overridden on purpose (super class impl will do), but don't delete or have to deal with detekt and API checker.
override fun hashCode() = ByteBuffer.wrap(bytes).int return super.hashCode()
}
/** /**
* Convert the hash value to an uppercase hexadecimal [String]. * Convert the hash value to an uppercase hexadecimal [String].
@ -62,7 +62,10 @@ sealed class SecureHash(bytes: ByteArray) : OpaqueBytes(bytes) {
} }
} }
override fun hashCode() = ByteBuffer.wrap(bytes).int override fun hashCode(): Int {
// Hash code not overridden on purpose (super class impl will do), but don't delete or have to deal with detekt and API checker.
return super.hashCode()
}
override fun toString(): String { override fun toString(): String {
return "$algorithm$DELIMITER${toHexString()}" return "$algorithm$DELIMITER${toHexString()}"

View File

@ -0,0 +1,54 @@
package net.corda.core.crypto.internal
import net.corda.core.utilities.ByteSequence
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference
import java.security.PublicKey
import java.util.concurrent.ConcurrentHashMap
object PublicKeyCache {
private val collectedWeakPubKeys = ReferenceQueue<PublicKey>()
private class WeakPubKey(key: PublicKey, val bytes: ByteSequence? = null) : WeakReference<PublicKey>(key, collectedWeakPubKeys) {
private val hashCode = key.hashCode()
override fun hashCode(): Int = hashCode
override fun equals(other: Any?): Boolean {
if(this === other) return true
if(other !is WeakPubKey) return false
if(this.hashCode != other.hashCode) return false
val thisGet = this.get()
val otherGet = other.get()
if(thisGet == null || otherGet == null) return false
return thisGet == otherGet
}
}
private val pubKeyToBytes = ConcurrentHashMap<WeakPubKey, ByteSequence>()
private val bytesToPubKey = ConcurrentHashMap<ByteSequence, WeakPubKey>()
private fun reapCollectedWeakPubKeys() {
while(true) {
val weakPubKey = (collectedWeakPubKeys.poll() as? WeakPubKey) ?: break
pubKeyToBytes.remove(weakPubKey)
bytesToPubKey.remove(weakPubKey.bytes!!)
}
}
fun bytesForCachedPublicKey(key: PublicKey): ByteSequence? {
val weakPubKey = WeakPubKey(key)
return pubKeyToBytes[weakPubKey]
}
fun publicKeyForCachedBytes(bytes: ByteSequence): PublicKey? {
return bytesToPubKey[bytes]?.get()
}
fun cachePublicKey(key: PublicKey): PublicKey {
reapCollectedWeakPubKeys()
val weakPubKey = WeakPubKey(key, ByteSequence.of(key.encoded))
pubKeyToBytes.putIfAbsent(weakPubKey, weakPubKey.bytes!!)
bytesToPubKey.putIfAbsent(weakPubKey.bytes, weakPubKey)
return key
}
}

View File

@ -555,7 +555,7 @@ fun <T : Any> SerializedBytes<T>.sign(keyPair: KeyPair): SignedData<T> = SignedD
fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) } fun ByteBuffer.copyBytes(): ByteArray = ByteArray(remaining()).also { get(it) }
val PublicKey.hash: SecureHash get() = encoded.sha256() val PublicKey.hash: SecureHash get() = Crypto.encodePublicKey(this).sha256()
/** /**
* Extension method for providing a sumBy method that processes and returns a Long * Extension method for providing a sumBy method that processes and returns a Long

View File

@ -1,9 +1,16 @@
package net.corda.core.internal package net.corda.core.internal
import net.corda.core.DeleteForDJVM import net.corda.core.DeleteForDJVM
import net.corda.core.crypto.Crypto
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import java.security.* import java.security.AlgorithmParameters
import java.security.InvalidKeyException
import java.security.MessageDigest
import java.security.PrivateKey
import java.security.PublicKey
import java.security.SecureRandom
import java.security.Signature
import java.security.spec.AlgorithmParameterSpec import java.security.spec.AlgorithmParameterSpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
@ -30,7 +37,7 @@ class X509EdDSAEngine : Signature {
override fun engineInitVerify(publicKey: PublicKey) { override fun engineInitVerify(publicKey: PublicKey) {
val parsedKey = try { val parsedKey = try {
publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(publicKey.encoded)) publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(Crypto.encodePublicKey(publicKey)))
} catch (e: Exception) { } catch (e: Exception) {
throw (InvalidKeyException(e.message)) throw (InvalidKeyException(e.message))
} }

View File

@ -166,7 +166,7 @@ class Vault<out T : ContractState>(val states: Iterable<StateAndRef<T>>) {
fun data(): ByteArray? { fun data(): ByteArray? {
return when (type()) { return when (type()) {
Type.HASH -> (constraint as HashAttachmentConstraint).attachmentId.bytes Type.HASH -> (constraint as HashAttachmentConstraint).attachmentId.bytes
Type.SIGNATURE -> (constraint as SignatureAttachmentConstraint).key.encoded Type.SIGNATURE -> Crypto.encodePublicKey((constraint as SignatureAttachmentConstraint).key)
else -> null else -> null
} }
} }

View File

@ -112,6 +112,7 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
if (this === other) return true if (this === other) return true
if (other !is ByteSequence) return false if (other !is ByteSequence) return false
if (this.size != other.size) return false if (this.size != other.size) return false
if (this.hashCode() != other.hashCode()) return false
return subArraysEqual(this._bytes, this.offset, this.size, other._bytes, other.offset) return subArraysEqual(this._bytes, this.offset, this.size, other._bytes, other.offset)
} }
@ -125,13 +126,18 @@ sealed class ByteSequence(private val _bytes: ByteArray, val offset: Int, val si
return true return true
} }
private var _hashCode: Int = 0;
override fun hashCode(): Int { override fun hashCode(): Int {
val thisBytes = _bytes return if (_hashCode == 0) {
var result = 1 val thisBytes = _bytes
for (index in offset until (offset + size)) { var result = 1
result = 31 * result + thisBytes[index] for (index in offset until (offset + size)) {
} result = 31 * result + thisBytes[index]
return result }
_hashCode = result
result
} else _hashCode
} }
override fun toString(): String = "[${copyBytes().toHexString()}]" override fun toString(): String = "[${copyBytes().toHexString()}]"

View File

@ -9,7 +9,7 @@ import net.corda.core.crypto.Crypto
import net.corda.core.internal.hash import net.corda.core.internal.hash
import java.nio.charset.Charset import java.nio.charset.Charset
import java.security.PublicKey import java.security.PublicKey
import java.util.* import java.util.Base64
// This file includes useful encoding methods and extension functions for the most common encoding/decoding operations. // This file includes useful encoding methods and extension functions for the most common encoding/decoding operations.
@ -81,7 +81,7 @@ fun String.hexToBase64(): String = hexToByteArray().toBase64()
fun parsePublicKeyBase58(base58String: String): PublicKey = Crypto.decodePublicKey(base58String.base58ToByteArray()) fun parsePublicKeyBase58(base58String: String): PublicKey = Crypto.decodePublicKey(base58String.base58ToByteArray())
/** Return the Base58 representation of the serialised public key. */ /** Return the Base58 representation of the serialised public key. */
fun PublicKey.toBase58String(): String = this.encoded.toBase58() fun PublicKey.toBase58String(): String = Crypto.encodePublicKey(this).toBase58()
/** Return the bytes of the SHA-256 output for this public key. */ /** Return the bytes of the SHA-256 output for this public key. */
fun PublicKey.toSHA256Bytes(): ByteArray = this.hash.bytes fun PublicKey.toSHA256Bytes(): ByteArray = this.hash.bytes

View File

@ -3,14 +3,33 @@ package net.corda.nodeapi.internal.crypto
import net.corda.core.CordaOID import net.corda.core.CordaOID
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.internal.* import net.corda.core.internal.CertRole
import net.corda.core.internal.SignedDataWithCert
import net.corda.core.internal.reader
import net.corda.core.internal.signWithCert
import net.corda.core.internal.uncheckedCast
import net.corda.core.internal.validate
import net.corda.core.internal.writer
import net.corda.core.utilities.days import net.corda.core.utilities.days
import net.corda.core.utilities.millis import net.corda.core.utilities.millis
import org.bouncycastle.asn1.* import org.bouncycastle.asn1.ASN1EncodableVector
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.ASN1Sequence
import org.bouncycastle.asn1.DERSequence
import org.bouncycastle.asn1.DERUTF8String
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x500.style.BCStyle import org.bouncycastle.asn1.x500.style.BCStyle
import org.bouncycastle.asn1.x509.* import org.bouncycastle.asn1.x509.BasicConstraints
import org.bouncycastle.asn1.x509.CRLDistPoint
import org.bouncycastle.asn1.x509.DistributionPoint
import org.bouncycastle.asn1.x509.DistributionPointName
import org.bouncycastle.asn1.x509.Extension import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.GeneralName
import org.bouncycastle.asn1.x509.GeneralNames
import org.bouncycastle.asn1.x509.KeyPurposeId
import org.bouncycastle.asn1.x509.KeyUsage
import org.bouncycastle.asn1.x509.NameConstraints
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.cert.X509CertificateHolder import org.bouncycastle.cert.X509CertificateHolder
import org.bouncycastle.cert.X509v3CertificateBuilder import org.bouncycastle.cert.X509v3CertificateBuilder
import org.bouncycastle.cert.bc.BcX509ExtensionUtils import org.bouncycastle.cert.bc.BcX509ExtensionUtils
@ -28,12 +47,18 @@ import java.nio.file.Path
import java.security.KeyPair import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import java.security.SignatureException import java.security.SignatureException
import java.security.cert.* import java.security.cert.CertPath
import java.security.cert.Certificate import java.security.cert.Certificate
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.TrustAnchor
import java.security.cert.X509CRL
import java.security.cert.X509Certificate
import java.time.Duration import java.time.Duration
import java.time.Instant import java.time.Instant
import java.time.temporal.ChronoUnit import java.time.temporal.ChronoUnit
import java.util.* import java.util.ArrayList
import java.util.Date
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
import kotlin.experimental.and import kotlin.experimental.and
import kotlin.experimental.or import kotlin.experimental.or
@ -189,7 +214,7 @@ object X509Utilities {
crlIssuer: X500Name? = null): X509v3CertificateBuilder { crlIssuer: X500Name? = null): X509v3CertificateBuilder {
val serial = generateCertificateSerialNumber() val serial = generateCertificateSerialNumber()
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 subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(ASN1Sequence.getInstance(Crypto.encodePublicKey(subjectPublicKey)))
val role = certificateType.role val role = certificateType.role
val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey) val builder = JcaX509v3CertificateBuilder(issuer, serial, validityWindow.first, validityWindow.second, subject, subjectPublicKey)

View File

@ -1,6 +1,10 @@
package net.corda.nodeapi.internal.serialization.kryo package net.corda.nodeapi.internal.serialization.kryo
import com.esotericsoftware.kryo.* import com.esotericsoftware.kryo.ClassResolver
import com.esotericsoftware.kryo.Kryo
import com.esotericsoftware.kryo.KryoException
import com.esotericsoftware.kryo.Registration
import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory
import com.esotericsoftware.kryo.io.Input import com.esotericsoftware.kryo.io.Input
import com.esotericsoftware.kryo.io.Output import com.esotericsoftware.kryo.io.Output
@ -17,7 +21,13 @@ import net.corda.core.internal.LazyMappedList
import net.corda.core.internal.uncheckedCast import net.corda.core.internal.uncheckedCast
import net.corda.core.serialization.SerializeAsTokenContext import net.corda.core.serialization.SerializeAsTokenContext
import net.corda.core.serialization.SerializedBytes import net.corda.core.serialization.SerializedBytes
import net.corda.core.transactions.* import net.corda.core.transactions.ComponentGroup
import net.corda.core.transactions.ContractUpgradeFilteredTransaction
import net.corda.core.transactions.ContractUpgradeWireTransaction
import net.corda.core.transactions.CoreTransaction
import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.SgxSupport import net.corda.core.utilities.SgxSupport
import net.corda.serialization.internal.serializationContextKey import net.corda.serialization.internal.serializationContextKey
@ -302,7 +312,7 @@ object PrivateKeySerializer : Serializer<PrivateKey>() {
object PublicKeySerializer : Serializer<PublicKey>() { object PublicKeySerializer : Serializer<PublicKey>() {
override fun write(kryo: Kryo, output: Output, obj: PublicKey) { override fun write(kryo: Kryo, output: Output, obj: PublicKey) {
// TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser. // TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser.
output.writeBytesWithLength(obj.encoded) output.writeBytesWithLength(Crypto.encodePublicKey(obj))
} }
override fun read(kryo: Kryo, input: Input, type: Class<PublicKey>): PublicKey { override fun read(kryo: Kryo, input: Input, type: Class<PublicKey>): PublicKey {

View File

@ -47,7 +47,9 @@ import java.security.cert.CertificateNotYetValidException
import java.security.cert.CollectionCertStoreParameters import java.security.cert.CollectionCertStoreParameters
import java.security.cert.TrustAnchor import java.security.cert.TrustAnchor
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.HashSet
import java.util.Optional
import java.util.UUID
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.stream.Stream import java.util.stream.Stream
@ -136,7 +138,7 @@ class PersistentIdentityService(cacheFactory: NamedCacheFactory) : SingletonSeri
) )
}, },
toPersistentEntity = { key: String, value: PublicKey -> toPersistentEntity = { key: String, value: PublicKey ->
PersistentHashToPublicKey(key, value.encoded) PersistentHashToPublicKey(key, Crypto.encodePublicKey(value))
}, },
persistentEntityClass = PersistentHashToPublicKey::class.java) persistentEntityClass = PersistentHashToPublicKey::class.java)
} }

View File

@ -1,6 +1,14 @@
package net.corda.node.services.keys package net.corda.node.services.keys
import net.corda.core.crypto.* import net.corda.core.crypto.Crypto
import net.corda.core.crypto.DigitalSignature
import net.corda.core.crypto.SignableData
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.TransactionSignature
import net.corda.core.crypto.generateKeyPair
import net.corda.core.crypto.keys
import net.corda.core.crypto.sign
import net.corda.core.crypto.toStringShort
import net.corda.core.internal.NamedCacheFactory import net.corda.core.internal.NamedCacheFactory
import net.corda.core.internal.telemetry.TelemetryServiceImpl import net.corda.core.internal.telemetry.TelemetryServiceImpl
import net.corda.core.serialization.SingletonSerializeAsToken import net.corda.core.serialization.SingletonSerializeAsToken
@ -16,9 +24,12 @@ 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.util.* import java.util.UUID
import javax.persistence.* import javax.persistence.Column
import kotlin.collections.LinkedHashSet import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.Lob
import javax.persistence.Table
/** /**
* A persistent implementation of [KeyManagementServiceInternal] to support CryptoService for initial keys and * A persistent implementation of [KeyManagementServiceInternal] to support CryptoService for initial keys and
@ -52,7 +63,7 @@ class BasicHSMKeyManagementService(
var privateKey: ByteArray = EMPTY_BYTE_ARRAY var privateKey: ByteArray = EMPTY_BYTE_ARRAY
) { ) {
constructor(publicKey: PublicKey, privateKey: PrivateKey) constructor(publicKey: PublicKey, privateKey: PrivateKey)
: this(publicKey.toStringShort(), publicKey.encoded, privateKey.encoded) : this(publicKey.toStringShort(), Crypto.encodePublicKey(publicKey), privateKey.encoded)
} }
private companion object { private companion object {

View File

@ -13,6 +13,6 @@ import javax.persistence.Converter
*/ */
@Converter(autoApply = true) @Converter(autoApply = true)
class PublicKeyToTextConverter : AttributeConverter<PublicKey, String> { class PublicKeyToTextConverter : AttributeConverter<PublicKey, String> {
override fun convertToDatabaseColumn(key: PublicKey?): String? = key?.encoded?.toHex() override fun convertToDatabaseColumn(key: PublicKey?): String? = key?.let { Crypto.encodePublicKey(key).toHex() }
override fun convertToEntityAttribute(text: String?): PublicKey? = text?.let { Crypto.decodePublicKey(it.hexToByteArray()) } override fun convertToEntityAttribute(text: String?): PublicKey? = text?.let { Crypto.decodePublicKey(it.hexToByteArray()) }
} }

View File

@ -1,7 +1,6 @@
package sandbox.net.corda.core.crypto package sandbox.net.corda.core.crypto
import sandbox.net.corda.core.crypto.DJVM.fromDJVM import sandbox.java.lang.Object
import sandbox.net.corda.core.crypto.DJVM.toDJVM
import sandbox.java.lang.String import sandbox.java.lang.String
import sandbox.java.lang.doCatch import sandbox.java.lang.doCatch
import sandbox.java.math.BigInteger import sandbox.java.math.BigInteger
@ -10,7 +9,8 @@ import sandbox.java.security.PrivateKey
import sandbox.java.security.PublicKey import sandbox.java.security.PublicKey
import sandbox.java.util.ArrayList import sandbox.java.util.ArrayList
import sandbox.java.util.List import sandbox.java.util.List
import sandbox.java.lang.Object import sandbox.net.corda.core.crypto.DJVM.fromDJVM
import sandbox.net.corda.core.crypto.DJVM.toDJVM
import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier import sandbox.org.bouncycastle.asn1.x509.AlgorithmIdentifier
import sandbox.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import sandbox.org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import java.security.GeneralSecurityException import java.security.GeneralSecurityException
@ -149,6 +149,11 @@ object Crypto : Object() {
return decodePublicKey(signatureScheme.schemeCodeName, encodedKey) return decodePublicKey(signatureScheme.schemeCodeName, encodedKey)
} }
@JvmStatic
fun encodePublicKey(key: java.security.PublicKey): ByteArray {
return key.encoded
}
@JvmStatic @JvmStatic
fun deriveKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair { fun deriveKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair {
throw sandbox.java.lang.failApi("Crypto.deriveKeyPair(SignatureScheme, PrivateKey, byte[])") throw sandbox.java.lang.failApi("Crypto.deriveKeyPair(SignatureScheme, PrivateKey, byte[])")

View File

@ -3,7 +3,13 @@ package net.corda.serialization.internal.amqp.custom
import net.corda.core.crypto.Crypto import net.corda.core.crypto.Crypto
import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY import net.corda.core.serialization.DESERIALIZATION_CACHE_PROPERTY
import net.corda.core.serialization.SerializationContext import net.corda.core.serialization.SerializationContext
import net.corda.serialization.internal.amqp.* import net.corda.serialization.internal.amqp.AMQPTypeIdentifiers
import net.corda.serialization.internal.amqp.CustomSerializer
import net.corda.serialization.internal.amqp.DeserializationInput
import net.corda.serialization.internal.amqp.RestrictedType
import net.corda.serialization.internal.amqp.Schema
import net.corda.serialization.internal.amqp.SerializationOutput
import net.corda.serialization.internal.amqp.SerializationSchemas
import org.apache.qpid.proton.codec.Data import org.apache.qpid.proton.codec.Data
import java.lang.reflect.Type import java.lang.reflect.Type
import java.security.PublicKey import java.security.PublicKey
@ -28,7 +34,7 @@ object PublicKeySerializer
context: SerializationContext context: SerializationContext
) { ) {
// TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser. // TODO: Instead of encoding to the default X509 format, we could have a custom per key type (space-efficient) serialiser.
output.writeObject(obj.encoded, data, clazz, context) output.writeObject(Crypto.encodePublicKey(obj), data, clazz, context)
} }
override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput, override fun readObject(obj: Any, schemas: SerializationSchemas, input: DeserializationInput,