diff --git a/.ci/api-current.txt b/.ci/api-current.txt
index 72eea8da0a..d4f9301255 100644
--- a/.ci/api-current.txt
+++ b/.ci/api-current.txt
@@ -8290,11 +8290,6 @@ public static final class net.corda.core.utilities.ProgressTracker$UNSTARTED ext
public interface net.corda.core.utilities.PropertyDelegate
public abstract T getValue(Object, kotlin.reflect.KProperty)
##
-public final class net.corda.core.utilities.SgxSupport extends java.lang.Object
- public static final boolean isInsideEnclave()
- @NotNull
- public static final net.corda.core.utilities.SgxSupport INSTANCE
-##
public final class net.corda.core.utilities.ThreadDumpUtilsKt extends java.lang.Object
@NotNull
public static final String asString(management.ThreadInfo, int)
diff --git a/build.gradle b/build.gradle
index 94ce6dbe9b..34fe923b28 100644
--- a/build.gradle
+++ b/build.gradle
@@ -90,7 +90,6 @@ buildscript {
ext.h2_version = constants.getProperty("h2Version")
ext.rxjava_version = constants.getProperty("rxjavaVersion")
ext.dokka_version = constants.getProperty("dokkaVersion")
- ext.eddsa_version = constants.getProperty("eddsaVersion")
ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion")
ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion")
ext.beanutils_version = constants.getProperty("beanutilsVersion")
@@ -178,7 +177,6 @@ buildscript {
classpath "com.guardsquare:proguard-gradle:$proguard_version"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0'
classpath "org.jetbrains.dokka:dokka-base:$dokka_version"
- classpath "net.i2p.crypto:eddsa:$eddsa_version" // Needed for ServiceIdentityGenerator in the build environment.
classpath "org.owasp:dependency-check-gradle:$dependency_checker_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version"
// Capsule gradle plugin forked and maintained locally to support Gradle 5.x
diff --git a/constants.properties b/constants.properties
index 3880d9f178..efd2246e17 100644
--- a/constants.properties
+++ b/constants.properties
@@ -21,7 +21,7 @@ guavaVersion=28.0-jre
quasarVersion=0.9.0_r3
dockerJavaVersion=3.2.5
proguardVersion=7.3.1
-// bouncy castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues.
+# Bouncy Castle version must not be changed on a patch release. Needs a full release test cycle to flush out any issues.
bouncycastleVersion=1.75
classgraphVersion=4.8.135
disruptorVersion=3.4.2
@@ -79,7 +79,6 @@ hibernateVersion=5.6.14.Final
h2Version=2.2.224
rxjavaVersion=1.3.8
dokkaVersion=1.8.20
-eddsaVersion=0.3.0
dependencyCheckerVersion=5.2.0
commonsCollectionsVersion=4.3
beanutilsVersion=1.9.4
diff --git a/core-tests/build.gradle b/core-tests/build.gradle
index 80c9b5c6ab..16347f88e2 100644
--- a/core-tests/build.gradle
+++ b/core-tests/build.gradle
@@ -184,7 +184,6 @@ quasar {
"io.github.classgraph**",
"io.netty*",
"liquibase**",
- "net.i2p.crypto.**",
"nonapi.io.github.classgraph.**",
"org.apiguardian.**",
"org.bouncycastle**",
diff --git a/core/build.gradle b/core/build.gradle
index 427179cb67..b1c5e1d7dd 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -36,8 +36,6 @@ dependencies {
// For caches rather than guava
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
implementation "org.apache.commons:commons-lang3:$commons_lang3_version"
- // Java ed25519 implementation. See https://github.com/str4d/ed25519-java/
- implementation "net.i2p.crypto:eddsa:$eddsa_version"
// Bouncy castle support needed for X509 certificate manipulation
implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
// required to use @Type annotation
@@ -93,19 +91,7 @@ processTestResources {
}
}
-compileTestJava {
- options.compilerArgs += [
- '--add-exports', 'java.base/sun.security.util=ALL-UNNAMED',
- '--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED'
- ]
-}
-
test {
- // TODO This obscures whether any Corda client APIs need these JVM flags as well (which they shouldn't do)
- jvmArgs += [
- '--add-exports', 'java.base/sun.security.util=ALL-UNNAMED',
- '--add-exports', 'java.base/sun.security.x509=ALL-UNNAMED'
- ]
maxParallelForks = (System.env.CORDA_CORE_TESTING_FORKS == null) ? 1 : "$System.env.CORDA_CORE_TESTING_FORKS".toInteger()
}
@@ -129,7 +115,6 @@ quasar {
"io.github.classgraph**",
"io.netty*",
"liquibase**",
- "net.i2p.crypto.**",
"nonapi.io.github.classgraph.**",
"org.apiguardian.**",
"org.bouncycastle**",
diff --git a/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt b/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt
index 0be1edfd55..70bf257a3f 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/CordaSecurityProvider.kt
@@ -5,11 +5,10 @@ import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
import net.corda.core.crypto.internal.PlatformSecureRandomService
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import java.security.Provider
-import java.util.*
+import java.util.Optional
import java.util.concurrent.ConcurrentHashMap
-@Suppress("DEPRECATION") // JDK11: should replace with Provider(String name, double version, String info) (since 9)
-class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
+class CordaSecurityProvider : Provider(PROVIDER_NAME, "0.2", "$PROVIDER_NAME security provider") {
companion object {
const val PROVIDER_NAME = "Corda"
}
@@ -17,21 +16,8 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
private val services = ConcurrentHashMap, Optional>()
init {
- put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", CompositeKeyFactory::class.java.name)
- put("Alg.Alias.KeyFactory.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM)
- put("Alg.Alias.KeyFactory.OID.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM)
- put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", CompositeSignature::class.java.name)
- put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
- put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
- putPlatformSecureRandomService()
-
- // JDK11+ - Hack to set Provider#legacyChanged to false, without this SecureRandom will not
- // pickup our random implementation (even if our provider is the first provider in
- // the chain).
- super.getService("UNDEFINED", "UNDEFINED")
- }
-
- private fun putPlatformSecureRandomService() {
+ putService(Service(this, "KeyFactory", CompositeKey.KEY_ALGORITHM, CompositeKeyFactory::class.java.name, listOf("$COMPOSITE_KEY", "OID.$COMPOSITE_KEY"), null))
+ putService(Service(this, "Signature", CompositeSignature.SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, listOf("$COMPOSITE_SIGNATURE", "OID.$COMPOSITE_SIGNATURE"), null))
putService(PlatformSecureRandomService(this))
}
diff --git a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt
index 8018c68892..7fbdde3cd7 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/Crypto.kt
@@ -1,31 +1,26 @@
package net.corda.core.crypto
import net.corda.core.CordaOID
+import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.internal.AliasPrivateKey
+import net.corda.core.crypto.internal.Curve25519.isOnCurve25519
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.cordaBouncyCastleProvider
import net.corda.core.crypto.internal.cordaSecurityProvider
-import net.corda.core.crypto.internal.`id-Curve25519ph`
import net.corda.core.crypto.internal.providerMap
+import net.corda.core.crypto.internal.sunEcProvider
import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.serialize
import net.corda.core.utilities.ByteSequence
-import net.i2p.crypto.eddsa.EdDSAEngine
-import net.i2p.crypto.eddsa.EdDSAPrivateKey
-import net.i2p.crypto.eddsa.EdDSAPublicKey
-import net.i2p.crypto.eddsa.math.GroupElement
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1Integer
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.DERNull
import org.bouncycastle.asn1.DERUTF8String
import org.bouncycastle.asn1.DLSequence
import org.bouncycastle.asn1.bc.BCObjectIdentifiers
+import org.bouncycastle.asn1.edec.EdECObjectIdentifiers
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
@@ -34,6 +29,9 @@ import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers
import org.bouncycastle.crypto.CryptoServicesRegistrar
+import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters
+import org.bouncycastle.crypto.util.PrivateKeyInfoFactory
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey
@@ -49,20 +47,24 @@ import org.bouncycastle.jce.spec.ECPublicKeySpec
import org.bouncycastle.math.ec.ECConstants
import org.bouncycastle.math.ec.FixedPointCombMultiplier
import org.bouncycastle.math.ec.WNafUtil
+import org.bouncycastle.math.ec.rfc8032.Ed25519
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
import java.math.BigInteger
import java.security.InvalidKeyException
import java.security.Key
-import java.security.KeyFactory
import java.security.KeyPair
import java.security.KeyPairGenerator
import java.security.PrivateKey
import java.security.Provider
import java.security.PublicKey
+import java.security.Signature
import java.security.SignatureException
+import java.security.interfaces.EdECPrivateKey
+import java.security.interfaces.EdECPublicKey
import java.security.spec.InvalidKeySpecException
+import java.security.spec.NamedParameterSpec
import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Mac
@@ -77,7 +79,7 @@ import javax.crypto.spec.SecretKeySpec
* RSA_SHA256 (RSA PKCS#1 using SHA256 as hash algorithm).
* ECDSA_SECP256K1_SHA256 (ECDSA using the secp256k1 Koblitz curve and SHA256 as hash algorithm).
* ECDSA_SECP256R1_SHA256 (ECDSA using the secp256r1 (NIST P-256) curve and SHA256 as hash algorithm).
- * EDDSA_ED25519_SHA512 (EdDSA using the ed255519 twisted Edwards curve and SHA512 as hash algorithm).
+ * EDDSA_ED25519_SHA512 (EdDSA using the ed25519 twisted Edwards curve and SHA512 as hash algorithm).
* SPHINCS256_SHA512 (SPHINCS-256 hash-based signature scheme using SHA512 as hash algorithm).
*
*/
@@ -95,7 +97,7 @@ object Crypto {
listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)),
cordaBouncyCastleProvider.name,
"RSA",
- "SHA256WITHRSA",
+ "SHA256withRSA",
null,
3072,
"RSA_SHA256 signature scheme using SHA256 as hash algorithm."
@@ -140,13 +142,12 @@ object Crypto {
val EDDSA_ED25519_SHA512: SignatureScheme = SignatureScheme(
4,
"EDDSA_ED25519_SHA512",
- AlgorithmIdentifier(`id-Curve25519ph`, null),
- emptyList(), // Both keys and the signature scheme use the same OID in i2p library.
- // We added EdDSA to bouncy castle for certificate signing.
- cordaBouncyCastleProvider.name,
- "1.3.101.112",
- EdDSAEngine.SIGNATURE_ALGORITHM,
- EdDSANamedCurveTable.getByName("ED25519"),
+ AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519, null),
+ emptyList(), // Both keys and the signature scheme use the same OID.
+ sunEcProvider.name,
+ "Ed25519",
+ "Ed25519",
+ NamedParameterSpec.ED25519,
256,
"EdDSA signature scheme using the ed25519 twisted Edwards curve."
)
@@ -164,11 +165,11 @@ object Crypto {
val SPHINCS256_SHA256 = SignatureScheme(
5,
"SPHINCS-256_SHA512",
- AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, DLSequence(arrayOf(ASN1Integer(0), SHA512_256))),
+ AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, null),
listOf(AlgorithmIdentifier(BCObjectIdentifiers.sphincs256, DLSequence(arrayOf(ASN1Integer(0), SHA512_256)))),
bouncyCastlePQCProvider.name,
"SPHINCS256",
- "SHA512WITHSPHINCS256",
+ "SHA512withSPHINCS256",
SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256),
256,
"SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers " +
@@ -244,8 +245,9 @@ object Crypto {
@JvmStatic
fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme {
- return algorithmMap[normaliseAlgorithmIdentifier(algorithm)]
- ?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm.algorithm.id}")
+ return requireNotNull(algorithmMap[normaliseAlgorithmIdentifier(algorithm)]) {
+ "Unrecognised algorithm identifier: ${algorithm.algorithm} ${algorithm.parameters}"
+ }
}
/** Find [SignatureScheme] by platform specific schemeNumberID. */
@@ -307,12 +309,11 @@ object Crypto {
@JvmStatic
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
val keyInfo = PrivateKeyInfo.getInstance(encodedKey)
- if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) {
- return convertIfBCEdDSAPrivateKey(decodeAliasPrivateKey(keyInfo))
+ return if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) {
+ decodeAliasPrivateKey(keyInfo)
+ } else {
+ findSignatureScheme(keyInfo.privateKeyAlgorithm).keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
}
- val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm)
- val keyFactory = keyFactory(signatureScheme)
- return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)))
}
private fun decodeAliasPrivateKey(keyInfo: PrivateKeyInfo): PrivateKey {
@@ -351,8 +352,7 @@ object Crypto {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
try {
- val keyFactory = keyFactory(signatureScheme)
- return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)))
+ return signatureScheme.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
throw InvalidKeySpecException("This private key cannot be decoded, please ensure it is PKCS8 encoded and that " +
"it corresponds to the input scheme's code name.", ikse)
@@ -368,12 +368,11 @@ object Crypto {
*/
@JvmStatic
fun decodePublicKey(encodedKey: ByteArray): PublicKey {
- return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: {
+ return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: run {
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey)
val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm)
- val keyFactory = keyFactory(signatureScheme)
- convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
- }()
+ internPublicKey(signatureScheme.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
+ }
}
@JvmStatic
@@ -412,8 +411,7 @@ object Crypto {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
try {
- val keyFactory = keyFactory(signatureScheme)
- return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
+ return signatureScheme.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) {
throw InvalidKeySpecException("This public key cannot be decoded, please ensure it is X509 encoded and " +
"that it corresponds to the input scheme's code name.", ikse)
@@ -471,12 +469,8 @@ object Crypto {
return withSignature(signatureScheme) { signature ->
// Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require
// extra randomness, but we have to ensure that non-deterministic algorithms (i.e., ECDSA) use non-blocking
- // SecureRandom implementation. Also, SPHINCS-256 implementation in BouncyCastle 1.60 fails with
- // ClassCastException if we invoke initSign with a SecureRandom as an input.
- // TODO Although we handle the above issue here, consider updating to BC 1.61+ which provides a fix.
- if (signatureScheme == EDDSA_ED25519_SHA512
- || signatureScheme == SPHINCS256_SHA256
- || signatureScheme == RSA_SHA256) {
+ // SecureRandom implementation.
+ if (signatureScheme == EDDSA_ED25519_SHA512 || signatureScheme == SPHINCS256_SHA256 || signatureScheme == RSA_SHA256) {
signature.initSign(privateKey)
} else {
// The rest of the algorithms will require a SecureRandom input (i.e., ECDSA or any new algorithm for which
@@ -716,8 +710,7 @@ object Crypto {
* This operation is currently supported for ECDSA secp256r1 (NIST P-256), ECDSA secp256k1 and EdDSA ed25519.
*
* Similarly to BIP32, the implemented algorithm uses an HMAC function based on SHA512 and it is actually
- * an implementation the HKDF rfc - Step 1: Extract function,
- * @see HKDF
+ * an implementation of the [HKDF rfc - Step 1: Extract function](https://tools.ietf.org/html/rfc5869),
* which is practically a variation of the private-parent-key -> private-child-key hardened key generation of BIP32.
*
* Unlike BIP32, where both private and public keys are extended to prevent deterministically
@@ -725,8 +718,8 @@ object Crypto {
* without a chain-code and the generated key relies solely on the security of the private key.
*
* Although without a chain-code we lose the aforementioned property of not depending solely on the key,
- * it should be mentioned that the cryptographic strength of the HMAC depends upon the size of the secret key.
- * @see HMAC Security
+ * it should be mentioned that the cryptographic strength of the HMAC depends upon the size of the secret key
+ * (see [HMAC Security](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Security)).
* Thus, as long as the master key is kept secret and has enough entropy (~256 bits for EC-schemes), the system
* is considered secure.
*
@@ -743,9 +736,9 @@ object Crypto {
* salt values should not be chosen by an attacker.
*
*
- * Regarding the last requirement, according to Krawczyk's HKDF scheme: While there is no need to keep the salt secret,
- * it is assumed that salt values are independent of the input keying material.
- * @see Cryptographic Extraction and Key Derivation - The HKDF Scheme.
+ * Regarding the last requirement, according to Krawczyk's HKDF scheme: _While there is no need to keep the salt secret,
+ * it is assumed that salt values are independent of the input keying material_
+ * (see [Cryptographic Extraction and Key Derivation - The HKDF Scheme](http://eprint.iacr.org/2010/264.pdf)).
*
* There are also protocols that require an authenticated nonce (e.g. when a DH derived key is used as a seed) and thus
* we need to make sure that nonces come from legitimate parties rather than selected by an attacker.
@@ -845,13 +838,7 @@ object Crypto {
private fun deriveKeyPairEdDSA(privateKey: PrivateKey, seed: ByteArray): KeyPair {
// Compute HMAC(privateKey, seed).
val macBytes = deriveHMAC(privateKey, seed)
-
- // Calculate key pair.
- val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
- val bytes = macBytes.copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
- val privateKeyD = EdDSAPrivateKeySpec(bytes, params)
- val publicKeyD = EdDSAPublicKeySpec(privateKeyD.a, params)
- return KeyPair(internPublicKey(EdDSAPublicKey(publicKeyD)), EdDSAPrivateKey(privateKeyD))
+ return deriveEdDSAKeyPair(macBytes)
}
/**
@@ -882,15 +869,20 @@ object Crypto {
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// Custom key pair generator from entropy.
- // The BigIntenger.toByteArray() uses the two's-complement representation.
+ // The BigInteger.toByteArray() uses the two's-complement representation.
// The entropy is transformed to a byte array in big-endian byte-order and
// only the first ed25519.field.getb() / 8 bytes are used.
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
- val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
- val bytes = entropy.toByteArray().copyOf(params.curve.field.getb() / 8) // Need to pad the entropy to the valid seed length.
- val priv = EdDSAPrivateKeySpec(bytes, params)
- val pub = EdDSAPublicKeySpec(priv.a, params)
- return KeyPair(internPublicKey(EdDSAPublicKey(pub)), EdDSAPrivateKey(priv))
+ return deriveEdDSAKeyPair(entropy.toByteArray().copyOf(Ed25519.PUBLIC_KEY_SIZE))
+ }
+
+ private fun deriveEdDSAKeyPair(bytes: ByteArray): KeyPair {
+ val privateKeyParams = Ed25519PrivateKeyParameters(bytes, 0) // This will copy the first 256 bits
+ val encodedPrivateKey = PrivateKeyInfoFactory.createPrivateKeyInfo(privateKeyParams).encoded
+ val privateKey = EDDSA_ED25519_SHA512.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedPrivateKey))
+ val encodedPublicKey = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(privateKeyParams.generatePublicKey()).encoded
+ val publicKey = EDDSA_ED25519_SHA512.keyFactory.generatePublic(X509EncodedKeySpec(encodedPublicKey))
+ return KeyPair(internPublicKey(publicKey), privateKey)
}
// Custom key pair generator from an entropy required for various tests. It is similar to deriveKeyPairECDSA,
@@ -925,7 +917,7 @@ object Crypto {
val mac = Mac.getInstance("HmacSHA512", cordaBouncyCastleProvider)
val keyData = when (privateKey) {
is BCECPrivateKey -> privateKey.d.toByteArray()
- is EdDSAPrivateKey -> privateKey.geta()
+ is EdECPrivateKey -> privateKey.bytes.get()
else -> throw InvalidKeyException("Key type ${privateKey.algorithm} is not supported for deterministic key derivation")
}
val key = SecretKeySpec(keyData, "HmacSHA512")
@@ -936,12 +928,12 @@ object Crypto {
/**
* Check if a point's coordinates are on the expected curve to avoid certain types of ECC attacks.
* Point-at-infinity is not permitted as well.
- * @see Small subgroup and invalid-curve attacks for a more descriptive explanation on such attacks.
+ * See [Small subgroup and invalid-curve attacks](https://safecurves.cr.yp.to/twist.html) for a more descriptive explanation on such attacks.
* We use this function on [validatePublicKey], which is currently used for signature verification only.
* Thus, as these attacks are mostly not relevant to signature verification, we should note that
* we are doing it out of an abundance of caution and specifically to proactively protect developers
* against using these points as part of a DH key agreement or for use cases as yet unimagined.
- * This method currently applies to BouncyCastle's ECDSA (both R1 and K1 curves) and I2P's EdDSA (ed25519 curve).
+ * This method currently applies to BouncyCastle's ECDSA (both R1 and K1 curves) and JCA EdDSA (ed25519 curve).
* @param publicKey a [PublicKey], usually used to validate a signer's public key in on the Curve.
* @param signatureScheme a [SignatureScheme] object, retrieved from supported signature schemes, see [Crypto].
* @return true if the point lies on the curve or false if it doesn't.
@@ -954,17 +946,11 @@ object Crypto {
}
return when (publicKey) {
is BCECPublicKey -> publicKey.parameters == signatureScheme.algSpec && !publicKey.q.isInfinity && publicKey.q.isValid
- is EdDSAPublicKey -> publicKey.params == signatureScheme.algSpec && !isEdDSAPointAtInfinity(publicKey) && publicKey.a.isOnCurve
+ is EdECPublicKey -> signatureScheme == EDDSA_ED25519_SHA512 && publicKey.params.name.equals("Ed25519", ignoreCase = true) && publicKey.point.isOnCurve25519
else -> throw IllegalArgumentException("Unsupported key type: ${publicKey::class}")
}
}
- // Return true if EdDSA publicKey is point at infinity.
- // For EdDSA a custom function is required as it is not supported by the I2P implementation.
- private fun isEdDSAPointAtInfinity(publicKey: EdDSAPublicKey): Boolean {
- return publicKey.a.toP3() == (EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3)
- }
-
/** Check if the requested [SignatureScheme] is supported by the system. */
@JvmStatic
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean {
@@ -981,7 +967,7 @@ object Crypto {
// Check if a public key satisfies algorithm specs (for ECC: key should lie on the curve and not being point-at-infinity).
private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean {
return when (key) {
- is BCECPublicKey, is EdDSAPublicKey -> publicKeyOnCurve(signatureScheme, key)
+ is BCECPublicKey, is EdECPublicKey -> publicKeyOnCurve(signatureScheme, key)
is BCRSAPublicKey -> key.modulus.bitLength() >= 2048 // Although the recommended RSA key size is 3072, we accept any key >= 2048bits.
is BCSphincs256PublicKey -> true
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
@@ -991,21 +977,6 @@ object Crypto {
private val interner = PrivateInterner()
private fun internPublicKey(key: PublicKey): PublicKey = PublicKeyCache.cachePublicKey(interner.intern(key))
-
- private fun convertIfBCEdDSAPublicKey(key: PublicKey): PublicKey {
- return internPublicKey(when (key) {
- is BCEdDSAPublicKey -> EdDSAPublicKey(X509EncodedKeySpec(key.encoded))
- else -> key
- })
- }
-
- private fun convertIfBCEdDSAPrivateKey(key: PrivateKey): PrivateKey {
- return when (key) {
- is BCEdDSAPrivateKey -> EdDSAPrivateKey(PKCS8EncodedKeySpec(key.encoded))
- else -> key
- }
- }
-
/**
* Convert a public key to a supported implementation.
* @param key a public key.
@@ -1031,9 +1002,9 @@ object Crypto {
is BCECPublicKey -> internPublicKey(key)
is BCRSAPublicKey -> internPublicKey(key)
is BCSphincs256PublicKey -> internPublicKey(key)
- is EdDSAPublicKey -> internPublicKey(key)
+ is EdECPublicKey -> internPublicKey(key)
is CompositeKey -> internPublicKey(key)
- is BCEdDSAPublicKey -> convertIfBCEdDSAPublicKey(key)
+ is BCEdDSAPublicKey -> internPublicKey(key)
else -> decodePublicKey(key.encoded)
}
}
@@ -1052,8 +1023,8 @@ object Crypto {
is BCECPrivateKey -> key
is BCRSAPrivateKey -> key
is BCSphincs256PrivateKey -> key
- is EdDSAPrivateKey -> key
- is BCEdDSAPrivateKey -> convertIfBCEdDSAPrivateKey(key)
+ is EdECPrivateKey -> key
+ is BCEdDSAPrivateKey -> key
else -> decodePrivateKey(key.encoded)
}
}
@@ -1095,8 +1066,4 @@ object Crypto {
private fun setBouncyCastleRNG() {
CryptoServicesRegistrar.setSecureRandom(newSecureRandom())
}
-
- private fun keyFactory(signatureScheme: SignatureScheme) = signatureScheme.getKeyFactory {
- KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
- }
}
diff --git a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt
index 3fc649f989..797123bf4d 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/CryptoUtils.kt
@@ -3,7 +3,8 @@
package net.corda.core.crypto
import net.corda.core.contracts.PrivacySalt
-import net.corda.core.crypto.internal.platformSecureRandomFactory
+import net.corda.core.crypto.internal.PlatformSecureRandomService
+import net.corda.core.crypto.internal.cordaSecurityProvider
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
@@ -19,6 +20,7 @@ import java.security.PublicKey
import java.security.SecureRandom
import java.security.SecureRandomSpi
import java.security.SignatureException
+import kotlin.math.abs
/**
* Utility to simplify the act of signing a byte array.
@@ -231,7 +233,12 @@ object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null)
* which should never happen and suggests an unusual JVM or non-standard Java library.
*/
@Throws(NoSuchAlgorithmException::class)
-fun newSecureRandom(): SecureRandom = platformSecureRandomFactory()
+fun newSecureRandom(): SecureRandom = sharedSecureRandom
+
+// This is safe to share because of the underlying implementation of SecureRandomSpi
+private val sharedSecureRandom: SecureRandom by lazy(LazyThreadSafetyMode.PUBLICATION) {
+ SecureRandom.getInstance(PlatformSecureRandomService.ALGORITHM, cordaSecurityProvider)
+}
/**
* Returns a random positive non-zero long generated using a secure RNG. This function sacrifies a bit of entropy in order
@@ -239,7 +246,7 @@ fun newSecureRandom(): SecureRandom = platformSecureRandomFactory()
*/
fun random63BitValue(): Long {
while (true) {
- val candidate = Math.abs(newSecureRandom().nextLong())
+ val candidate = abs(newSecureRandom().nextLong())
// No need to check for -0L
if (candidate != 0L && candidate != Long.MIN_VALUE) {
return candidate
diff --git a/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt b/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt
index 27b3fc4750..885868873a 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/SignatureScheme.kt
@@ -1,5 +1,6 @@
package net.corda.core.crypto
+import net.corda.core.crypto.internal.providerMap
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import java.security.KeyFactory
import java.security.Signature
@@ -36,11 +37,6 @@ data class SignatureScheme(
@Volatile
private var memoizedKeyFactory: KeyFactory? = null
- internal fun getKeyFactory(factoryFactory: () -> KeyFactory): KeyFactory {
- return memoizedKeyFactory ?: run {
- val newFactory = factoryFactory()
- memoizedKeyFactory = newFactory
- newFactory
- }
- }
+ internal val keyFactory: KeyFactory
+ get() = memoizedKeyFactory ?: KeyFactory.getInstance(algorithmName, providerMap[providerName]).also { memoizedKeyFactory = it }
}
diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Curve25519.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Curve25519.kt
new file mode 100644
index 0000000000..7b5a7dd9f8
--- /dev/null
+++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Curve25519.kt
@@ -0,0 +1,46 @@
+
+package net.corda.core.crypto.internal
+
+import java.math.BigInteger
+import java.math.BigInteger.TWO
+import java.security.spec.EdECPoint
+
+/**
+ * Parameters for Curve25519, as defined in https://www.rfc-editor.org/rfc/rfc7748#section-4.1.
+ */
+@Suppress("MagicNumber")
+object Curve25519 {
+ val p = TWO.pow(255) - 19.toBigInteger() // 2^255 - 19
+ val d = ModP(BigInteger("37095705934669439343138083508754565189542113879843219016388785533085940283555"))
+
+ val EdECPoint.isOnCurve25519: Boolean
+ // https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.3
+ get() {
+ if (y >= p) return false
+ val ySquared = ModP(y).pow(TWO)
+ val u = ySquared - 1 // y^2 - 1 (mod p)
+ val v = d * ySquared + 1 // dy^2 + 1 (mod p)
+ val x = (u / v).pow((p + 3.toBigInteger()).shiftRight(3)) // (u/v)^((p+3)/8) (mod p)
+ val vxSquared = v * x.pow(TWO)
+ return vxSquared == u || vxSquared == -u
+ }
+
+ fun BigInteger.modP(): ModP = ModP(mod(p))
+
+ private fun BigInteger.additiveInverse(): BigInteger = p - this
+
+ data class ModP(val value: BigInteger) : Comparable {
+ fun pow(exponent: BigInteger): ModP = ModP(value.modPow(exponent, p))
+
+ operator fun unaryMinus(): ModP = ModP(value.additiveInverse())
+ operator fun plus(other: ModP): ModP = (this.value + other.value).modP()
+ operator fun plus(other: Int): ModP = (this.value + other.toBigInteger()).modP()
+ operator fun minus(other: ModP): ModP = (this.value + other.value.additiveInverse()).modP()
+ operator fun minus(other: Int): ModP = (this.value + other.toBigInteger().additiveInverse()).modP()
+ operator fun times(other: ModP): ModP = (this.value * other.value).modP()
+ operator fun div(other: ModP): ModP = (this.value * other.value.modInverse(p)).modP()
+
+ override fun compareTo(other: ModP): Int = this.value.compareTo(other.value)
+ override fun toString(): String = "$value (mod Curve25519 p)"
+ }
+}
diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt
index fc7336f855..64b5feef78 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Instances.kt
@@ -26,9 +26,8 @@ object Instances {
private val signatureFactory: SignatureFactory = CachingSignatureFactory()
// The provider itself is a very bad key class as hashCode() is expensive and contended. So use name and version instead.
- private data class SignatureKey(val algorithm: String, val providerName: String?, val providerVersion: Double?) {
- constructor(algorithm: String, provider: Provider?) : this(algorithm, provider?.name,
- @Suppress("DEPRECATION") provider?.version) // JDK11: should replace with getVersionStr() (since 9)
+ private data class SignatureKey(val algorithm: String, val providerName: String?, val providerVersion: String?) {
+ constructor(algorithm: String, provider: Provider?) : this(algorithm, provider?.name, provider?.versionStr)
}
private class CachingSignatureFactory : SignatureFactory {
diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt
index 6e94948c3b..1570c55f82 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/internal/PlatformSecureRandom.kt
@@ -2,8 +2,6 @@
package net.corda.core.crypto.internal
import io.netty.util.concurrent.FastThreadLocal
-import net.corda.core.crypto.DummySecureRandom
-import net.corda.core.utilities.SgxSupport
import net.corda.core.utilities.loggerFor
import org.apache.commons.lang3.SystemUtils
import java.io.DataInputStream
@@ -16,21 +14,8 @@ import java.security.SecureRandom
import java.security.SecureRandomSpi
import kotlin.system.exitProcess
-/**
- * This has been migrated into a separate class so that it
- * is easier to delete from the core-deterministic module.
- */
-val platformSecureRandom: () -> SecureRandom = when {
- SgxSupport.isInsideEnclave -> {
- { DummySecureRandom }
- }
- else -> {
- { sharedSecureRandom }
- }
-}
-
class PlatformSecureRandomService(provider: Provider)
- : Provider.Service(provider, "SecureRandom", ALGORITHM, PlatformSecureRandomSpi::javaClass.name, null, null) {
+ : Provider.Service(provider, "SecureRandom", ALGORITHM, PlatformSecureRandomSpi::class.java.name, null, null) {
companion object {
const val ALGORITHM = "CordaPRNG"
@@ -88,8 +73,3 @@ private class LinuxSecureRandomSpi : SecureRandomSpi() {
override fun engineGenerateSeed(numBytes: Int): ByteArray = ByteArray(numBytes).apply { engineNextBytes(this) }
}
-
-// This is safe to share because of the underlying implementation of SecureRandomSpi
-private val sharedSecureRandom: SecureRandom by lazy(LazyThreadSafetyMode.PUBLICATION) {
- SecureRandom.getInstance(PlatformSecureRandomService.ALGORITHM)
-}
diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt
index df19ab17b3..27bfcafb96 100644
--- a/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt
+++ b/core/src/main/kotlin/net/corda/core/crypto/internal/ProviderMap.kt
@@ -1,24 +1,17 @@
package net.corda.core.crypto.internal
import net.corda.core.crypto.CordaSecurityProvider
-import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
-import net.corda.core.crypto.Crypto.decodePrivateKey
-import net.corda.core.crypto.Crypto.decodePublicKey
-import net.corda.core.internal.X509EdDSAEngine
-import net.i2p.crypto.eddsa.EdDSAEngine
-import net.i2p.crypto.eddsa.EdDSASecurityProvider
-import org.bouncycastle.asn1.ASN1ObjectIdentifier
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
-import org.bouncycastle.jcajce.provider.asymmetric.ec.AlgorithmParametersSpi
-import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
import java.security.Provider
-import java.security.SecureRandom
import java.security.Security
import java.util.Collections.unmodifiableMap
+val sunEcProvider = checkNotNull(Security.getProvider("SunEC")).also {
+ // Insert Secp256k1SupportProvider just in-front of SunEC for adding back support for secp256k1
+ Security.insertProviderAt(Secp256k1SupportProvider(), Security.getProviders().indexOf(it))
+}
+
val cordaSecurityProvider = CordaSecurityProvider().also {
// Among the others, we should register [CordaSecurityProvider] as the first provider, to ensure that when invoking [SecureRandom()]
// the [platformSecureRandom] is returned (which is registered in CordaSecurityProvider).
@@ -29,40 +22,8 @@ val cordaSecurityProvider = CordaSecurityProvider().also {
Security.insertProviderAt(it, 1) // The position is 1-based.
}
-// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
-val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
-val cordaBouncyCastleProvider = BouncyCastleProvider().apply {
- putAll(EdDSASecurityProvider())
- // Override the normal EdDSA engine with one which can handle X509 keys.
- put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.java.name)
- put("Signature.Ed25519", X509EdDSAEngine::class.java.name)
- addKeyInfoConverter(`id-Curve25519ph`, object : AsymmetricKeyInfoConverter {
- override fun generatePublic(keyInfo: SubjectPublicKeyInfo) = decodePublicKey(EDDSA_ED25519_SHA512, keyInfo.encoded)
- override fun generatePrivate(keyInfo: PrivateKeyInfo) = decodePrivateKey(EDDSA_ED25519_SHA512, keyInfo.encoded)
- })
- // Required due to [X509CRL].verify() reported issues in network-services after BC 1.60 update.
- put("AlgorithmParameters.SHA256WITHECDSA", AlgorithmParametersSpi::class.java.name)
-}.also {
- // This registration is needed for reading back EdDSA key from java keystore.
- // TODO: Find a way to make JKS work with bouncy castle provider or implement our own provide so we don't have to register bouncy castle provider.
+val cordaBouncyCastleProvider = BouncyCastleProvider().also {
Security.addProvider(it)
-
- // Remove providers that class with bouncy castle
- val bcProviders = it.keys
-
- // JDK 17: Add SunEC provider as lowest priority, as we use Bouncycastle for EDDSA
- // and remove amy algorithms that conflict with Bouncycastle
- val sunEC = Security.getProvider("SunEC")
- if (sunEC != null) {
- Security.removeProvider("SunEC")
- Security.addProvider(sunEC)
-
- for(alg in sunEC.keys) {
- if (bcProviders.contains(alg)) {
- sunEC.remove(alg)
- }
- }
- }
}
val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
@@ -75,8 +36,6 @@ val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
// The val is immutable to avoid any harmful state changes.
internal val providerMap: Map = unmodifiableMap(
- listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider)
+ listOf(sunEcProvider, cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider)
.associateByTo(LinkedHashMap(), Provider::getName)
)
-
-fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source.
diff --git a/core/src/main/kotlin/net/corda/core/crypto/internal/Secp256k1SupportProvider.kt b/core/src/main/kotlin/net/corda/core/crypto/internal/Secp256k1SupportProvider.kt
new file mode 100644
index 0000000000..5f7f669b1b
--- /dev/null
+++ b/core/src/main/kotlin/net/corda/core/crypto/internal/Secp256k1SupportProvider.kt
@@ -0,0 +1,188 @@
+@file:Suppress("MagicNumber")
+
+package net.corda.core.crypto.internal
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider
+import java.math.BigInteger
+import java.math.BigInteger.ZERO
+import java.security.AlgorithmParameters
+import java.security.KeyPair
+import java.security.KeyPairGeneratorSpi
+import java.security.PrivateKey
+import java.security.Provider
+import java.security.PublicKey
+import java.security.SecureRandom
+import java.security.Signature
+import java.security.SignatureSpi
+import java.security.interfaces.ECPrivateKey
+import java.security.interfaces.ECPublicKey
+import java.security.spec.AlgorithmParameterSpec
+import java.security.spec.ECFieldFp
+import java.security.spec.ECParameterSpec
+import java.security.spec.ECPoint
+import java.security.spec.EllipticCurve
+import java.security.spec.NamedParameterSpec
+
+/**
+ * Augment the SunEC provider with secp256k1 curve support by delegating to [BouncyCastleProvider] when secp256k1 keys or params are
+ * requested. Otherwise delegates to SunEC.
+ */
+class Secp256k1SupportProvider : Provider("Secp256k1Support", "1.0", "Augmenting SunEC with support for the secp256k1 curve via BC") {
+ init {
+ put("Signature.SHA256withECDSA", Secp256k1SupportSignatureSpi::class.java.name)
+ put("KeyPairGenerator.EC", Secp256k1SupportKeyPairGeneratorSpi::class.java.name)
+ put("AlgorithmParameters.EC", "sun.security.util.ECParameters")
+ put("KeyFactory.EC", "sun.security.ec.ECKeyFactory")
+ }
+
+ class Secp256k1SupportSignatureSpi : SignatureSpi() {
+ private lateinit var sunEc: Signature
+ private lateinit var bc: Signature
+ private lateinit var selected: Signature
+
+ override fun engineInitVerify(publicKey: PublicKey?) {
+ selectProvider((publicKey as? ECPublicKey)?.params)
+ selected.initVerify(publicKey)
+ }
+
+ override fun engineInitSign(privateKey: PrivateKey?) {
+ selectProvider((privateKey as? ECPrivateKey)?.params)
+ selected.initSign(privateKey)
+ }
+
+ override fun engineSetParameter(params: AlgorithmParameterSpec?) {
+ selectProvider(params)
+ // The BC implementation throws UnsupportedOperationException, so we just avoid calling it.
+ if (selected !== bc) {
+ selected.setParameter(params)
+ }
+ }
+
+ private fun selectProvider(params: AlgorithmParameterSpec?) {
+ if (params.isSecp256k1) {
+ if (!::bc.isInitialized) {
+ bc = Signature.getInstance("SHA256withECDSA", cordaBouncyCastleProvider)
+ }
+ selected = bc
+ } else {
+ selectSunEc()
+ }
+ }
+
+ private fun selectSunEc() {
+ if (!::sunEc.isInitialized) {
+ sunEc = Signature.getInstance("SHA256withECDSA", sunEcProvider)
+ }
+ selected = sunEc
+ }
+
+ override fun engineUpdate(b: Byte) {
+ defaultToSunEc()
+ selected.update(b)
+ }
+
+ override fun engineUpdate(b: ByteArray?, off: Int, len: Int) {
+ defaultToSunEc()
+ selected.update(b, off, len)
+ }
+
+ override fun engineSign(): ByteArray {
+ defaultToSunEc()
+ return selected.sign()
+ }
+
+ override fun engineVerify(sigBytes: ByteArray?): Boolean {
+ defaultToSunEc()
+ return selected.verify(sigBytes)
+ }
+
+ override fun engineGetParameters(): AlgorithmParameters {
+ defaultToSunEc()
+ return selected.parameters
+ }
+
+ @Deprecated("Deprecated in Java")
+ @Suppress("DEPRECATION")
+ override fun engineSetParameter(param: String?, value: Any?) {
+ defaultToSunEc()
+ selected.setParameter(param, value)
+ }
+
+ @Deprecated("Deprecated in Java")
+ @Suppress("DEPRECATION")
+ override fun engineGetParameter(param: String?): Any {
+ defaultToSunEc()
+ return selected.getParameter(param)
+ }
+
+ private fun defaultToSunEc() {
+ // Even though it's probably a bug to start using the Signature object without first calling one of the intialize methods,
+ // default it to SunEC provider anyway and let it deal with the issue.
+ if (!::selected.isInitialized) {
+ selectSunEc()
+ }
+ }
+ }
+
+ class Secp256k1SupportKeyPairGeneratorSpi : KeyPairGeneratorSpi() {
+ // The methods in KeyPairGeneratorSpi are public, which allows us to directly call them. This is not the case with SignatureSpi (above).
+ private lateinit var sunEc: KeyPairGeneratorSpi
+ private lateinit var bc: KeyPairGeneratorSpi
+ private lateinit var selected: KeyPairGeneratorSpi
+
+ override fun initialize(keysize: Int, random: SecureRandom?) {
+ selectSunEc()
+ selected.initialize(keysize, random)
+ }
+
+ override fun initialize(params: AlgorithmParameterSpec?, random: SecureRandom?) {
+ if (params.isSecp256k1) {
+ if (!::bc.isInitialized) {
+ bc = org.bouncycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi.EC()
+ }
+ selected = bc
+ } else {
+ selectSunEc()
+ }
+ selected.initialize(params, random)
+ }
+
+ private fun selectSunEc() {
+ if (!::sunEc.isInitialized) {
+ sunEc = sunEcProvider.getService("KeyPairGenerator", "EC").newInstance(null) as KeyPairGeneratorSpi
+ }
+ selected = sunEc
+ }
+
+ override fun generateKeyPair(): KeyPair {
+ if (!::selected.isInitialized) {
+ // In-case initialize wasn't first called, default to SunEC
+ selectSunEc()
+ }
+ return selected.generateKeyPair()
+ }
+ }
+}
+
+/**
+ * Parameters for the secp256k1 curve
+ */
+private object Secp256k1 {
+ val n = BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16)
+ val g = ECPoint(
+ BigInteger("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 16),
+ BigInteger("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8", 16)
+ )
+ val curve = EllipticCurve(
+ ECFieldFp(BigInteger("fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 16)),
+ ZERO,
+ 7.toBigInteger()
+ )
+}
+
+val AlgorithmParameterSpec?.isSecp256k1: Boolean
+ get() = when (this) {
+ is ECParameterSpec -> cofactor == 1 && order == Secp256k1.n && curve == Secp256k1.curve && generator == Secp256k1.g
+ is NamedParameterSpec -> name.equals("secp256k1", ignoreCase = true)
+ else -> false
+ }
diff --git a/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt b/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt
index e552172844..b17f50e0c8 100644
--- a/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt
+++ b/core/src/main/kotlin/net/corda/core/internal/StatePointerSearch.kt
@@ -13,7 +13,7 @@ import java.util.*
class StatePointerSearch(val state: ContractState) {
private companion object {
// Classes in these packages should not be part of a search.
- private val blackListedPackages = setOf("java.", "javax.", "org.bouncycastle.", "net.i2p.crypto.")
+ private val blackListedPackages = setOf("java.", "javax.", "org.bouncycastle.")
}
// Type required for traversal.
diff --git a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt b/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt
deleted file mode 100644
index 94c4897da8..0000000000
--- a/core/src/main/kotlin/net/corda/core/internal/X509EdDSAEngine.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package net.corda.core.internal
-
-import net.corda.core.crypto.Crypto
-import net.i2p.crypto.eddsa.EdDSAEngine
-import net.i2p.crypto.eddsa.EdDSAPublicKey
-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.X509EncodedKeySpec
-
-/**
- * Wrapper around [EdDSAEngine] which can intelligently rewrite X509Keys to a [EdDSAPublicKey]. This is a temporary
- * solution until this is integrated upstream and/or a custom certificate factory implemented to force the correct
- * key type. Only intercepts public keys passed into [engineInitVerify], as there is no equivalent issue with private
- * keys.
- */
-class X509EdDSAEngine : Signature {
- private val engine: EdDSAEngine
-
- constructor() : super(EdDSAEngine.SIGNATURE_ALGORITHM) {
- engine = EdDSAEngine()
- }
-
- constructor(digest: MessageDigest) : super(EdDSAEngine.SIGNATURE_ALGORITHM) {
- engine = EdDSAEngine(digest)
- }
-
- override fun engineInitSign(privateKey: PrivateKey) = engine.initSign(privateKey)
-
- override fun engineInitSign(privateKey: PrivateKey, random: SecureRandom) = engine.initSign(privateKey, random)
-
- override fun engineInitVerify(publicKey: PublicKey) {
- val parsedKey = try {
- publicKey as? EdDSAPublicKey ?: EdDSAPublicKey(X509EncodedKeySpec(Crypto.encodePublicKey(publicKey)))
- } catch (e: Exception) {
- throw (InvalidKeyException(e.message))
- }
- engine.initVerify(parsedKey)
- }
-
- override fun engineSign(): ByteArray = engine.sign()
- override fun engineVerify(sigBytes: ByteArray): Boolean = engine.verify(sigBytes)
-
- override fun engineUpdate(b: Byte) = engine.update(b)
- override fun engineUpdate(b: ByteArray, off: Int, len: Int) = engine.update(b, off, len)
-
- override fun engineGetParameters(): AlgorithmParameters = engine.parameters
- override fun engineSetParameter(params: AlgorithmParameterSpec) = engine.setParameter(params)
- @Suppress("DEPRECATION", "OverridingDeprecatedMember")
- override fun engineGetParameter(param: String): Any = engine.getParameter(param)
-
- @Suppress("DEPRECATION", "OverridingDeprecatedMember")
- override fun engineSetParameter(param: String, value: Any?) = engine.setParameter(param, value)
-}
diff --git a/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt b/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt
deleted file mode 100644
index b4691cb1e0..0000000000
--- a/core/src/main/kotlin/net/corda/core/utilities/SgxSupport.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.corda.core.utilities
-
-object SgxSupport {
- @JvmStatic
- val isInsideEnclave: Boolean by lazy {
- (System.getProperty("os.name") == "Linux") && (System.getProperty("java.vm.name") == "Avian (Corda)")
- }
-}
diff --git a/core/src/test/java/net/corda/core/internal/X509EdDSAEngineTest.java b/core/src/test/java/net/corda/core/internal/X509EdDSAEngineTest.java
deleted file mode 100644
index e353db8043..0000000000
--- a/core/src/test/java/net/corda/core/internal/X509EdDSAEngineTest.java
+++ /dev/null
@@ -1,135 +0,0 @@
-package net.corda.core.internal;
-
-import net.corda.core.crypto.Crypto;
-import net.i2p.crypto.eddsa.EdDSAEngine;
-import net.i2p.crypto.eddsa.EdDSAPublicKey;
-import org.junit.Test;
-import sun.security.util.BitArray;
-import sun.security.util.ObjectIdentifier;
-import sun.security.x509.AlgorithmId;
-import sun.security.x509.X509Key;
-
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.KeyPair;
-import java.security.SignatureException;
-import java.util.Random;
-
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.junit.Assert.assertTrue;
-
-/**
- * JDK11 upgrade: rewritten in Java to gain access to private internal JDK classes via module directives (not available to Kotlin compiler):
- * import sun.security.util.BitArray;
- * import sun.security.util.ObjectIdentifier;
- * import sun.security.x509.AlgorithmId;
- * import sun.security.x509.X509Key;
- */
-public class X509EdDSAEngineTest {
- private static final long SEED = 20170920L;
- private static final int TEST_DATA_SIZE = 2000;
-
- // offset into an EdDSA header indicating where the key header and actual key start
- // in the underlying byte array
- private static final int KEY_HEADER_START = 9;
- private static final int KEY_START = 12;
-
- private X509Key toX509Key(EdDSAPublicKey publicKey) throws IOException, InvalidKeyException {
- byte[] internals = publicKey.getEncoded();
-
- // key size in the header includes the count unused bits at the end of the key
- // [keyHeaderStart + 2] but NOT the key header ID [keyHeaderStart] so the
- // actual length of the key blob is size - 1
- int keySize = (internals[KEY_HEADER_START + 1]) - 1;
-
- byte[] key = new byte[keySize];
- System.arraycopy(internals, KEY_START, key, 0, keySize);
-
- // 1.3.101.102 is the EdDSA OID
- return new TestX509Key(new AlgorithmId(ObjectIdentifier.of("1.3.101.112")), new BitArray(keySize * 8, key));
- }
-
- private static class TestX509Key extends X509Key {
- TestX509Key(AlgorithmId algorithmId, BitArray key) throws InvalidKeyException {
- this.algid = algorithmId;
- this.setKey(key);
- this.encode();
- }
- }
-
- /**
- * Put the X509EdDSA engine through basic tests to verify that the functions are hooked up correctly.
- */
- @Test
- public void SignAndVerify() throws InvalidKeyException, SignatureException {
- X509EdDSAEngine engine = new X509EdDSAEngine();
- KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED));
- EdDSAPublicKey publicKey = (EdDSAPublicKey) keyPair.getPublic();
- byte[] randomBytes = new byte[TEST_DATA_SIZE];
- new Random(SEED).nextBytes(randomBytes);
- engine.initSign(keyPair.getPrivate());
- engine.update(randomBytes[0]);
- engine.update(randomBytes, 1, randomBytes.length - 1);
-
- // Now verify the signature
- byte[] signature = engine.sign();
-
- engine.initVerify(publicKey);
- engine.update(randomBytes);
- assertTrue(engine.verify(signature));
- }
-
- /**
- * Verify that signing with an X509Key wrapped EdDSA key works.
- */
- @Test
- public void SignAndVerifyWithX509Key() throws InvalidKeyException, SignatureException, IOException {
- X509EdDSAEngine engine = new X509EdDSAEngine();
- KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1));
- X509Key publicKey = toX509Key((EdDSAPublicKey) keyPair.getPublic());
- byte[] randomBytes = new byte[TEST_DATA_SIZE];
- new Random(SEED + 1).nextBytes(randomBytes);
- engine.initSign(keyPair.getPrivate());
- engine.update(randomBytes[0]);
- engine.update(randomBytes, 1, randomBytes.length - 1);
-
- // Now verify the signature
- byte[] signature = engine.sign();
-
- engine.initVerify(publicKey);
- engine.update(randomBytes);
- assertTrue(engine.verify(signature));
- }
-
- /**
- * Verify that signing with an X509Key wrapped EdDSA key succeeds when using the underlying EdDSAEngine.
- */
- @Test
- public void SignAndVerifyWithX509KeyAndOldEngineFails() throws InvalidKeyException, SignatureException, IOException {
- X509EdDSAEngine engine = new X509EdDSAEngine();
- KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.EDDSA_ED25519_SHA512, BigInteger.valueOf(SEED + 1));
- X509Key publicKey = toX509Key((EdDSAPublicKey) keyPair.getPublic());
- byte[] randomBytes = new byte[TEST_DATA_SIZE];
- new Random(SEED + 1).nextBytes(randomBytes);
- engine.initSign(keyPair.getPrivate());
- engine.update(randomBytes[0]);
- engine.update(randomBytes, 1, randomBytes.length - 1);
-
- // Now verify the signature
- byte[] signature = engine.sign();
- engine.initVerify(publicKey);
- engine.update(randomBytes);
- engine.verify(signature);
- }
-
- /** Verify will fail if the input public key cannot be converted to EdDSA public key. */
- @Test
- public void verifyWithNonSupportedKeyTypeFails() {
- EdDSAEngine engine = new EdDSAEngine();
- KeyPair keyPair = Crypto.deriveKeyPairFromEntropy(Crypto.ECDSA_SECP256K1_SHA256, BigInteger.valueOf(SEED));
- assertThatExceptionOfType(InvalidKeyException.class).isThrownBy(() ->
- engine.initVerify(keyPair.getPublic())
- );
- }
-}
diff --git a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt
index b011d029c6..d5c125b7bb 100644
--- a/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt
+++ b/core/src/test/kotlin/net/corda/core/crypto/CryptoUtilsTest.kt
@@ -8,15 +8,10 @@ import net.corda.core.crypto.Crypto.RSA_SHA256
import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
import net.corda.core.crypto.internal.PlatformSecureRandomService
import net.corda.core.utilities.OpaqueBytes
-import net.i2p.crypto.eddsa.EdDSAKey
-import net.i2p.crypto.eddsa.EdDSAPrivateKey
-import net.i2p.crypto.eddsa.EdDSAPublicKey
-import net.i2p.crypto.eddsa.math.GroupElement
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
-import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.apache.commons.lang3.ArrayUtils.EMPTY_BYTE_ARRAY
+import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatIllegalArgumentException
+import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
@@ -24,15 +19,18 @@ import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
+import org.bouncycastle.math.ec.rfc8032.Ed25519
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals
-import org.junit.Ignore
import org.junit.Test
import java.math.BigInteger
import java.security.KeyPairGenerator
import java.security.SecureRandom
import java.security.Security
+import java.security.interfaces.EdECPrivateKey
+import java.security.interfaces.EdECPublicKey
+import java.security.spec.NamedParameterSpec
import java.util.Random
import kotlin.test.assertEquals
import kotlin.test.assertFalse
@@ -132,11 +130,8 @@ class CryptoUtilsTest {
// test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc()
- try {
+ assertThatThrownBy {
Crypto.doVerify(pubKey, signedData, testBytes)
- fail()
- } catch (e: Exception) {
- // expected
}
}
@@ -498,9 +493,9 @@ class CryptoUtilsTest {
val (privEd, pubEd) = keyPairEd
assertEquals(privEd.algorithm, "EdDSA")
- assertEquals((privEd as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
+ assertEquals((privEd as EdECPrivateKey).params.name, NamedParameterSpec.ED25519.name)
assertEquals(pubEd.algorithm, "EdDSA")
- assertEquals((pubEd as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
+ assertEquals((pubEd as EdECPublicKey).params.name, NamedParameterSpec.ED25519.name)
}
@Test(timeout=300_000)
@@ -659,18 +654,23 @@ class CryptoUtilsTest {
@Test(timeout=300_000)
fun `Check EdDSA public key on curve`() {
- val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
- val pubEdDSA = keyPairEdDSA.public
- assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA))
- // Use R1 curve for check.
- assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA))
- // Check for point at infinity.
- val pubKeySpec = EdDSAPublicKeySpec((EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3), EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec)
- assertFalse(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, EdDSAPublicKey(pubKeySpec)))
+ repeat(100) {
+ val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
+ val pubEdDSA = keyPairEdDSA.public
+ assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA))
+ // Use R1 curve for check.
+ assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA))
+ }
+ val invalidKey = run {
+ val bytes = ByteArray(Ed25519.PUBLIC_KEY_SIZE).also { it[0] = 2 }
+ val encoded = SubjectPublicKeyInfo(EDDSA_ED25519_SHA512.signatureOID, bytes).encoded
+ Crypto.decodePublicKey(encoded)
+ }
+ assertThat(invalidKey).isInstanceOf(EdECPublicKey::class.java)
+ assertThat(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, invalidKey)).isFalse()
}
@Test(timeout = 300_000)
- @Ignore("TODO JDK17: Fixme")
fun `Unsupported EC public key type on curve`() {
val keyGen = KeyPairGenerator.getInstance("EC") // sun.security.ec.ECPublicKeyImpl
keyGen.initialize(256, newSecureRandom())
@@ -772,10 +772,8 @@ class CryptoUtilsTest {
// Check scheme.
assertEquals(priv.algorithm, dpriv.algorithm)
assertEquals(pub.algorithm, dpub.algorithm)
- assertTrue(dpriv is EdDSAPrivateKey)
- assertTrue(dpub is EdDSAPublicKey)
- assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
- assertEquals((dpub as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
+ assertEquals((dpriv as EdECPrivateKey).params.name, NamedParameterSpec.ED25519.name)
+ assertEquals((dpub as EdECPublicKey).params.name, NamedParameterSpec.ED25519.name)
assertEquals(Crypto.findSignatureScheme(dpriv), EDDSA_ED25519_SHA512)
assertEquals(Crypto.findSignatureScheme(dpub), EDDSA_ED25519_SHA512)
diff --git a/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt b/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt
index 6a30e5e2d6..666d2b7ce0 100644
--- a/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt
+++ b/core/src/test/kotlin/net/corda/core/crypto/EdDSATests.kt
@@ -1,17 +1,16 @@
package net.corda.core.crypto
+import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
+import net.corda.core.crypto.internal.Instances.withSignature
import net.corda.core.utilities.hexToByteArray
import net.corda.core.utilities.toHex
-import net.i2p.crypto.eddsa.EdDSAPrivateKey
-import net.i2p.crypto.eddsa.EdDSASecurityProvider
-import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
-import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
+import org.assertj.core.api.Assertions.assertThat
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.junit.Test
import java.security.PrivateKey
-import java.security.Signature
-import java.util.Locale
-import kotlin.test.assertEquals
-import kotlin.test.assertNotEquals
+import java.security.spec.EdECPrivateKeySpec
+import java.security.spec.NamedParameterSpec
+import java.security.spec.X509EncodedKeySpec
/**
* Testing PureEdDSA Ed25519 using test vectors from https://tools.ietf.org/html/rfc8032#section-7.1
@@ -19,8 +18,6 @@ import kotlin.test.assertNotEquals
class EdDSATests {
@Test(timeout=300_000)
fun `PureEdDSA Ed25519 test vectors`() {
- val edParams = Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
-
// MESSAGE (length 0 bytes).
val testVector1 = SignatureTestVector(
"9d61b19deffd5a60ba844af492ec2cc4" +
@@ -152,11 +149,24 @@ class EdDSATests {
"3dca179c138ac17ad9bef1177331a704"
)
+ val keyFactory = EDDSA_ED25519_SHA512.keyFactory
+
val testVectors = listOf(testVector1, testVector2, testVector3, testVector1024, testVectorSHAabc)
- testVectors.forEach {
- val privateKey = EdDSAPrivateKey(EdDSAPrivateKeySpec(it.privateKeyHex.hexToByteArray(), edParams))
- assertEquals(it.signatureOutputHex, doSign(privateKey, it.messageToSignHex.hexToByteArray()).toHex()
- .lowercase(Locale.getDefault()))
+ testVectors.forEach { testVector ->
+ val messageBytes = testVector.messageToSignHex.hexToByteArray()
+ val signatureBytes = testVector.signatureOutputHex.hexToByteArray()
+ // Check the private key produces the expected signature
+ val privateKey = keyFactory.generatePrivate(EdECPrivateKeySpec(NamedParameterSpec.ED25519, testVector.privateKeyHex.hexToByteArray()))
+ assertThat(doSign(privateKey, messageBytes)).isEqualTo(signatureBytes)
+ // Check the public key verifies the signature
+ val result = withSignature(EDDSA_ED25519_SHA512) { signature ->
+ val publicKeyInfo = SubjectPublicKeyInfo(EDDSA_ED25519_SHA512.signatureOID, testVector.publicKeyHex.hexToByteArray())
+ val publicKey = keyFactory.generatePublic(X509EncodedKeySpec(publicKeyInfo.encoded))
+ signature.initVerify(publicKey)
+ signature.update(messageBytes)
+ signature.verify(signatureBytes)
+ }
+ assertThat(result).isTrue()
}
// Test vector for the variant Ed25519ctx, expected to fail.
@@ -172,9 +182,8 @@ class EdDSATests {
"5a5ca2df6668346291c2043d4eb3e90d"
)
- val privateKey = EdDSAPrivateKey(EdDSAPrivateKeySpec(testVectorEd25519ctx.privateKeyHex.hexToByteArray(), edParams))
- assertNotEquals(testVectorEd25519ctx.signatureOutputHex, doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex()
- .lowercase(Locale.getDefault()))
+ val privateKey = keyFactory.generatePrivate(EdECPrivateKeySpec(NamedParameterSpec.ED25519, testVectorEd25519ctx.privateKeyHex.hexToByteArray()))
+ assertThat(doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex().lowercase()).isNotEqualTo(testVectorEd25519ctx.signatureOutputHex)
}
/** A test vector object for digital signature schemes. */
@@ -185,9 +194,10 @@ class EdDSATests {
// Required to implement a custom doSign function, because Corda's Crypto.doSign does not allow empty messages (testVector1).
private fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray {
- val signature = Signature.getInstance(Crypto.EDDSA_ED25519_SHA512.signatureName, EdDSASecurityProvider())
- signature.initSign(privateKey)
- signature.update(clearData)
- return signature.sign()
+ return withSignature(EDDSA_ED25519_SHA512) { signature ->
+ signature.initSign(privateKey)
+ signature.update(clearData)
+ signature.sign()
+ }
}
}
diff --git a/detekt-baseline.xml b/detekt-baseline.xml
index 01a15a1059..d3fdd92468 100644
--- a/detekt-baseline.xml
+++ b/detekt-baseline.xml
@@ -1426,7 +1426,6 @@
TooManyFunctions:ActionExecutorImpl.kt$ActionExecutorImpl : ActionExecutor
TooManyFunctions:AppendOnlyPersistentMap.kt$AppendOnlyPersistentMapBase<K, V, E, out EK>
TooManyFunctions:ArtemisTcpTransport.kt$ArtemisTcpTransport$Companion
- TooManyFunctions:BCCryptoService.kt$BCCryptoService : CryptoService
TooManyFunctions:BFTSmart.kt$BFTSmart$Replica : DefaultRecoverable
TooManyFunctions:BaseTransaction.kt$BaseTransaction : NamedByHash
TooManyFunctions:ClassCarpenter.kt$ClassCarpenterImpl : ClassCarpenter
@@ -1669,9 +1668,6 @@
WildcardImport:AttachmentsClassLoader.kt$import net.corda.core.serialization.*
WildcardImport:AttachmentsClassLoaderStaticContractTests.kt$import net.corda.core.contracts.*
WildcardImport:AutoOfferFlow.kt$import net.corda.core.flows.*
- WildcardImport:BCCryptoService.kt$import java.security.*
- WildcardImport:BCCryptoService.kt$import net.corda.nodeapi.internal.cryptoservice.*
- WildcardImport:BCCryptoServiceTests.kt$import java.security.*
WildcardImport:BFTNotaryServiceTests.kt$import net.corda.core.crypto.*
WildcardImport:BFTNotaryServiceTests.kt$import net.corda.testing.node.internal.*
WildcardImport:BFTSmart.kt$import net.corda.core.crypto.*
diff --git a/node-api-tests/build.gradle b/node-api-tests/build.gradle
index a13fc4d9d6..3ac3d4d775 100644
--- a/node-api-tests/build.gradle
+++ b/node-api-tests/build.gradle
@@ -15,7 +15,6 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_version"
- testImplementation "net.i2p.crypto:eddsa:$eddsa_version"
testImplementation "com.typesafe:config:$typesafe_config_version"
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
testImplementation "co.paralleluniverse:quasar-core:$quasar_version"
diff --git a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt
index 356be174eb..b8d84467cd 100644
--- a/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt
+++ b/node-api-tests/src/test/kotlin/net/corda/nodeapitests/internal/crypto/X509UtilitiesTest.kt
@@ -1,6 +1,5 @@
package net.corda.nodeapitests.internal.crypto
-
import io.netty.handler.ssl.ClientAuth
import io.netty.handler.ssl.SslContextBuilder
import io.netty.handler.ssl.SslProvider
@@ -52,7 +51,6 @@ import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.TestIdentity
import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.internal.createDevIntermediateCaCertPath
-import net.i2p.crypto.eddsa.EdDSAPrivateKey
import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier
import org.bouncycastle.asn1.x509.BasicConstraints
@@ -60,9 +58,7 @@ import org.bouncycastle.asn1.x509.CRLDistPoint
import org.bouncycastle.asn1.x509.Extension
import org.bouncycastle.asn1.x509.KeyUsage
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
-import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@@ -77,7 +73,8 @@ import java.security.KeyPair
import java.security.PrivateKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
-import java.util.*
+import java.security.interfaces.EdECPrivateKey
+import java.util.Date
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLParameters
import javax.net.ssl.SSLServerSocket
@@ -93,7 +90,6 @@ import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.test.fail
-@Ignore("TODO JDK17: Fixme")
class X509UtilitiesTest {
private companion object {
val ALICE = TestIdentity(ALICE_NAME, 70).party
@@ -122,9 +118,9 @@ class X509UtilitiesTest {
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),
+ 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, EdECPrivateKey::class.java, EdECPrivateKey::class.java),
// By default, JKS returns SUN RSA key.
Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java)
)
@@ -136,8 +132,7 @@ class X509UtilitiesTest {
@Test(timeout=300_000)
fun `create valid self-signed CA certificate`() {
- Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY
- && ( it != SPHINCS256_SHA256)}.forEach { validSelfSignedCertificate(it) }
+ Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { validSelfSignedCertificate(it) }
}
private fun validSelfSignedCertificate(signatureScheme: SignatureScheme) {
@@ -158,7 +153,7 @@ class X509UtilitiesTest {
@Test(timeout=300_000)
fun `load and save a PEM file certificate`() {
- Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { loadSavePEMCert(it) }
+ Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach(::loadSavePEMCert)
}
private fun loadSavePEMCert(signatureScheme: SignatureScheme) {
@@ -172,8 +167,7 @@ class X509UtilitiesTest {
@Test(timeout=300_000)
fun `create valid server certificate chain`() {
- certChainSchemeCombinations.filter{ it.first != SPHINCS256_SHA256 }
- .forEach { createValidServerCertChain(it.first, it.second) }
+ certChainSchemeCombinations.forEach { createValidServerCertChain(it.first, it.second) }
}
private fun createValidServerCertChain(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) {
@@ -451,13 +445,11 @@ class X509UtilitiesTest {
schemeToKeyTypes.forEach { getCorrectKeyFromKeystore(it.first, it.second, it.third) }
}
- private fun getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, uncastedClass: Class, castedClass: Class) {
+ private fun getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, rawClass: Class, supportedClass: Class) {
val keyPair = generateKeyPair(signatureScheme)
- val (keyFromKeystore, keyFromKeystoreCasted) = storeAndGetKeysFromKeystore(keyPair)
- if (uncastedClass == EdDSAPrivateKey::class.java && keyFromKeystore !is BCEdDSAPrivateKey) {
- assertThat(keyFromKeystore).isInstanceOf(uncastedClass)
- }
- assertThat(keyFromKeystoreCasted).isInstanceOf(castedClass)
+ val (rawKey, supportedKey) = storeAndGetKeysFromKeystore(keyPair)
+ assertThat(rawKey).isInstanceOf(rawClass)
+ assertThat(supportedKey).isInstanceOf(supportedClass)
}
private fun storeAndGetKeysFromKeystore(keyPair: KeyPair): Pair {
@@ -466,9 +458,9 @@ class X509UtilitiesTest {
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert))
- val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
- val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
- return Pair(keyFromKeystore, keyFromKeystoreCasted)
+ val rawKey = keyStore.getKey("Key", "keypassword".toCharArray())
+ val supportedKey = keyStore.getSupportedKey("Key", "keypassword")
+ return Pair(rawKey, supportedKey)
}
@Test(timeout=300_000)
diff --git a/node-api/build.gradle b/node-api/build.gradle
index 8b7dbdea93..79e04a203c 100644
--- a/node-api/build.gradle
+++ b/node-api/build.gradle
@@ -57,7 +57,6 @@ dependencies {
implementation "io.reactivex:rxjava:$rxjava_version"
implementation "javax.persistence:javax.persistence-api:2.2"
implementation "org.hibernate:hibernate-core:$hibernate_version"
- implementation "net.i2p.crypto:eddsa:$eddsa_version"
implementation "co.paralleluniverse:quasar-osgi-annotations:$quasar_version"
runtimeOnly 'com.mattbertolini:liquibase-slf4j:2.0.0'
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt
index 92980beae1..b50e9d8fa3 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/config/CertificateStore.kt
@@ -78,7 +78,7 @@ interface CertificateStore : Iterable> {
}
fun setCertPathOnly(alias: String, certificates: List) {
- // In case CryptoService and CertificateStore share the same KeyStore (i.e., when BCCryptoService is used),
+ // In case CryptoService and CertificateStore share the same KeyStore (i.e., when DefaultCryptoService is used),
// extract the existing key from the Keystore and store it again along with the new certificate chain.
// This is because KeyStores do not support updateKeyEntry and thus we cannot update the certificate chain
// without overriding the key entry.
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt
index bbee9e5d2a..a84fb0ad08 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilder.kt
@@ -1,6 +1,5 @@
package net.corda.nodeapi.internal.crypto
-import net.corda.core.crypto.Crypto.SPHINCS256_SHA256
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.internal.Instances
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
@@ -27,9 +26,7 @@ object ContentSignerBuilder {
val sig = try {
signatureInstance.apply {
- // TODO special handling for Sphincs due to a known BouncyCastle's Sphincs bug we reported.
- // It is fixed in BC 161b12, so consider updating the below if-statement after updating BouncyCastle.
- if (random != null && signatureScheme != SPHINCS256_SHA256) {
+ if (random != null) {
initSign(privateKey, random)
} else {
initSign(privateKey)
@@ -48,7 +45,7 @@ object ContentSignerBuilder {
private class SignatureOutputStream(private val sig: Signature, private val optimised: Boolean) : OutputStream() {
private var alreadySigned = false
- internal val signature: ByteArray by lazy {
+ val signature: ByteArray by lazy {
try {
alreadySigned = true
sig.sign()
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt
index c4d062dfff..c9e17b9773 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/crypto/X509Utilities.kt
@@ -69,7 +69,7 @@ import kotlin.io.path.reader
import kotlin.io.path.writer
object X509Utilities {
- // Note that this default value only applies to BCCryptoService. Other implementations of CryptoService may have to use different
+ // Note that this default value only applies to DefaultCryptoService. Other implementations of CryptoService may have to use different
// schemes (for instance `UtimacoCryptoService.DEFAULT_IDENTITY_SIGNATURE_SCHEME`).
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256
@@ -303,10 +303,10 @@ object X509Utilities {
crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509Certificate {
val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer)
- return builder.build(issuerSigner).run {
- require(isValidOn(Date())){"Certificate is not valid at instant now"}
- toJca()
- }
+ val certificate = builder.build(issuerSigner).toJca()
+ certificate.checkValidity(Date())
+ certificate.verify(issuerPublicKey)
+ return certificate
}
/**
@@ -340,18 +340,22 @@ object X509Utilities {
validityWindow,
nameConstraints,
crlDistPoint,
- crlIssuer)
- return builder.build(signer).run {
- require(isValidOn(Date())){"Certificate is not valid at instant now"}
- require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))){"Invalid signature"}
- toJca()
- }
+ crlIssuer
+ )
+ val certificate = builder.build(signer).toJca()
+ certificate.checkValidity(Date())
+ certificate.verify(issuerKeyPair.public)
+ return certificate
}
/**
* Create certificate signing request using provided information.
*/
- fun createCertificateSigningRequest(subject: X500Principal, email: String, publicKey: PublicKey, contentSigner: ContentSigner, certRole: CertRole = CertRole.NODE_CA): PKCS10CertificationRequest {
+ fun createCertificateSigningRequest(subject: X500Principal,
+ email: String,
+ publicKey: PublicKey,
+ contentSigner: ContentSigner,
+ certRole: CertRole = CertRole.NODE_CA): PKCS10CertificationRequest {
return JcaPKCS10CertificationRequestBuilder(subject, publicKey)
.addAttribute(BCStyle.E, DERUTF8String(email))
.addAttribute(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), certRole)
@@ -410,7 +414,7 @@ object X509Utilities {
}
// Assuming cert type to role is 1:1
-val CertRole.certificateType: CertificateType get() = CertificateType.values().first { it.role == this }
+val CertRole.certificateType: CertificateType get() = CertificateType.entries.first { it.role == this }
/**
* Convert a [X509Certificate] into BouncyCastle's [X509CertificateHolder].
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoService.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoService.kt
similarity index 87%
rename from node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoService.kt
rename to node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoService.kt
index d57f750b96..f48cbe0fb1 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoService.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoService.kt
@@ -1,9 +1,8 @@
-package net.corda.nodeapi.internal.cryptoservice.bouncycastle
+package net.corda.nodeapi.internal.cryptoservice
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
-import net.corda.core.crypto.internal.Instances.getSignatureInstance
-import net.corda.core.crypto.internal.cordaBouncyCastleProvider
+import net.corda.core.crypto.internal.Instances.withSignature
import net.corda.core.crypto.newSecureRandom
import net.corda.core.crypto.sha256
import net.corda.core.utilities.detailedLogger
@@ -14,27 +13,30 @@ import net.corda.nodeapi.internal.crypto.ContentSignerBuilder
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
import net.corda.nodeapi.internal.crypto.save
-import net.corda.nodeapi.internal.cryptoservice.*
-import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.CryptoServiceException
import org.bouncycastle.operator.ContentSigner
import java.nio.file.Path
-import java.security.*
+import java.security.KeyPair
+import java.security.KeyPairGenerator
+import java.security.KeyStore
+import java.security.PrivateKey
+import java.security.Provider
+import java.security.PublicKey
+import java.security.Signature
import java.security.spec.ECGenParameterSpec
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.security.auth.x500.X500Principal
/**
- * Basic implementation of a [CryptoService] that uses BouncyCastle for cryptographic operations
+ * Basic implementation of a [CryptoService] which uses Corda's [Provider]s for cryptographic operations
* and a Java KeyStore in the form of [CertificateStore] to store private keys.
- * This service reuses the [NodeConfiguration.signingCertificateStore] to store keys.
*
* The [wrappingKeyStorePath] must be provided in order to execute any wrapping operations (e.g. [createWrappingKey], [generateWrappedKeyPair])
*/
-class BCCryptoService(private val legalName: X500Principal,
- private val certificateStoreSupplier: CertificateStoreSupplier,
- private val wrappingKeyStorePath: Path? = null) : CryptoService {
+@Suppress("TooManyFunctions")
+class DefaultCryptoService(private val legalName: X500Principal,
+ private val certificateStoreSupplier: CertificateStoreSupplier,
+ private val wrappingKeyStorePath: Path? = null) : CryptoService {
private companion object {
val detailedLogger = detailedLogger()
@@ -97,7 +99,7 @@ class BCCryptoService(private val legalName: X500Principal,
private fun signWithAlgorithm(alias: String, data: ByteArray, signAlgorithm: String): ByteArray {
val privateKey = certificateStore.query { getPrivateKey(alias, certificateStore.entryPassword) }
- val signature = Signature.getInstance(signAlgorithm, cordaBouncyCastleProvider)
+ val signature = Signature.getInstance(signAlgorithm)
detailedLogger.trace { "CryptoService(action=signing_start;alias=$alias;algorithm=$signAlgorithm)" }
signature.initSign(privateKey, newSecureRandom())
signature.update(data)
@@ -126,7 +128,7 @@ class BCCryptoService(private val legalName: X500Principal,
/**
* If a node is running in [NodeConfiguration.devMode] and for backwards compatibility purposes, the same [KeyStore]
- * is reused outside [BCCryptoService] to update certificate paths. [resyncKeystore] will sync [BCCryptoService]'s
+ * is reused outside [DefaultCryptoService] to update certificate paths. [resyncKeystore] will sync [DefaultCryptoService]'s
* loaded [certificateStore] in memory with the contents of the corresponding [KeyStore] file.
*/
fun resyncKeystore() {
@@ -178,7 +180,7 @@ class BCCryptoService(private val legalName: X500Principal,
}
val wrappingKey = wrappingKeyStore.getKey(masterKeyAlias, certificateStore.entryPassword.toCharArray())
- val cipher = Cipher.getInstance("AESWRAPPAD", cordaBouncyCastleProvider)
+ val cipher = Cipher.getInstance("AESWRAPPAD")
cipher.init(Cipher.WRAP_MODE, wrappingKey)
val keyPairGenerator = keyPairGeneratorFromScheme(childKeyScheme)
@@ -199,22 +201,23 @@ class BCCryptoService(private val legalName: X500Principal,
1 -> "AESWRAPPAD"
else -> "AES"
}
- val cipher = Cipher.getInstance(algorithm, cordaBouncyCastleProvider)
+ val cipher = Cipher.getInstance(algorithm)
cipher.init(Cipher.UNWRAP_MODE, wrappingKey)
val privateKey = cipher.unwrap(wrappedPrivateKey.keyMaterial, keyAlgorithmFromScheme(wrappedPrivateKey.signatureScheme), Cipher.PRIVATE_KEY) as PrivateKey
- val signature = getSignatureInstance(wrappedPrivateKey.signatureScheme.signatureName, cordaBouncyCastleProvider)
- signature.initSign(privateKey, newSecureRandom())
- signature.update(payloadToSign)
- return signature.sign()
+ return withSignature(wrappedPrivateKey.signatureScheme) { signature ->
+ signature.initSign(privateKey, newSecureRandom())
+ signature.update(payloadToSign)
+ signature.sign()
+ }
}
- override fun getWrappingMode(): WrappingMode? = WrappingMode.DEGRADED_WRAPPED
+ override fun getWrappingMode(): WrappingMode = WrappingMode.DEGRADED_WRAPPED
private fun keyPairGeneratorFromScheme(scheme: SignatureScheme): KeyPairGenerator {
val algorithm = keyAlgorithmFromScheme(scheme)
- val keyPairGenerator = KeyPairGenerator.getInstance(algorithm, cordaBouncyCastleProvider)
+ val keyPairGenerator = KeyPairGenerator.getInstance(algorithm)
when (scheme) {
Crypto.ECDSA_SECP256R1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256r1"))
Crypto.ECDSA_SECP256K1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256k1"))
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt
index 3f80ba5200..76597a3f32 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/DefaultKryoCustomizer.kt
@@ -20,7 +20,6 @@ import de.javakaffee.kryoserializers.guava.ImmutableSortedSetSerializer
import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName
import net.corda.core.contracts.PrivacySalt
-import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.AbstractAttachment
@@ -40,14 +39,6 @@ import net.corda.core.utilities.toNonEmptySet
import net.corda.serialization.internal.DefaultWhitelist
import net.corda.serialization.internal.GeneratedAttachment
import net.corda.serialization.internal.MutableClassWhitelist
-import net.i2p.crypto.eddsa.EdDSAPrivateKey
-import net.i2p.crypto.eddsa.EdDSAPublicKey
-import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey
-import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
-import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateCrtKey
-import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
-import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
-import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.objenesis.instantiator.ObjectInstantiator
import org.objenesis.strategy.InstantiatorStrategy
import org.objenesis.strategy.StdInstantiatorStrategy
@@ -62,14 +53,13 @@ import java.security.PublicKey
import java.security.cert.CertPath
import java.security.cert.X509Certificate
import java.util.*
-import kotlin.collections.ArrayList
object DefaultKryoCustomizer {
private val serializationWhitelists: List by lazy {
ServiceLoader.load(SerializationWhitelist::class.java, this.javaClass.classLoader).toList() + DefaultWhitelist
}
- fun customize(kryo: Kryo, publicKeySerializer: Serializer = PublicKeySerializer): Kryo {
+ fun customize(kryo: Kryo): Kryo {
return kryo.apply {
isRegistrationRequired = false
references = true
@@ -110,6 +100,8 @@ object DefaultKryoCustomizer {
// Please add any new registrations to the end.
addDefaultSerializer(LinkedHashMapIteratorSerializer.getIterator()::class.java.superclass, LinkedHashMapIteratorSerializer)
+ addDefaultSerializer(PublicKey::class.java, PublicKeySerializer)
+ addDefaultSerializer(PrivateKey::class.java, PrivateKeySerializer)
register(LinkedHashMapEntrySerializer.getEntry()::class.java, LinkedHashMapEntrySerializer)
register(LinkedListItrSerializer.getListItr()::class.java, LinkedListItrSerializer)
register(Arrays.asList("").javaClass, ArraysAsListSerializer())
@@ -126,11 +118,6 @@ object DefaultKryoCustomizer {
// InputStream subclasses whitelisting, required for attachments.
register(BufferedInputStream::class.java, InputStreamSerializer)
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), InputStreamSerializer)
- register(PublicKey::class.java, publicKeySerializer)
- register(PrivateKey::class.java, PrivateKeySerializer)
- register(EdDSAPublicKey::class.java, publicKeySerializer)
- register(EdDSAPrivateKey::class.java, PrivateKeySerializer)
- register(CompositeKey::class.java, publicKeySerializer) // Using a custom serializer for compactness
// Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
register(Array::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> })
// This ensures a NonEmptySetSerializer is constructed with an initial value.
@@ -139,12 +126,6 @@ object DefaultKryoCustomizer {
register(Class::class.java, ClassSerializer)
register(FileInputStream::class.java, InputStreamSerializer)
register(CertPath::class.java, CertPathSerializer)
- register(BCECPrivateKey::class.java, PrivateKeySerializer)
- register(BCECPublicKey::class.java, publicKeySerializer)
- register(BCRSAPrivateCrtKey::class.java, PrivateKeySerializer)
- register(BCRSAPublicKey::class.java, publicKeySerializer)
- register(BCSphincs256PrivateKey::class.java, PrivateKeySerializer)
- register(BCSphincs256PublicKey::class.java, publicKeySerializer)
register(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
register(PartyAndCertificate::class.java, PartyAndCertificateSerializer)
diff --git a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt
index b68b4a2e68..0ad5a7d037 100644
--- a/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt
+++ b/node-api/src/main/kotlin/net/corda/nodeapi/internal/serialization/kryo/Kryo.kt
@@ -28,7 +28,6 @@ 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.SgxSupport
import net.corda.serialization.internal.serializationContextKey
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@@ -83,11 +82,8 @@ class ImmutableClassSerializer(val klass: KClass) : Serializer()
init {
// Verify that this class is immutable (all properties are final).
- // We disable this check inside SGX as the reflection blows up.
- if (!SgxSupport.isInsideEnclave) {
- props.forEach {
- require(it !is KMutableProperty<*>) { "$it mutable property of class: ${klass} is unsupported" }
- }
+ props.forEach {
+ require(it !is KMutableProperty<*>) { "$it mutable property of class: $klass is unsupported" }
}
}
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt
index 544a5d34ec..39fa3073ae 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/SignedNodeInfoTest.kt
@@ -6,16 +6,15 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort
+import net.corda.coretesting.internal.TestNodeInfoBuilder
+import net.corda.coretesting.internal.signWith
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.SerializationEnvironmentRule
-import net.corda.coretesting.internal.TestNodeInfoBuilder
-import net.corda.coretesting.internal.signWith
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import java.security.KeyPair
@@ -56,7 +55,6 @@ class SignedNodeInfoTest {
}
@Test(timeout=300_000)
- @Ignore("TODO JDK17: Fixme")
fun `verifying composite keys only`() {
val aliceKeyPair = generateKeyPair()
val bobKeyPair = generateKeyPair()
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt
index 6920c78093..7b3a329a9b 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/crypto/ContentSignerBuilderTest.kt
@@ -28,6 +28,6 @@ class ContentSignerBuilderTest {
.isThrownBy {
ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
}
- .withMessage("Incorrect key type EC for signature scheme NONEwithEdDSA")
+ .withMessage("Incorrect key type EC for signature scheme Ed25519")
}
-}
\ No newline at end of file
+}
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoServiceTests.kt
similarity index 83%
rename from node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt
rename to node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoServiceTests.kt
index 5eafd10187..560f33a69a 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/bouncycastle/BCCryptoServiceTests.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/cryptoservice/DefaultCryptoServiceTests.kt
@@ -1,33 +1,32 @@
-package net.corda.nodeapi.internal.cryptoservice.bouncycastle
+package net.corda.nodeapi.internal.cryptoservice
import net.corda.core.crypto.Crypto
import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.internal.cordaBouncyCastleProvider
import net.corda.core.utilities.days
+import net.corda.coretesting.internal.stubs.CertificateStoreStubs
import net.corda.nodeapi.internal.config.CertificateStoreSupplier
import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities
-import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.CryptoServiceException
-import net.corda.nodeapi.internal.cryptoservice.WrappedPrivateKey
-import net.corda.nodeapi.internal.cryptoservice.WrappingMode
-import net.corda.testing.core.ALICE_NAME
-import net.corda.coretesting.internal.stubs.CertificateStoreStubs
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
+import net.corda.testing.core.ALICE_NAME
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.io.FileOutputStream
import java.nio.file.Path
-import java.security.*
+import java.security.KeyPair
+import java.security.KeyPairGenerator
+import java.security.KeyStore
+import java.security.PublicKey
+import java.security.Signature
import java.security.spec.ECGenParameterSpec
import java.time.Duration
-import java.util.*
+import java.util.UUID
import javax.crypto.Cipher
import javax.security.auth.x500.X500Principal
import kotlin.io.path.div
@@ -35,7 +34,7 @@ import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
-class BCCryptoServiceTests {
+class DefaultCryptoServiceTests {
companion object {
val clearData = "data".toByteArray()
}
@@ -61,15 +60,13 @@ class BCCryptoServiceTests {
}
@Test(timeout=300_000)
- @Ignore("TODO JDK17: Fixme")
- fun `BCCryptoService generate key pair and sign both data and cert`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ fun `cryptoService generate key pair and sign both data and cert`() {
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
// Testing every supported scheme.
- Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY
- && it.signatureName != "SHA512WITHSPHINCS256"}.forEach { generateKeyAndSignForScheme(cryptoService, it) }
+ Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY }.forEach { generateKeyAndSignForScheme(cryptoService, it) }
}
- private fun generateKeyAndSignForScheme(cryptoService: BCCryptoService, signatureScheme: SignatureScheme) {
+ private fun generateKeyAndSignForScheme(cryptoService: DefaultCryptoService, signatureScheme: SignatureScheme) {
val alias = "signature${signatureScheme.schemeNumberID}"
val pubKey = cryptoService.generateKeyPair(alias, signatureScheme)
assertTrue { cryptoService.containsKey(alias) }
@@ -95,12 +92,10 @@ class BCCryptoServiceTests {
}
@Test(timeout=300_000)
- @Ignore("TODO JDK17: Fixme")
- fun `BCCryptoService generate key pair and sign with existing schemes`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ fun `cryptoService generate key pair and sign with existing schemes`() {
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
// Testing every supported scheme.
- Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY
- && it.signatureName != "SHA512WITHSPHINCS256"}.forEach {
+ Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY }.forEach {
val alias = "signature${it.schemeNumberID}"
val pubKey = cryptoService.generateKeyPair(alias, it)
assertTrue { cryptoService.containsKey(alias) }
@@ -110,9 +105,7 @@ class BCCryptoServiceTests {
}
@Test(timeout=300_000)
- @Ignore("TODO JDK17: Fixme")
- fun `BCCryptoService generate key pair and sign with passed signing algorithm`() {
-
+ fun `cryptoService generate key pair and sign with passed signing algorithm`() {
assertTrue{signAndVerify(signAlgo = "NONEwithRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")}
assertTrue{signAndVerify(signAlgo = "MD2withRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")}
assertTrue{signAndVerify(signAlgo = "MD5withRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")}
@@ -132,7 +125,7 @@ class BCCryptoServiceTests {
private fun signAndVerify(signAlgo: String, alias: String, keyTypeAlgo: String): Boolean {
val keyPairGenerator = KeyPairGenerator.getInstance(keyTypeAlgo)
val keyPair = keyPairGenerator.genKeyPair()
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, createKeystore(alias, keyPair), wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, createKeystore(alias, keyPair), wrappingKeyStorePath)
assertTrue { cryptoService.containsKey(alias) }
val signatureData = cryptoService.sign(alias, clearData, signAlgo)
return verify(signAlgo, cryptoService.getPublicKey(alias), signatureData, clearData)
@@ -175,7 +168,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `When key does not exist getPublicKey, sign and getSigner should throw`() {
val nonExistingAlias = "nonExistingAlias"
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
assertFalse { cryptoService.containsKey(nonExistingAlias) }
assertFailsWith { cryptoService.getPublicKey(nonExistingAlias) }
assertFailsWith { cryptoService.sign(nonExistingAlias, clearData) }
@@ -184,7 +177,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService supports degraded mode of wrapping`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val supportedMode = cryptoService.getWrappingMode()
assertThat(supportedMode).isEqualTo(WrappingMode.DEGRADED_WRAPPED)
@@ -192,7 +185,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService does not fail when requested to create same wrapping key twice with failIfExists is false`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val keyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(keyAlias)
@@ -201,7 +194,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService does fail when requested to create same wrapping key twice with failIfExists is true`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val keyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(keyAlias)
@@ -213,7 +206,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService fails when asked to generate wrapped key pair or sign, but the master key specified does not exist`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val wrappingKeyAlias = UUID.randomUUID().toString()
@@ -230,7 +223,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService can generate wrapped key pair and sign with the private key successfully, using default algorithm`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val wrappingKeyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(wrappingKeyAlias)
@@ -239,7 +232,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService can generate wrapped key pair and sign with the private key successfully`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val wrappingKeyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(wrappingKeyAlias)
@@ -264,7 +257,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000)
fun `cryptoService can sign with previously encoded version of wrapped key`() {
- val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
+ val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val wrappingKeyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(wrappingKeyAlias)
diff --git a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt
index 0692abeae5..f58d46312c 100644
--- a/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt
+++ b/node-api/src/test/kotlin/net/corda/nodeapi/internal/serialization/kryo/KryoTests.kt
@@ -139,7 +139,12 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test(timeout=300_000)
fun `deserialised key pair functions the same as serialised one`() {
- val keyPair = generateKeyPair()
+ // The default signature scheme, EDDSA_ED25519_SHA512, generates public keys which have a writeReplace method (EdDSAPublicKeyImpl).
+ // This is picked up by Quasar's custom ReplaceableObjectKryo, which will *always* use the writeReplace for serialisation, ignoring
+ // any Kryo serialisers that might have been configured. This thus means the deserialisation path does not go via
+ // Cryto.decodePublicKey, which interns the materialised PublicKey. To avoid all of this, and test the last assertion, we use
+ // ECDSA keys, whose implementation doesn't have a writeReplace method.
+ val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val bitsToSign: ByteArray = Ints.toByteArray(0x01234567)
val wrongBits: ByteArray = Ints.toByteArray(0x76543210)
val signature = keyPair.sign(bitsToSign)
diff --git a/node/build.gradle b/node/build.gradle
index 44c08ef17a..f904c1db21 100644
--- a/node/build.gradle
+++ b/node/build.gradle
@@ -223,7 +223,6 @@ dependencies {
integrationTestImplementation "junit:junit:$junit_version"
integrationTestImplementation "org.assertj:assertj-core:${assertj_version}"
integrationTestImplementation "org.apache.qpid:qpid-jms-client:${protonj_version}"
- integrationTestImplementation "net.i2p.crypto:eddsa:$eddsa_version"
// used by FinalityFlowErrorHandlingTest
slowIntegrationTestImplementation project(':testing:cordapps:cashobservers')
@@ -276,7 +275,6 @@ quasar {
"io.github.classgraph**",
"io.netty*",
"liquibase**",
- "net.i2p.crypto.**",
"nonapi.io.github.classgraph.**",
"org.apiguardian.**",
"org.bouncycastle**",
diff --git a/node/capsule/build.gradle b/node/capsule/build.gradle
index 3a2ac236c4..6258b55be3 100644
--- a/node/capsule/build.gradle
+++ b/node/capsule/build.gradle
@@ -65,7 +65,7 @@ tasks.register('buildCordaJAR', FatCapsule) {
applicationVersion = corda_release_version
applicationId = "net.corda.node.Corda"
// See experimental/quasar-hook/README.md for how to generate.
- def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;org.mockito**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.bytebuddy**;net.i2p**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)"
+ def quasarExcludeExpression = "x(antlr**;bftsmart**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;org.mockito**;com.opengamma**;com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;io.github**;io.netty**;jdk**;kotlin**;net.bytebuddy**;org.apache**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.objectweb**;org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;com.lmax**;picocli**;liquibase**;com.github.benmanes**;org.json**;org.postgresql**;nonapi.io.github.classgraph**;io.opentelemetry**)"
def quasarClassLoaderExclusion = "l(net.corda.core.serialization.internal.**)"
def quasarOptions = "m"
javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarOptions}${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"]
diff --git a/node/capsule/src/main/resources/node-jvm-args.txt b/node/capsule/src/main/resources/node-jvm-args.txt
index 21d6d9f829..d47e01d01a 100644
--- a/node/capsule/src/main/resources/node-jvm-args.txt
+++ b/node/capsule/src/main/resources/node-jvm-args.txt
@@ -3,7 +3,12 @@
--add-opens=java.base/java.nio=ALL-UNNAMED
--add-opens=java.base/java.security=ALL-UNNAMED
--add-opens=java.base/java.security.cert=ALL-UNNAMED
+--add-opens=java.base/java.security.spec=ALL-UNNAMED
--add-opens=java.base/java.time=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.util.concurrent=ALL-UNNAMED
+--add-opens=java.base/sun.security.pkcs=ALL-UNNAMED
+--add-opens=java.base/sun.security.util=ALL-UNNAMED
+--add-opens=java.base/sun.security.x509=ALL-UNNAMED
--add-opens=java.sql/java.sql=ALL-UNNAMED
+--add-opens=jdk.crypto.ec/sun.security.ec.ed=ALL-UNNAMED
diff --git a/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt b/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt
index 1d3e929dde..ee94fc62d0 100644
--- a/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt
+++ b/node/src/integration-test/kotlin/net/corda/node/customcheckpointserializer/TestCorDapp.kt
@@ -7,7 +7,6 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.serialization.CheckpointCustomSerializer
import net.corda.testing.node.internal.CustomCordapp
import net.corda.testing.node.internal.enclosedCordapp
-import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.assertj.core.api.Assertions
import java.security.PublicKey
import java.time.Duration
@@ -198,17 +197,4 @@ class TestCorDapp {
throw FlowException("Broken on purpose")
}
}
-
- @Suppress("unused")
- class BrokenEdDSAPublicKeySerializer :
- CheckpointCustomSerializer {
- override fun toProxy(obj: EdDSAPublicKey): String {
- throw FlowException("Broken on purpose")
- }
-
- override fun fromProxy(proxy: String): EdDSAPublicKey {
- throw FlowException("Broken on purpose")
- }
- }
-
}
diff --git a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
index 57cbea71f5..8d12f27003 100644
--- a/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/AbstractNode.kt
@@ -150,7 +150,7 @@ import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.nodeapi.internal.cordapp.cordappSchemas
import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
+import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService
import net.corda.nodeapi.internal.lifecycle.NodeLifecycleEvent
import net.corda.nodeapi.internal.lifecycle.NodeLifecycleEventsDistributor
import net.corda.nodeapi.internal.lifecycle.NodeServicesContext
@@ -1061,7 +1061,7 @@ abstract class AbstractNode(val configuration: NodeConfiguration,
}
protected open fun makeCryptoService(): CryptoService {
- return BCCryptoService(configuration.myLegalName.x500Principal, configuration.signingCertificateStore)
+ return DefaultCryptoService(configuration.myLegalName.x500Principal, configuration.signingCertificateStore)
}
@VisibleForTesting
diff --git a/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt b/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt
index 6c4225cdc0..f5ff1aa2c3 100644
--- a/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt
+++ b/node/src/main/kotlin/net/corda/node/internal/KeyStoreHandler.kt
@@ -16,7 +16,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_AL
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS
import net.corda.nodeapi.internal.crypto.checkValidity
import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
+import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService
import java.io.IOException
import java.math.BigInteger
import java.nio.file.NoSuchFileException
@@ -54,8 +54,8 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
if (configuration.devMode) {
configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy)
// configureWithDevSSLCertificate is a devMode process that writes directly to keystore files, so
- // we should re-synchronise BCCryptoService with the updated keystore file.
- if (cryptoService is BCCryptoService) {
+ // we should re-synchronise DefaultCryptoService with the updated keystore file.
+ if (cryptoService is DefaultCryptoService) {
cryptoService.resyncKeystore()
}
}
diff --git a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt
index 9777d07a14..bd5c9c9999 100644
--- a/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt
+++ b/node/src/main/kotlin/net/corda/node/services/config/ConfigUtilities.kt
@@ -17,7 +17,7 @@ import net.corda.nodeapi.internal.config.toProperties
import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
+import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService
import net.corda.nodeapi.internal.installDevNodeCaCertPath
import net.corda.nodeapi.internal.loadDevCaTrustStore
import net.corda.nodeapi.internal.registerDevP2pCertificates
@@ -195,7 +195,7 @@ fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500N
FileBasedCertificateStoreSupplier(keyStore.path, keyStore.storePassword, keyStore.entryPassword).get(true)
.also { it.registerDevP2pCertificates(myLegalName) }
when (cryptoService) {
- is BCCryptoService, null -> {
+ is DefaultCryptoService, null -> {
val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.storePassword, signingCertificateStore.entryPassword).get(true)
.also {
it.installDevNodeCaCertPath(myLegalName)
diff --git a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
index a5cf742a9e..02d3695995 100644
--- a/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
+++ b/node/src/main/kotlin/net/corda/node/services/config/NodeConfiguration.kt
@@ -77,7 +77,7 @@ interface NodeConfiguration : ConfigurationWithOptionsContainer {
val baseDirectory: Path
val certificatesDirectory: Path
// signingCertificateStore is used to store certificate chains.
- // However, BCCryptoService is reusing this to store keys as well.
+ // However, DefaultCryptoService is reusing this to store keys as well.
val signingCertificateStore: FileBasedCertificateStoreSupplier
val p2pSslOptions: MutualSslConfiguration
diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt
index b999b1183d..c35cc1fa9f 100644
--- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt
+++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt
@@ -22,7 +22,7 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
import net.corda.nodeapi.internal.crypto.X509Utilities.DEFAULT_VALIDITY_WINDOW
import net.corda.nodeapi.internal.crypto.x509
import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
+import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService
import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.operator.ContentSigner
@@ -98,7 +98,7 @@ open class NetworkRegistrationHelper(
certificatesDirectory.safeSymbolicRead().createDirectories()
// We need this in case cryptoService and certificateStore share the same KeyStore (for backwards compatibility purposes).
// If we didn't, then an update to cryptoService wouldn't be reflected to certificateStore that is already loaded in memory.
- val certStore: CertificateStore = if (cryptoService is BCCryptoService) cryptoService.certificateStore else certificateStore
+ val certStore: CertificateStore = if (cryptoService is DefaultCryptoService) cryptoService.certificateStore else certificateStore
// SELF_SIGNED_PRIVATE_KEY is used as progress indicator.
if (certStore.contains(nodeCaKeyAlias) && !certStore.contains(SELF_SIGNED_PRIVATE_KEY)) {
@@ -169,7 +169,7 @@ open class NetworkRegistrationHelper(
certificatesDirectory.safeSymbolicRead().createDirectories()
// We need this in case cryptoService and certificateStore share the same KeyStore (for backwards compatibility purposes).
// If we didn't, then an update to cryptoService wouldn't be reflected to certificateStore that is already loaded in memory.
- val certStore: CertificateStore = if (cryptoService is BCCryptoService) cryptoService.certificateStore else certificateStore
+ val certStore: CertificateStore = if (cryptoService is DefaultCryptoService) cryptoService.certificateStore else certificateStore
if (!certStore.contains(nodeCaKeyAlias)) {
logProgress("Node CA key doesn't exist, program will now terminate...")
@@ -374,7 +374,7 @@ class NodeRegistrationConfiguration(
tlsCertCrlDistPoint = config.tlsCertCrlDistPoint,
certificatesDirectory = config.certificatesDirectory,
emailAddress = config.emailAddress,
- cryptoService = BCCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore),
+ cryptoService = DefaultCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore),
certificateStore = config.signingCertificateStore.get(true),
notaryServiceConfig = config.notary?.let {
// Validation of the presence of the notary service legal name is only done here and not in the top level configuration
diff --git a/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt b/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt
index 8e67f741ed..e6b39038a6 100644
--- a/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt
+++ b/node/src/test/kotlin/net/corda/node/internal/KeyStoreHandlerTest.kt
@@ -23,13 +23,12 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_COMPOS
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_ALIAS
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS
import net.corda.nodeapi.internal.cryptoservice.CryptoService
-import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
+import net.corda.nodeapi.internal.cryptoservice.DefaultCryptoService
import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME
import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
@@ -37,7 +36,6 @@ import java.security.KeyPair
import java.security.PublicKey
import kotlin.io.path.div
-@Ignore("TODO JDK17: Fixme")
class KeyStoreHandlerTest {
@Rule
@JvmField
@@ -49,7 +47,7 @@ class KeyStoreHandlerTest {
private val keyStore get() = config.signingCertificateStore.get()
- private lateinit var cryptoService: BCCryptoService
+ private lateinit var cryptoService: DefaultCryptoService
private lateinit var keyStoreHandler: KeyStoreHandler
@@ -66,7 +64,7 @@ class KeyStoreHandlerTest {
doReturn(ALICE_NAME).whenever(it).myLegalName
doReturn(null).whenever(it).notary
}
- cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore)
+ cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore)
keyStoreHandler = KeyStoreHandler(config, cryptoService)
}
@@ -192,7 +190,7 @@ class KeyStoreHandlerTest {
val devCertificateDir = tempFolder.root.toPath() / "certificates-dev"
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(devCertificateDir)
val p2pSslOptions = CertificateStoreStubs.P2P.withCertificatesDirectory(devCertificateDir)
- val devCryptoService = BCCryptoService(config.myLegalName.x500Principal, signingCertificateStore)
+ val devCryptoService = DefaultCryptoService(config.myLegalName.x500Principal, signingCertificateStore)
doReturn(true).whenever(config).devMode
doReturn(signingCertificateStore).whenever(config).signingCertificateStore
diff --git a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt
index 6b982b920c..1110f14ea1 100644
--- a/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt
+++ b/testing/node-driver/src/main/kotlin/net/corda/testing/node/internal/DriverDSLImpl.kt
@@ -955,8 +955,7 @@ class DriverDSLImpl(
val excludePackagePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" +
"com.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" +
"com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" +
- "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;" +
- "net.i2p**;org.apache**;" +
+ "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;org.apache**;" +
"org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" +
"org.hamcrest**;org.hibernate**;org.jboss**;org.jcp**;org.joda**;org.junit**;org.mockito**;org.objectweb**;" +
"org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;" +