ENT-11101: Fix all crypto issues introduced by Java 17 upgrade

The various crypto tests that were previously ignored have been re-enabled.

The abandoned i2p EdDSA library has been replaced with native support that was added in Java 15.

Java 17 (via the `SunEC` provider) does not support the secp256k1 curve (one of the two ECDSA curves supported in Corda). This would not normally have been an issue as secp256k1 is already taken care of by Bouncy Castle. However, this only works if the `Crypto` API is used or if `”BC”` is explicitly specified as the provider (e.g. `Signature.getInstance(“SHA256withECDSA”, “BC”)`). If no provider is specified, which is what is more common, and actually what the Java docs recommend, then this doesn’t work as the `SunEC` provider is selected. To resolve this, a custom provider was created, installed just in front of `SunEC`, which “augments” `SunEC` by delegating to Bouncy Castle if keys or parameters for secp256k1 are encountered.

`X509Utilities.createCertificate` now calls `X509Certificate.verify()` to verify the created certificate, rather than using the Bouncy Castle API. This is more representative of how certificates will be verified (e.g. during SSL handshake) and weeds out other issues (such as unsupported curve error for secp256k1).

`BCCryptoService` has been renamed to `DefaultCryptoService` as it no longer explicitly uses Bouncy Castle but rather uses the installed security providers. This was done to fix a failing test. Further, `BCCryptoService` was already relying on the installed providers in some places.

The hack to get Corda `SecureRandom` working was also resolved. Also, as an added bonus, tests which ignored `SPHINCS256_SHA256` have been reinstated.

Note, there is a slightly inconsistency between how EdDSA and ECDSA keys are handled (and also RSA). For the later, Bouncy Castle is preferred, and methods such as `toSupportedKey*` will convert any JDK class to Bouncy Castle. For EdDSA the preference is the JDK (`SunEC`). However, this is simply a continuation of the previous preference of the i2p library over Bouncy Castle.
This commit is contained in:
Shams Asari 2024-02-29 14:46:27 +00:00
parent 6dfbed572e
commit 0091807c2f
45 changed files with 507 additions and 648 deletions

View File

@ -8290,11 +8290,6 @@ public static final class net.corda.core.utilities.ProgressTracker$UNSTARTED ext
public interface net.corda.core.utilities.PropertyDelegate public interface net.corda.core.utilities.PropertyDelegate
public abstract T getValue(Object, kotlin.reflect.KProperty) 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 public final class net.corda.core.utilities.ThreadDumpUtilsKt extends java.lang.Object
@NotNull @NotNull
public static final String asString(management.ThreadInfo, int) public static final String asString(management.ThreadInfo, int)

View File

@ -90,7 +90,6 @@ buildscript {
ext.h2_version = constants.getProperty("h2Version") ext.h2_version = constants.getProperty("h2Version")
ext.rxjava_version = constants.getProperty("rxjavaVersion") ext.rxjava_version = constants.getProperty("rxjavaVersion")
ext.dokka_version = constants.getProperty("dokkaVersion") ext.dokka_version = constants.getProperty("dokkaVersion")
ext.eddsa_version = constants.getProperty("eddsaVersion")
ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion") ext.dependency_checker_version = constants.getProperty("dependencyCheckerVersion")
ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion") ext.commons_collections_version = constants.getProperty("commonsCollectionsVersion")
ext.beanutils_version = constants.getProperty("beanutilsVersion") ext.beanutils_version = constants.getProperty("beanutilsVersion")
@ -178,7 +177,6 @@ buildscript {
classpath "com.guardsquare:proguard-gradle:$proguard_version" classpath "com.guardsquare:proguard-gradle:$proguard_version"
classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.15.0'
classpath "org.jetbrains.dokka:dokka-base:$dokka_version" 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.owasp:dependency-check-gradle:$dependency_checker_version"
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version" classpath "org.jfrog.buildinfo:build-info-extractor-gradle:$artifactory_plugin_version"
// Capsule gradle plugin forked and maintained locally to support Gradle 5.x // Capsule gradle plugin forked and maintained locally to support Gradle 5.x

View File

@ -21,7 +21,7 @@ guavaVersion=28.0-jre
quasarVersion=0.9.0_r3 quasarVersion=0.9.0_r3
dockerJavaVersion=3.2.5 dockerJavaVersion=3.2.5
proguardVersion=7.3.1 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 bouncycastleVersion=1.75
classgraphVersion=4.8.135 classgraphVersion=4.8.135
disruptorVersion=3.4.2 disruptorVersion=3.4.2
@ -79,7 +79,6 @@ hibernateVersion=5.6.14.Final
h2Version=2.2.224 h2Version=2.2.224
rxjavaVersion=1.3.8 rxjavaVersion=1.3.8
dokkaVersion=1.8.20 dokkaVersion=1.8.20
eddsaVersion=0.3.0
dependencyCheckerVersion=5.2.0 dependencyCheckerVersion=5.2.0
commonsCollectionsVersion=4.3 commonsCollectionsVersion=4.3
beanutilsVersion=1.9.4 beanutilsVersion=1.9.4

View File

@ -184,7 +184,6 @@ quasar {
"io.github.classgraph**", "io.github.classgraph**",
"io.netty*", "io.netty*",
"liquibase**", "liquibase**",
"net.i2p.crypto.**",
"nonapi.io.github.classgraph.**", "nonapi.io.github.classgraph.**",
"org.apiguardian.**", "org.apiguardian.**",
"org.bouncycastle**", "org.bouncycastle**",

View File

@ -36,8 +36,6 @@ dependencies {
// For caches rather than guava // For caches rather than guava
implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version" implementation "com.github.ben-manes.caffeine:caffeine:$caffeine_version"
implementation "org.apache.commons:commons-lang3:$commons_lang3_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 // Bouncy castle support needed for X509 certificate manipulation
implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}" implementation "org.bouncycastle:bcprov-jdk18on:${bouncycastle_version}"
// required to use @Type annotation // 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 { 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() 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.github.classgraph**",
"io.netty*", "io.netty*",
"liquibase**", "liquibase**",
"net.i2p.crypto.**",
"nonapi.io.github.classgraph.**", "nonapi.io.github.classgraph.**",
"org.apiguardian.**", "org.apiguardian.**",
"org.bouncycastle**", "org.bouncycastle**",

View File

@ -5,11 +5,10 @@ import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
import net.corda.core.crypto.internal.PlatformSecureRandomService import net.corda.core.crypto.internal.PlatformSecureRandomService
import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.ASN1ObjectIdentifier
import java.security.Provider import java.security.Provider
import java.util.* import java.util.Optional
import java.util.concurrent.ConcurrentHashMap 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.2", "$PROVIDER_NAME security provider") {
class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
companion object { companion object {
const val PROVIDER_NAME = "Corda" const val PROVIDER_NAME = "Corda"
} }
@ -17,21 +16,8 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
private val services = ConcurrentHashMap<Pair<String, String>, Optional<Service>>() private val services = ConcurrentHashMap<Pair<String, String>, Optional<Service>>()
init { init {
put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", CompositeKeyFactory::class.java.name) putService(Service(this, "KeyFactory", CompositeKey.KEY_ALGORITHM, CompositeKeyFactory::class.java.name, listOf("$COMPOSITE_KEY", "OID.$COMPOSITE_KEY"), null))
put("Alg.Alias.KeyFactory.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM) putService(Service(this, "Signature", CompositeSignature.SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, listOf("$COMPOSITE_SIGNATURE", "OID.$COMPOSITE_SIGNATURE"), null))
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(PlatformSecureRandomService(this)) putService(PlatformSecureRandomService(this))
} }

View File

@ -1,31 +1,26 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.CordaOID 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.AliasPrivateKey
import net.corda.core.crypto.internal.Curve25519.isOnCurve25519
import net.corda.core.crypto.internal.Instances.withSignature import net.corda.core.crypto.internal.Instances.withSignature
import net.corda.core.crypto.internal.PublicKeyCache import net.corda.core.crypto.internal.PublicKeyCache
import net.corda.core.crypto.internal.bouncyCastlePQCProvider import net.corda.core.crypto.internal.bouncyCastlePQCProvider
import net.corda.core.crypto.internal.cordaBouncyCastleProvider import net.corda.core.crypto.internal.cordaBouncyCastleProvider
import net.corda.core.crypto.internal.cordaSecurityProvider import net.corda.core.crypto.internal.cordaSecurityProvider
import net.corda.core.crypto.internal.`id-Curve25519ph`
import net.corda.core.crypto.internal.providerMap import net.corda.core.crypto.internal.providerMap
import net.corda.core.crypto.internal.sunEcProvider
import net.corda.core.internal.utilities.PrivateInterner import net.corda.core.internal.utilities.PrivateInterner
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.ByteSequence import net.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.ASN1Integer
import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.DERNull import org.bouncycastle.asn1.DERNull
import org.bouncycastle.asn1.DERUTF8String import org.bouncycastle.asn1.DERUTF8String
import org.bouncycastle.asn1.DLSequence import org.bouncycastle.asn1.DLSequence
import org.bouncycastle.asn1.bc.BCObjectIdentifiers import org.bouncycastle.asn1.bc.BCObjectIdentifiers
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers
import org.bouncycastle.asn1.nist.NISTObjectIdentifiers import org.bouncycastle.asn1.nist.NISTObjectIdentifiers
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo 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.x509.SubjectPublicKeyInfo
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers import org.bouncycastle.asn1.x9.X9ObjectIdentifiers
import org.bouncycastle.crypto.CryptoServicesRegistrar 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.BCECPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey
import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey 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.ECConstants
import org.bouncycastle.math.ec.FixedPointCombMultiplier import org.bouncycastle.math.ec.FixedPointCombMultiplier
import org.bouncycastle.math.ec.WNafUtil 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.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
import java.math.BigInteger import java.math.BigInteger
import java.security.InvalidKeyException import java.security.InvalidKeyException
import java.security.Key import java.security.Key
import java.security.KeyFactory
import java.security.KeyPair import java.security.KeyPair
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.security.PrivateKey import java.security.PrivateKey
import java.security.Provider import java.security.Provider
import java.security.PublicKey import java.security.PublicKey
import java.security.Signature
import java.security.SignatureException import java.security.SignatureException
import java.security.interfaces.EdECPrivateKey
import java.security.interfaces.EdECPublicKey
import java.security.spec.InvalidKeySpecException import java.security.spec.InvalidKeySpecException
import java.security.spec.NamedParameterSpec
import java.security.spec.PKCS8EncodedKeySpec import java.security.spec.PKCS8EncodedKeySpec
import java.security.spec.X509EncodedKeySpec import java.security.spec.X509EncodedKeySpec
import javax.crypto.Mac import javax.crypto.Mac
@ -77,7 +79,7 @@ import javax.crypto.spec.SecretKeySpec
* <li>RSA_SHA256 (RSA PKCS#1 using SHA256 as hash algorithm). * <li>RSA_SHA256 (RSA PKCS#1 using SHA256 as hash algorithm).
* <li>ECDSA_SECP256K1_SHA256 (ECDSA using the secp256k1 Koblitz curve and SHA256 as hash algorithm). * <li>ECDSA_SECP256K1_SHA256 (ECDSA using the secp256k1 Koblitz curve and SHA256 as hash algorithm).
* <li>ECDSA_SECP256R1_SHA256 (ECDSA using the secp256r1 (NIST P-256) curve and SHA256 as hash algorithm). * <li>ECDSA_SECP256R1_SHA256 (ECDSA using the secp256r1 (NIST P-256) curve and SHA256 as hash algorithm).
* <li>EDDSA_ED25519_SHA512 (EdDSA using the ed255519 twisted Edwards curve and SHA512 as hash algorithm). * <li>EDDSA_ED25519_SHA512 (EdDSA using the ed25519 twisted Edwards curve and SHA512 as hash algorithm).
* <li>SPHINCS256_SHA512 (SPHINCS-256 hash-based signature scheme using SHA512 as hash algorithm). * <li>SPHINCS256_SHA512 (SPHINCS-256 hash-based signature scheme using SHA512 as hash algorithm).
* </ul> * </ul>
*/ */
@ -95,7 +97,7 @@ object Crypto {
listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)), listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)),
cordaBouncyCastleProvider.name, cordaBouncyCastleProvider.name,
"RSA", "RSA",
"SHA256WITHRSA", "SHA256withRSA",
null, null,
3072, 3072,
"RSA_SHA256 signature scheme using SHA256 as hash algorithm." "RSA_SHA256 signature scheme using SHA256 as hash algorithm."
@ -140,13 +142,12 @@ object Crypto {
val EDDSA_ED25519_SHA512: SignatureScheme = SignatureScheme( val EDDSA_ED25519_SHA512: SignatureScheme = SignatureScheme(
4, 4,
"EDDSA_ED25519_SHA512", "EDDSA_ED25519_SHA512",
AlgorithmIdentifier(`id-Curve25519ph`, null), AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519, null),
emptyList(), // Both keys and the signature scheme use the same OID in i2p library. emptyList(), // Both keys and the signature scheme use the same OID.
// We added EdDSA to bouncy castle for certificate signing. sunEcProvider.name,
cordaBouncyCastleProvider.name, "Ed25519",
"1.3.101.112", "Ed25519",
EdDSAEngine.SIGNATURE_ALGORITHM, NamedParameterSpec.ED25519,
EdDSANamedCurveTable.getByName("ED25519"),
256, 256,
"EdDSA signature scheme using the ed25519 twisted Edwards curve." "EdDSA signature scheme using the ed25519 twisted Edwards curve."
) )
@ -164,11 +165,11 @@ object Crypto {
val SPHINCS256_SHA256 = SignatureScheme( val SPHINCS256_SHA256 = SignatureScheme(
5, 5,
"SPHINCS-256_SHA512", "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)))), listOf(AlgorithmIdentifier(BCObjectIdentifiers.sphincs256, DLSequence(arrayOf(ASN1Integer(0), SHA512_256)))),
bouncyCastlePQCProvider.name, bouncyCastlePQCProvider.name,
"SPHINCS256", "SPHINCS256",
"SHA512WITHSPHINCS256", "SHA512withSPHINCS256",
SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256), SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256),
256, 256,
"SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers " + "SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers " +
@ -244,8 +245,9 @@ object Crypto {
@JvmStatic @JvmStatic
fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme { fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme {
return algorithmMap[normaliseAlgorithmIdentifier(algorithm)] return requireNotNull(algorithmMap[normaliseAlgorithmIdentifier(algorithm)]) {
?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm.algorithm.id}") "Unrecognised algorithm identifier: ${algorithm.algorithm} ${algorithm.parameters}"
}
} }
/** Find [SignatureScheme] by platform specific schemeNumberID. */ /** Find [SignatureScheme] by platform specific schemeNumberID. */
@ -307,12 +309,11 @@ object Crypto {
@JvmStatic @JvmStatic
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
val keyInfo = PrivateKeyInfo.getInstance(encodedKey) val keyInfo = PrivateKeyInfo.getInstance(encodedKey)
if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) { return if (keyInfo.privateKeyAlgorithm.algorithm == ASN1ObjectIdentifier(CordaOID.ALIAS_PRIVATE_KEY)) {
return convertIfBCEdDSAPrivateKey(decodeAliasPrivateKey(keyInfo)) 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 { private fun decodeAliasPrivateKey(keyInfo: PrivateKeyInfo): PrivateKey {
@ -351,8 +352,7 @@ object Crypto {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
} }
try { try {
val keyFactory = keyFactory(signatureScheme) return signatureScheme.keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
return convertIfBCEdDSAPrivateKey(keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey)))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
throw InvalidKeySpecException("This private key cannot be decoded, please ensure it is PKCS8 encoded and that " + 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) "it corresponds to the input scheme's code name.", ikse)
@ -368,12 +368,11 @@ object Crypto {
*/ */
@JvmStatic @JvmStatic
fun decodePublicKey(encodedKey: ByteArray): PublicKey { fun decodePublicKey(encodedKey: ByteArray): PublicKey {
return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: { return PublicKeyCache.publicKeyForCachedBytes(ByteSequence.of(encodedKey)) ?: run {
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey)
val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm)
val keyFactory = keyFactory(signatureScheme) internPublicKey(signatureScheme.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))) }
}()
} }
@JvmStatic @JvmStatic
@ -412,8 +411,7 @@ object Crypto {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
} }
try { try {
val keyFactory = keyFactory(signatureScheme) return signatureScheme.keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))
return convertIfBCEdDSAPublicKey(keyFactory.generatePublic(X509EncodedKeySpec(encodedKey)))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
throw InvalidKeySpecException("This public key cannot be decoded, please ensure it is X509 encoded and " + 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) "that it corresponds to the input scheme's code name.", ikse)
@ -471,12 +469,8 @@ object Crypto {
return withSignature(signatureScheme) { signature -> return withSignature(signatureScheme) { signature ->
// Note that deterministic signature schemes, such as EdDSA, original SPHINCS-256 and RSA PKCS#1, do not require // 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 // 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 // SecureRandom implementation.
// ClassCastException if we invoke initSign with a SecureRandom as an input. if (signatureScheme == EDDSA_ED25519_SHA512 || signatureScheme == SPHINCS256_SHA256 || signatureScheme == RSA_SHA256) {
// 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) {
signature.initSign(privateKey) signature.initSign(privateKey)
} else { } else {
// The rest of the algorithms will require a SecureRandom input (i.e., ECDSA or any new algorithm for which // 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. * 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 * 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, * an implementation of the [HKDF rfc - Step 1: Extract function](https://tools.ietf.org/html/rfc5869),
* @see <a href="https://tools.ietf.org/html/rfc5869">HKDF</a>
* which is practically a variation of the private-parent-key -> private-child-key hardened key generation of BIP32. * 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 * 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. * 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, * 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. * it should be mentioned that the cryptographic strength of the HMAC depends upon the size of the secret key
* @see <a href="https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Security">HMAC Security</a> * (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 * Thus, as long as the master key is kept secret and has enough entropy (~256 bits for EC-schemes), the system
* is considered secure. * is considered secure.
* *
@ -743,9 +736,9 @@ object Crypto {
* <li>salt values should not be chosen by an attacker. * <li>salt values should not be chosen by an attacker.
* </ul></p> * </ul></p>
* *
* Regarding the last requirement, according to Krawczyk's HKDF scheme: <i>While there is no need to keep the salt secret, * 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</i>. * it is assumed that salt values are independent of the input keying material_
* @see <a href="http://eprint.iacr.org/2010/264.pdf">Cryptographic Extraction and Key Derivation - The HKDF Scheme</a>. * (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 * 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. * 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 { private fun deriveKeyPairEdDSA(privateKey: PrivateKey, seed: ByteArray): KeyPair {
// Compute HMAC(privateKey, seed). // Compute HMAC(privateKey, seed).
val macBytes = deriveHMAC(privateKey, seed) val macBytes = deriveHMAC(privateKey, seed)
return deriveEdDSAKeyPair(macBytes)
// 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))
} }
/** /**
@ -882,15 +869,20 @@ object Crypto {
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// Custom key pair generator from 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 // The entropy is transformed to a byte array in big-endian byte-order and
// only the first ed25519.field.getb() / 8 bytes are used. // only the first ed25519.field.getb() / 8 bytes are used.
private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair { private fun deriveEdDSAKeyPairFromEntropy(entropy: BigInteger): KeyPair {
val params = EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec return deriveEdDSAKeyPair(entropy.toByteArray().copyOf(Ed25519.PUBLIC_KEY_SIZE))
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) private fun deriveEdDSAKeyPair(bytes: ByteArray): KeyPair {
return KeyPair(internPublicKey(EdDSAPublicKey(pub)), EdDSAPrivateKey(priv)) 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, // 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 mac = Mac.getInstance("HmacSHA512", cordaBouncyCastleProvider)
val keyData = when (privateKey) { val keyData = when (privateKey) {
is BCECPrivateKey -> privateKey.d.toByteArray() 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") else -> throw InvalidKeyException("Key type ${privateKey.algorithm} is not supported for deterministic key derivation")
} }
val key = SecretKeySpec(keyData, "HmacSHA512") 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. * 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. * Point-at-infinity is not permitted as well.
* @see <a href="https://safecurves.cr.yp.to/twist.html">Small subgroup and invalid-curve attacks</a> 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. * 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 * 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 * 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. * 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 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]. * @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. * @return true if the point lies on the curve or false if it doesn't.
@ -954,17 +946,11 @@ object Crypto {
} }
return when (publicKey) { return when (publicKey) {
is BCECPublicKey -> publicKey.parameters == signatureScheme.algSpec && !publicKey.q.isInfinity && publicKey.q.isValid 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}") 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. */ /** Check if the requested [SignatureScheme] is supported by the system. */
@JvmStatic @JvmStatic
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean { 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). // 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 { private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean {
return when (key) { 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 BCRSAPublicKey -> key.modulus.bitLength() >= 2048 // Although the recommended RSA key size is 3072, we accept any key >= 2048bits.
is BCSphincs256PublicKey -> true is BCSphincs256PublicKey -> true
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}") else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
@ -991,21 +977,6 @@ object Crypto {
private val interner = PrivateInterner<PublicKey>() private val interner = PrivateInterner<PublicKey>()
private fun internPublicKey(key: PublicKey): PublicKey = PublicKeyCache.cachePublicKey(interner.intern(key)) 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. * Convert a public key to a supported implementation.
* @param key a public key. * @param key a public key.
@ -1031,9 +1002,9 @@ object Crypto {
is BCECPublicKey -> internPublicKey(key) is BCECPublicKey -> internPublicKey(key)
is BCRSAPublicKey -> internPublicKey(key) is BCRSAPublicKey -> internPublicKey(key)
is BCSphincs256PublicKey -> internPublicKey(key) is BCSphincs256PublicKey -> internPublicKey(key)
is EdDSAPublicKey -> internPublicKey(key) is EdECPublicKey -> internPublicKey(key)
is CompositeKey -> internPublicKey(key) is CompositeKey -> internPublicKey(key)
is BCEdDSAPublicKey -> convertIfBCEdDSAPublicKey(key) is BCEdDSAPublicKey -> internPublicKey(key)
else -> decodePublicKey(key.encoded) else -> decodePublicKey(key.encoded)
} }
} }
@ -1052,8 +1023,8 @@ object Crypto {
is BCECPrivateKey -> key is BCECPrivateKey -> key
is BCRSAPrivateKey -> key is BCRSAPrivateKey -> key
is BCSphincs256PrivateKey -> key is BCSphincs256PrivateKey -> key
is EdDSAPrivateKey -> key is EdECPrivateKey -> key
is BCEdDSAPrivateKey -> convertIfBCEdDSAPrivateKey(key) is BCEdDSAPrivateKey -> key
else -> decodePrivateKey(key.encoded) else -> decodePrivateKey(key.encoded)
} }
} }
@ -1095,8 +1066,4 @@ object Crypto {
private fun setBouncyCastleRNG() { private fun setBouncyCastleRNG() {
CryptoServicesRegistrar.setSecureRandom(newSecureRandom()) CryptoServicesRegistrar.setSecureRandom(newSecureRandom())
} }
private fun keyFactory(signatureScheme: SignatureScheme) = signatureScheme.getKeyFactory {
KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
}
} }

View File

@ -3,7 +3,8 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.contracts.PrivacySalt 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.SerializationDefaults
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
@ -19,6 +20,7 @@ import java.security.PublicKey
import java.security.SecureRandom import java.security.SecureRandom
import java.security.SecureRandomSpi import java.security.SecureRandomSpi
import java.security.SignatureException import java.security.SignatureException
import kotlin.math.abs
/** /**
* Utility to simplify the act of signing a byte array. * 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. * which should never happen and suggests an unusual JVM or non-standard Java library.
*/ */
@Throws(NoSuchAlgorithmException::class) @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 * 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 { fun random63BitValue(): Long {
while (true) { while (true) {
val candidate = Math.abs(newSecureRandom().nextLong()) val candidate = abs(newSecureRandom().nextLong())
// No need to check for -0L // No need to check for -0L
if (candidate != 0L && candidate != Long.MIN_VALUE) { if (candidate != 0L && candidate != Long.MIN_VALUE) {
return candidate return candidate

View File

@ -1,5 +1,6 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.internal.providerMap
import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import java.security.KeyFactory import java.security.KeyFactory
import java.security.Signature import java.security.Signature
@ -36,11 +37,6 @@ data class SignatureScheme(
@Volatile @Volatile
private var memoizedKeyFactory: KeyFactory? = null private var memoizedKeyFactory: KeyFactory? = null
internal fun getKeyFactory(factoryFactory: () -> KeyFactory): KeyFactory { internal val keyFactory: KeyFactory
return memoizedKeyFactory ?: run { get() = memoizedKeyFactory ?: KeyFactory.getInstance(algorithmName, providerMap[providerName]).also { memoizedKeyFactory = it }
val newFactory = factoryFactory()
memoizedKeyFactory = newFactory
newFactory
}
}
} }

View File

@ -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<ModP> {
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)"
}
}

View File

@ -26,9 +26,8 @@ object Instances {
private val signatureFactory: SignatureFactory = CachingSignatureFactory() 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. // 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?) { private data class SignatureKey(val algorithm: String, val providerName: String?, val providerVersion: String?) {
constructor(algorithm: String, provider: Provider?) : this(algorithm, provider?.name, constructor(algorithm: String, provider: Provider?) : this(algorithm, provider?.name, provider?.versionStr)
@Suppress("DEPRECATION") provider?.version) // JDK11: should replace with getVersionStr() (since 9)
} }
private class CachingSignatureFactory : SignatureFactory { private class CachingSignatureFactory : SignatureFactory {

View File

@ -2,8 +2,6 @@
package net.corda.core.crypto.internal package net.corda.core.crypto.internal
import io.netty.util.concurrent.FastThreadLocal 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 net.corda.core.utilities.loggerFor
import org.apache.commons.lang3.SystemUtils import org.apache.commons.lang3.SystemUtils
import java.io.DataInputStream import java.io.DataInputStream
@ -16,21 +14,8 @@ import java.security.SecureRandom
import java.security.SecureRandomSpi import java.security.SecureRandomSpi
import kotlin.system.exitProcess 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) 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 { companion object {
const val ALGORITHM = "CordaPRNG" const val ALGORITHM = "CordaPRNG"
@ -88,8 +73,3 @@ private class LinuxSecureRandomSpi : SecureRandomSpi() {
override fun engineGenerateSeed(numBytes: Int): ByteArray = ByteArray(numBytes).apply { engineNextBytes(this) } 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)
}

View File

@ -1,24 +1,17 @@
package net.corda.core.crypto.internal package net.corda.core.crypto.internal
import net.corda.core.crypto.CordaSecurityProvider 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.jce.provider.BouncyCastleProvider
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
import java.security.Provider import java.security.Provider
import java.security.SecureRandom
import java.security.Security import java.security.Security
import java.util.Collections.unmodifiableMap 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 { val cordaSecurityProvider = CordaSecurityProvider().also {
// Among the others, we should register [CordaSecurityProvider] as the first provider, to ensure that when invoking [SecureRandom()] // 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). // 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. Security.insertProviderAt(it, 1) // The position is 1-based.
} }
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00 val cordaBouncyCastleProvider = BouncyCastleProvider().also {
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.
Security.addProvider(it) 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 { 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. // 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. // The val is immutable to avoid any harmful state changes.
internal val providerMap: Map<String, Provider> = unmodifiableMap( internal val providerMap: Map<String, Provider> = unmodifiableMap(
listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider) listOf(sunEcProvider, cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider)
.associateByTo(LinkedHashMap(), Provider::getName) .associateByTo(LinkedHashMap(), Provider::getName)
) )
fun platformSecureRandomFactory(): SecureRandom = platformSecureRandom() // To minimise diff of CryptoUtils against open-source.

View File

@ -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
}

View File

@ -13,7 +13,7 @@ import java.util.*
class StatePointerSearch(val state: ContractState) { class StatePointerSearch(val state: ContractState) {
private companion object { private companion object {
// Classes in these packages should not be part of a search. // 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. // Type required for traversal.

View File

@ -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)
}

View File

@ -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)")
}
}

View File

@ -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())
);
}
}

View File

@ -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.Crypto.SPHINCS256_SHA256
import net.corda.core.crypto.internal.PlatformSecureRandomService import net.corda.core.crypto.internal.PlatformSecureRandomService
import net.corda.core.utilities.OpaqueBytes 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.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.assertThatIllegalArgumentException
import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey 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.ECNamedCurveTable
import org.bouncycastle.jce.interfaces.ECKey import org.bouncycastle.jce.interfaces.ECKey
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec 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.BCSphincs256PrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PublicKey
import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNotEquals
import org.junit.Ignore
import org.junit.Test import org.junit.Test
import java.math.BigInteger import java.math.BigInteger
import java.security.KeyPairGenerator import java.security.KeyPairGenerator
import java.security.SecureRandom import java.security.SecureRandom
import java.security.Security import java.security.Security
import java.security.interfaces.EdECPrivateKey
import java.security.interfaces.EdECPublicKey
import java.security.spec.NamedParameterSpec
import java.util.Random import java.util.Random
import kotlin.test.assertEquals import kotlin.test.assertEquals
import kotlin.test.assertFalse import kotlin.test.assertFalse
@ -132,11 +130,8 @@ class CryptoUtilsTest {
// test on malformed signatures (even if they change for 1 bit) // test on malformed signatures (even if they change for 1 bit)
signedData[0] = signedData[0].inc() signedData[0] = signedData[0].inc()
try { assertThatThrownBy {
Crypto.doVerify(pubKey, signedData, testBytes) Crypto.doVerify(pubKey, signedData, testBytes)
fail()
} catch (e: Exception) {
// expected
} }
} }
@ -498,9 +493,9 @@ class CryptoUtilsTest {
val (privEd, pubEd) = keyPairEd val (privEd, pubEd) = keyPairEd
assertEquals(privEd.algorithm, "EdDSA") 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.algorithm, "EdDSA")
assertEquals((pubEd as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519")) assertEquals((pubEd as EdECPublicKey).params.name, NamedParameterSpec.ED25519.name)
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@ -659,18 +654,23 @@ class CryptoUtilsTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `Check EdDSA public key on curve`() { fun `Check EdDSA public key on curve`() {
val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512) repeat(100) {
val pubEdDSA = keyPairEdDSA.public val keyPairEdDSA = Crypto.generateKeyPair(EDDSA_ED25519_SHA512)
assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA)) val pubEdDSA = keyPairEdDSA.public
// Use R1 curve for check. assertTrue(Crypto.publicKeyOnCurve(EDDSA_ED25519_SHA512, pubEdDSA))
assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA)) // Use R1 curve for check.
// Check for point at infinity. assertFalse(Crypto.publicKeyOnCurve(ECDSA_SECP256R1_SHA256, pubEdDSA))
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))) 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) @Test(timeout = 300_000)
@Ignore("TODO JDK17: Fixme")
fun `Unsupported EC public key type on curve`() { fun `Unsupported EC public key type on curve`() {
val keyGen = KeyPairGenerator.getInstance("EC") // sun.security.ec.ECPublicKeyImpl val keyGen = KeyPairGenerator.getInstance("EC") // sun.security.ec.ECPublicKeyImpl
keyGen.initialize(256, newSecureRandom()) keyGen.initialize(256, newSecureRandom())
@ -772,10 +772,8 @@ class CryptoUtilsTest {
// Check scheme. // Check scheme.
assertEquals(priv.algorithm, dpriv.algorithm) assertEquals(priv.algorithm, dpriv.algorithm)
assertEquals(pub.algorithm, dpub.algorithm) assertEquals(pub.algorithm, dpub.algorithm)
assertTrue(dpriv is EdDSAPrivateKey) assertEquals((dpriv as EdECPrivateKey).params.name, NamedParameterSpec.ED25519.name)
assertTrue(dpub is EdDSAPublicKey) assertEquals((dpub as EdECPublicKey).params.name, NamedParameterSpec.ED25519.name)
assertEquals((dpriv as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
assertEquals((dpub as EdDSAKey).params, EdDSANamedCurveTable.getByName("ED25519"))
assertEquals(Crypto.findSignatureScheme(dpriv), EDDSA_ED25519_SHA512) assertEquals(Crypto.findSignatureScheme(dpriv), EDDSA_ED25519_SHA512)
assertEquals(Crypto.findSignatureScheme(dpub), EDDSA_ED25519_SHA512) assertEquals(Crypto.findSignatureScheme(dpub), EDDSA_ED25519_SHA512)

View File

@ -1,17 +1,16 @@
package net.corda.core.crypto 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.hexToByteArray
import net.corda.core.utilities.toHex import net.corda.core.utilities.toHex
import net.i2p.crypto.eddsa.EdDSAPrivateKey import org.assertj.core.api.Assertions.assertThat
import net.i2p.crypto.eddsa.EdDSASecurityProvider import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import org.junit.Test import org.junit.Test
import java.security.PrivateKey import java.security.PrivateKey
import java.security.Signature import java.security.spec.EdECPrivateKeySpec
import java.util.Locale import java.security.spec.NamedParameterSpec
import kotlin.test.assertEquals import java.security.spec.X509EncodedKeySpec
import kotlin.test.assertNotEquals
/** /**
* Testing PureEdDSA Ed25519 using test vectors from https://tools.ietf.org/html/rfc8032#section-7.1 * 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 { class EdDSATests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `PureEdDSA Ed25519 test vectors`() { fun `PureEdDSA Ed25519 test vectors`() {
val edParams = Crypto.EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec
// MESSAGE (length 0 bytes). // MESSAGE (length 0 bytes).
val testVector1 = SignatureTestVector( val testVector1 = SignatureTestVector(
"9d61b19deffd5a60ba844af492ec2cc4" + "9d61b19deffd5a60ba844af492ec2cc4" +
@ -152,11 +149,24 @@ class EdDSATests {
"3dca179c138ac17ad9bef1177331a704" "3dca179c138ac17ad9bef1177331a704"
) )
val keyFactory = EDDSA_ED25519_SHA512.keyFactory
val testVectors = listOf(testVector1, testVector2, testVector3, testVector1024, testVectorSHAabc) val testVectors = listOf(testVector1, testVector2, testVector3, testVector1024, testVectorSHAabc)
testVectors.forEach { testVectors.forEach { testVector ->
val privateKey = EdDSAPrivateKey(EdDSAPrivateKeySpec(it.privateKeyHex.hexToByteArray(), edParams)) val messageBytes = testVector.messageToSignHex.hexToByteArray()
assertEquals(it.signatureOutputHex, doSign(privateKey, it.messageToSignHex.hexToByteArray()).toHex() val signatureBytes = testVector.signatureOutputHex.hexToByteArray()
.lowercase(Locale.getDefault())) // 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. // Test vector for the variant Ed25519ctx, expected to fail.
@ -172,9 +182,8 @@ class EdDSATests {
"5a5ca2df6668346291c2043d4eb3e90d" "5a5ca2df6668346291c2043d4eb3e90d"
) )
val privateKey = EdDSAPrivateKey(EdDSAPrivateKeySpec(testVectorEd25519ctx.privateKeyHex.hexToByteArray(), edParams)) val privateKey = keyFactory.generatePrivate(EdECPrivateKeySpec(NamedParameterSpec.ED25519, testVectorEd25519ctx.privateKeyHex.hexToByteArray()))
assertNotEquals(testVectorEd25519ctx.signatureOutputHex, doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex() assertThat(doSign(privateKey, testVectorEd25519ctx.messageToSignHex.hexToByteArray()).toHex().lowercase()).isNotEqualTo(testVectorEd25519ctx.signatureOutputHex)
.lowercase(Locale.getDefault()))
} }
/** A test vector object for digital signature schemes. */ /** 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). // 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 { private fun doSign(privateKey: PrivateKey, clearData: ByteArray): ByteArray {
val signature = Signature.getInstance(Crypto.EDDSA_ED25519_SHA512.signatureName, EdDSASecurityProvider()) return withSignature(EDDSA_ED25519_SHA512) { signature ->
signature.initSign(privateKey) signature.initSign(privateKey)
signature.update(clearData) signature.update(clearData)
return signature.sign() signature.sign()
}
} }
} }

View File

@ -1426,7 +1426,6 @@
<ID>TooManyFunctions:ActionExecutorImpl.kt$ActionExecutorImpl : ActionExecutor</ID> <ID>TooManyFunctions:ActionExecutorImpl.kt$ActionExecutorImpl : ActionExecutor</ID>
<ID>TooManyFunctions:AppendOnlyPersistentMap.kt$AppendOnlyPersistentMapBase&lt;K, V, E, out EK&gt;</ID> <ID>TooManyFunctions:AppendOnlyPersistentMap.kt$AppendOnlyPersistentMapBase&lt;K, V, E, out EK&gt;</ID>
<ID>TooManyFunctions:ArtemisTcpTransport.kt$ArtemisTcpTransport$Companion</ID> <ID>TooManyFunctions:ArtemisTcpTransport.kt$ArtemisTcpTransport$Companion</ID>
<ID>TooManyFunctions:BCCryptoService.kt$BCCryptoService : CryptoService</ID>
<ID>TooManyFunctions:BFTSmart.kt$BFTSmart$Replica : DefaultRecoverable</ID> <ID>TooManyFunctions:BFTSmart.kt$BFTSmart$Replica : DefaultRecoverable</ID>
<ID>TooManyFunctions:BaseTransaction.kt$BaseTransaction : NamedByHash</ID> <ID>TooManyFunctions:BaseTransaction.kt$BaseTransaction : NamedByHash</ID>
<ID>TooManyFunctions:ClassCarpenter.kt$ClassCarpenterImpl : ClassCarpenter</ID> <ID>TooManyFunctions:ClassCarpenter.kt$ClassCarpenterImpl : ClassCarpenter</ID>
@ -1669,9 +1668,6 @@
<ID>WildcardImport:AttachmentsClassLoader.kt$import net.corda.core.serialization.*</ID> <ID>WildcardImport:AttachmentsClassLoader.kt$import net.corda.core.serialization.*</ID>
<ID>WildcardImport:AttachmentsClassLoaderStaticContractTests.kt$import net.corda.core.contracts.*</ID> <ID>WildcardImport:AttachmentsClassLoaderStaticContractTests.kt$import net.corda.core.contracts.*</ID>
<ID>WildcardImport:AutoOfferFlow.kt$import net.corda.core.flows.*</ID> <ID>WildcardImport:AutoOfferFlow.kt$import net.corda.core.flows.*</ID>
<ID>WildcardImport:BCCryptoService.kt$import java.security.*</ID>
<ID>WildcardImport:BCCryptoService.kt$import net.corda.nodeapi.internal.cryptoservice.*</ID>
<ID>WildcardImport:BCCryptoServiceTests.kt$import java.security.*</ID>
<ID>WildcardImport:BFTNotaryServiceTests.kt$import net.corda.core.crypto.*</ID> <ID>WildcardImport:BFTNotaryServiceTests.kt$import net.corda.core.crypto.*</ID>
<ID>WildcardImport:BFTNotaryServiceTests.kt$import net.corda.testing.node.internal.*</ID> <ID>WildcardImport:BFTNotaryServiceTests.kt$import net.corda.testing.node.internal.*</ID>
<ID>WildcardImport:BFTSmart.kt$import net.corda.core.crypto.*</ID> <ID>WildcardImport:BFTSmart.kt$import net.corda.core.crypto.*</ID>

View File

@ -15,7 +15,6 @@ dependencies {
testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}" testImplementation "org.junit.jupiter:junit-jupiter-api:${junit_jupiter_version}"
testImplementation "junit:junit:$junit_version" testImplementation "junit:junit:$junit_version"
testImplementation "org.mockito.kotlin:mockito-kotlin:$mockito_kotlin_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 "com.typesafe:config:$typesafe_config_version"
testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version" testImplementation "io.dropwizard.metrics:metrics-core:$metrics_version"
testImplementation "co.paralleluniverse:quasar-core:$quasar_version" testImplementation "co.paralleluniverse:quasar-core:$quasar_version"

View File

@ -1,6 +1,5 @@
package net.corda.nodeapitests.internal.crypto package net.corda.nodeapitests.internal.crypto
import io.netty.handler.ssl.ClientAuth import io.netty.handler.ssl.ClientAuth
import io.netty.handler.ssl.SslContextBuilder import io.netty.handler.ssl.SslContextBuilder
import io.netty.handler.ssl.SslProvider 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.core.TestIdentity
import net.corda.testing.driver.internal.incrementalPortAllocation import net.corda.testing.driver.internal.incrementalPortAllocation
import net.corda.testing.internal.createDevIntermediateCaCertPath import net.corda.testing.internal.createDevIntermediateCaCertPath
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier
import org.bouncycastle.asn1.x509.BasicConstraints 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.Extension
import org.bouncycastle.asn1.x509.KeyUsage import org.bouncycastle.asn1.x509.KeyUsage
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier import org.bouncycastle.asn1.x509.SubjectKeyIdentifier
import org.bouncycastle.jcajce.provider.asymmetric.edec.BCEdDSAPrivateKey
import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey import org.bouncycastle.pqc.jcajce.provider.sphincs.BCSphincs256PrivateKey
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -77,7 +73,8 @@ import java.security.KeyPair
import java.security.PrivateKey import java.security.PrivateKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.security.interfaces.EdECPrivateKey
import java.util.Date
import javax.net.ssl.SSLContext import javax.net.ssl.SSLContext
import javax.net.ssl.SSLParameters import javax.net.ssl.SSLParameters
import javax.net.ssl.SSLServerSocket import javax.net.ssl.SSLServerSocket
@ -93,7 +90,6 @@ import kotlin.test.assertNull
import kotlin.test.assertTrue import kotlin.test.assertTrue
import kotlin.test.fail import kotlin.test.fail
@Ignore("TODO JDK17: Fixme")
class X509UtilitiesTest { class X509UtilitiesTest {
private companion object { private companion object {
val ALICE = TestIdentity(ALICE_NAME, 70).party val ALICE = TestIdentity(ALICE_NAME, 70).party
@ -122,9 +118,9 @@ class X509UtilitiesTest {
val schemeToKeyTypes = listOf( val schemeToKeyTypes = listOf(
// By default, JKS returns SUN EC key. // 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_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(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(EDDSA_ED25519_SHA512, EdECPrivateKey::class.java, EdECPrivateKey::class.java),
// By default, JKS returns SUN RSA key. // By default, JKS returns SUN RSA key.
Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java) Triple(SPHINCS256_SHA256, BCSphincs256PrivateKey::class.java, BCSphincs256PrivateKey::class.java)
) )
@ -136,8 +132,7 @@ class X509UtilitiesTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `create valid self-signed CA certificate`() { fun `create valid self-signed CA certificate`() {
Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { validSelfSignedCertificate(it) }
&& ( it != SPHINCS256_SHA256)}.forEach { validSelfSignedCertificate(it) }
} }
private fun validSelfSignedCertificate(signatureScheme: SignatureScheme) { private fun validSelfSignedCertificate(signatureScheme: SignatureScheme) {
@ -158,7 +153,7 @@ class X509UtilitiesTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `load and save a PEM file certificate`() { fun `load and save a PEM file certificate`() {
Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach { loadSavePEMCert(it) } Crypto.supportedSignatureSchemes().filter { it != COMPOSITE_KEY }.forEach(::loadSavePEMCert)
} }
private fun loadSavePEMCert(signatureScheme: SignatureScheme) { private fun loadSavePEMCert(signatureScheme: SignatureScheme) {
@ -172,8 +167,7 @@ class X509UtilitiesTest {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `create valid server certificate chain`() { fun `create valid server certificate chain`() {
certChainSchemeCombinations.filter{ it.first != SPHINCS256_SHA256 } certChainSchemeCombinations.forEach { createValidServerCertChain(it.first, it.second) }
.forEach { createValidServerCertChain(it.first, it.second) }
} }
private fun createValidServerCertChain(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) { private fun createValidServerCertChain(signatureSchemeRoot: SignatureScheme, signatureSchemeChild: SignatureScheme) {
@ -451,13 +445,11 @@ class X509UtilitiesTest {
schemeToKeyTypes.forEach { getCorrectKeyFromKeystore(it.first, it.second, it.third) } schemeToKeyTypes.forEach { getCorrectKeyFromKeystore(it.first, it.second, it.third) }
} }
private fun <U, C> getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, uncastedClass: Class<U>, castedClass: Class<C>) { private fun <R, S> getCorrectKeyFromKeystore(signatureScheme: SignatureScheme, rawClass: Class<R>, supportedClass: Class<S>) {
val keyPair = generateKeyPair(signatureScheme) val keyPair = generateKeyPair(signatureScheme)
val (keyFromKeystore, keyFromKeystoreCasted) = storeAndGetKeysFromKeystore(keyPair) val (rawKey, supportedKey) = storeAndGetKeysFromKeystore(keyPair)
if (uncastedClass == EdDSAPrivateKey::class.java && keyFromKeystore !is BCEdDSAPrivateKey) { assertThat(rawKey).isInstanceOf(rawClass)
assertThat(keyFromKeystore).isInstanceOf(uncastedClass) assertThat(supportedKey).isInstanceOf(supportedClass)
}
assertThat(keyFromKeystoreCasted).isInstanceOf(castedClass)
} }
private fun storeAndGetKeysFromKeystore(keyPair: KeyPair): Pair<Key, PrivateKey> { private fun storeAndGetKeysFromKeystore(keyPair: KeyPair): Pair<Key, PrivateKey> {
@ -466,9 +458,9 @@ class X509UtilitiesTest {
val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword") val keyStore = loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert)) keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert))
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray()) val rawKey = keyStore.getKey("Key", "keypassword".toCharArray())
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword") val supportedKey = keyStore.getSupportedKey("Key", "keypassword")
return Pair(keyFromKeystore, keyFromKeystoreCasted) return Pair(rawKey, supportedKey)
} }
@Test(timeout=300_000) @Test(timeout=300_000)

View File

@ -57,7 +57,6 @@ dependencies {
implementation "io.reactivex:rxjava:$rxjava_version" implementation "io.reactivex:rxjava:$rxjava_version"
implementation "javax.persistence:javax.persistence-api:2.2" implementation "javax.persistence:javax.persistence-api:2.2"
implementation "org.hibernate:hibernate-core:$hibernate_version" implementation "org.hibernate:hibernate-core:$hibernate_version"
implementation "net.i2p.crypto:eddsa:$eddsa_version"
implementation "co.paralleluniverse:quasar-osgi-annotations:$quasar_version" implementation "co.paralleluniverse:quasar-osgi-annotations:$quasar_version"
runtimeOnly 'com.mattbertolini:liquibase-slf4j:2.0.0' runtimeOnly 'com.mattbertolini:liquibase-slf4j:2.0.0'

View File

@ -78,7 +78,7 @@ interface CertificateStore : Iterable<Pair<String, X509Certificate>> {
} }
fun setCertPathOnly(alias: String, certificates: List<X509Certificate>) { fun setCertPathOnly(alias: String, certificates: List<X509Certificate>) {
// 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. // 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 // This is because KeyStores do not support updateKeyEntry and thus we cannot update the certificate chain
// without overriding the key entry. // without overriding the key entry.

View File

@ -1,6 +1,5 @@
package net.corda.nodeapi.internal.crypto 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.SignatureScheme
import net.corda.core.crypto.internal.Instances import net.corda.core.crypto.internal.Instances
import org.bouncycastle.asn1.x509.AlgorithmIdentifier import org.bouncycastle.asn1.x509.AlgorithmIdentifier
@ -27,9 +26,7 @@ object ContentSignerBuilder {
val sig = try { val sig = try {
signatureInstance.apply { signatureInstance.apply {
// TODO special handling for Sphincs due to a known BouncyCastle's Sphincs bug we reported. if (random != null) {
// It is fixed in BC 161b12, so consider updating the below if-statement after updating BouncyCastle.
if (random != null && signatureScheme != SPHINCS256_SHA256) {
initSign(privateKey, random) initSign(privateKey, random)
} else { } else {
initSign(privateKey) initSign(privateKey)
@ -48,7 +45,7 @@ object ContentSignerBuilder {
private class SignatureOutputStream(private val sig: Signature, private val optimised: Boolean) : OutputStream() { private class SignatureOutputStream(private val sig: Signature, private val optimised: Boolean) : OutputStream() {
private var alreadySigned = false private var alreadySigned = false
internal val signature: ByteArray by lazy { val signature: ByteArray by lazy {
try { try {
alreadySigned = true alreadySigned = true
sig.sign() sig.sign()

View File

@ -69,7 +69,7 @@ import kotlin.io.path.reader
import kotlin.io.path.writer import kotlin.io.path.writer
object X509Utilities { 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`). // schemes (for instance `UtimacoCryptoService.DEFAULT_IDENTITY_SIGNATURE_SCHEME`).
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512 val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256 val DEFAULT_TLS_SIGNATURE_SCHEME = Crypto.ECDSA_SECP256R1_SHA256
@ -303,10 +303,10 @@ object X509Utilities {
crlDistPoint: String? = null, crlDistPoint: String? = null,
crlIssuer: X500Name? = null): X509Certificate { crlIssuer: X500Name? = null): X509Certificate {
val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer) val builder = createPartialCertificate(certificateType, issuer, issuerPublicKey, subject, subjectPublicKey, validityWindow, nameConstraints, crlDistPoint, crlIssuer)
return builder.build(issuerSigner).run { val certificate = builder.build(issuerSigner).toJca()
require(isValidOn(Date())){"Certificate is not valid at instant now"} certificate.checkValidity(Date())
toJca() certificate.verify(issuerPublicKey)
} return certificate
} }
/** /**
@ -340,18 +340,22 @@ object X509Utilities {
validityWindow, validityWindow,
nameConstraints, nameConstraints,
crlDistPoint, crlDistPoint,
crlIssuer) crlIssuer
return builder.build(signer).run { )
require(isValidOn(Date())){"Certificate is not valid at instant now"} val certificate = builder.build(signer).toJca()
require(isSignatureValid(JcaContentVerifierProviderBuilder().build(issuerKeyPair.public))){"Invalid signature"} certificate.checkValidity(Date())
toJca() certificate.verify(issuerKeyPair.public)
} return certificate
} }
/** /**
* Create certificate signing request using provided information. * 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) return JcaPKCS10CertificationRequestBuilder(subject, publicKey)
.addAttribute(BCStyle.E, DERUTF8String(email)) .addAttribute(BCStyle.E, DERUTF8String(email))
.addAttribute(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), certRole) .addAttribute(ASN1ObjectIdentifier(CordaOID.X509_EXTENSION_CORDA_ROLE), certRole)
@ -410,7 +414,7 @@ object X509Utilities {
} }
// Assuming cert type to role is 1:1 // 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]. * Convert a [X509Certificate] into BouncyCastle's [X509CertificateHolder].

View File

@ -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.Crypto
import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.internal.Instances.getSignatureInstance import net.corda.core.crypto.internal.Instances.withSignature
import net.corda.core.crypto.internal.cordaBouncyCastleProvider
import net.corda.core.crypto.newSecureRandom import net.corda.core.crypto.newSecureRandom
import net.corda.core.crypto.sha256 import net.corda.core.crypto.sha256
import net.corda.core.utilities.detailedLogger 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.X509Utilities
import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore import net.corda.nodeapi.internal.crypto.loadOrCreateKeyStore
import net.corda.nodeapi.internal.crypto.save 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 org.bouncycastle.operator.ContentSigner
import java.nio.file.Path 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 java.security.spec.ECGenParameterSpec
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.crypto.KeyGenerator import javax.crypto.KeyGenerator
import javax.security.auth.x500.X500Principal 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. * 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]) * The [wrappingKeyStorePath] must be provided in order to execute any wrapping operations (e.g. [createWrappingKey], [generateWrappedKeyPair])
*/ */
class BCCryptoService(private val legalName: X500Principal, @Suppress("TooManyFunctions")
private val certificateStoreSupplier: CertificateStoreSupplier, class DefaultCryptoService(private val legalName: X500Principal,
private val wrappingKeyStorePath: Path? = null) : CryptoService { private val certificateStoreSupplier: CertificateStoreSupplier,
private val wrappingKeyStorePath: Path? = null) : CryptoService {
private companion object { private companion object {
val detailedLogger = detailedLogger() val detailedLogger = detailedLogger()
@ -97,7 +99,7 @@ class BCCryptoService(private val legalName: X500Principal,
private fun signWithAlgorithm(alias: String, data: ByteArray, signAlgorithm: String): ByteArray { private fun signWithAlgorithm(alias: String, data: ByteArray, signAlgorithm: String): ByteArray {
val privateKey = certificateStore.query { getPrivateKey(alias, certificateStore.entryPassword) } 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)" } detailedLogger.trace { "CryptoService(action=signing_start;alias=$alias;algorithm=$signAlgorithm)" }
signature.initSign(privateKey, newSecureRandom()) signature.initSign(privateKey, newSecureRandom())
signature.update(data) 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] * 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. * loaded [certificateStore] in memory with the contents of the corresponding [KeyStore] file.
*/ */
fun resyncKeystore() { fun resyncKeystore() {
@ -178,7 +180,7 @@ class BCCryptoService(private val legalName: X500Principal,
} }
val wrappingKey = wrappingKeyStore.getKey(masterKeyAlias, certificateStore.entryPassword.toCharArray()) 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) cipher.init(Cipher.WRAP_MODE, wrappingKey)
val keyPairGenerator = keyPairGeneratorFromScheme(childKeyScheme) val keyPairGenerator = keyPairGeneratorFromScheme(childKeyScheme)
@ -199,22 +201,23 @@ class BCCryptoService(private val legalName: X500Principal,
1 -> "AESWRAPPAD" 1 -> "AESWRAPPAD"
else -> "AES" else -> "AES"
} }
val cipher = Cipher.getInstance(algorithm, cordaBouncyCastleProvider) val cipher = Cipher.getInstance(algorithm)
cipher.init(Cipher.UNWRAP_MODE, wrappingKey) cipher.init(Cipher.UNWRAP_MODE, wrappingKey)
val privateKey = cipher.unwrap(wrappedPrivateKey.keyMaterial, keyAlgorithmFromScheme(wrappedPrivateKey.signatureScheme), Cipher.PRIVATE_KEY) as PrivateKey val privateKey = cipher.unwrap(wrappedPrivateKey.keyMaterial, keyAlgorithmFromScheme(wrappedPrivateKey.signatureScheme), Cipher.PRIVATE_KEY) as PrivateKey
val signature = getSignatureInstance(wrappedPrivateKey.signatureScheme.signatureName, cordaBouncyCastleProvider) return withSignature(wrappedPrivateKey.signatureScheme) { signature ->
signature.initSign(privateKey, newSecureRandom()) signature.initSign(privateKey, newSecureRandom())
signature.update(payloadToSign) signature.update(payloadToSign)
return signature.sign() signature.sign()
}
} }
override fun getWrappingMode(): WrappingMode? = WrappingMode.DEGRADED_WRAPPED override fun getWrappingMode(): WrappingMode = WrappingMode.DEGRADED_WRAPPED
private fun keyPairGeneratorFromScheme(scheme: SignatureScheme): KeyPairGenerator { private fun keyPairGeneratorFromScheme(scheme: SignatureScheme): KeyPairGenerator {
val algorithm = keyAlgorithmFromScheme(scheme) val algorithm = keyAlgorithmFromScheme(scheme)
val keyPairGenerator = KeyPairGenerator.getInstance(algorithm, cordaBouncyCastleProvider) val keyPairGenerator = KeyPairGenerator.getInstance(algorithm)
when (scheme) { when (scheme) {
Crypto.ECDSA_SECP256R1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256r1")) Crypto.ECDSA_SECP256R1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256r1"))
Crypto.ECDSA_SECP256K1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256k1")) Crypto.ECDSA_SECP256K1_SHA256 -> keyPairGenerator.initialize(ECGenParameterSpec("secp256k1"))

View File

@ -20,7 +20,6 @@ import de.javakaffee.kryoserializers.guava.ImmutableSortedSetSerializer
import net.corda.core.contracts.ContractAttachment import net.corda.core.contracts.ContractAttachment
import net.corda.core.contracts.ContractClassName import net.corda.core.contracts.ContractClassName
import net.corda.core.contracts.PrivacySalt import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.CompositeKey
import net.corda.core.crypto.SecureHash import net.corda.core.crypto.SecureHash
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.internal.AbstractAttachment 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.DefaultWhitelist
import net.corda.serialization.internal.GeneratedAttachment import net.corda.serialization.internal.GeneratedAttachment
import net.corda.serialization.internal.MutableClassWhitelist 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.instantiator.ObjectInstantiator
import org.objenesis.strategy.InstantiatorStrategy import org.objenesis.strategy.InstantiatorStrategy
import org.objenesis.strategy.StdInstantiatorStrategy import org.objenesis.strategy.StdInstantiatorStrategy
@ -62,14 +53,13 @@ import java.security.PublicKey
import java.security.cert.CertPath import java.security.cert.CertPath
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.* import java.util.*
import kotlin.collections.ArrayList
object DefaultKryoCustomizer { object DefaultKryoCustomizer {
private val serializationWhitelists: List<SerializationWhitelist> by lazy { private val serializationWhitelists: List<SerializationWhitelist> by lazy {
ServiceLoader.load(SerializationWhitelist::class.java, this.javaClass.classLoader).toList() + DefaultWhitelist ServiceLoader.load(SerializationWhitelist::class.java, this.javaClass.classLoader).toList() + DefaultWhitelist
} }
fun customize(kryo: Kryo, publicKeySerializer: Serializer<PublicKey> = PublicKeySerializer): Kryo { fun customize(kryo: Kryo): Kryo {
return kryo.apply { return kryo.apply {
isRegistrationRequired = false isRegistrationRequired = false
references = true references = true
@ -110,6 +100,8 @@ object DefaultKryoCustomizer {
// Please add any new registrations to the end. // Please add any new registrations to the end.
addDefaultSerializer(LinkedHashMapIteratorSerializer.getIterator()::class.java.superclass, LinkedHashMapIteratorSerializer) addDefaultSerializer(LinkedHashMapIteratorSerializer.getIterator()::class.java.superclass, LinkedHashMapIteratorSerializer)
addDefaultSerializer(PublicKey::class.java, PublicKeySerializer)
addDefaultSerializer(PrivateKey::class.java, PrivateKeySerializer)
register(LinkedHashMapEntrySerializer.getEntry()::class.java, LinkedHashMapEntrySerializer) register(LinkedHashMapEntrySerializer.getEntry()::class.java, LinkedHashMapEntrySerializer)
register(LinkedListItrSerializer.getListItr()::class.java, LinkedListItrSerializer) register(LinkedListItrSerializer.getListItr()::class.java, LinkedListItrSerializer)
register(Arrays.asList("").javaClass, ArraysAsListSerializer()) register(Arrays.asList("").javaClass, ArraysAsListSerializer())
@ -126,11 +118,6 @@ object DefaultKryoCustomizer {
// InputStream subclasses whitelisting, required for attachments. // InputStream subclasses whitelisting, required for attachments.
register(BufferedInputStream::class.java, InputStreamSerializer) register(BufferedInputStream::class.java, InputStreamSerializer)
register(Class.forName("sun.net.www.protocol.jar.JarURLConnection\$JarURLInputStream"), 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. // Exceptions. We don't bother sending the stack traces as the client will fill in its own anyway.
register(Array<StackTraceElement>::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> }) register(Array<StackTraceElement>::class, read = { _, _ -> emptyArray() }, write = { _, _, _ -> })
// This ensures a NonEmptySetSerializer is constructed with an initial value. // This ensures a NonEmptySetSerializer is constructed with an initial value.
@ -139,12 +126,6 @@ object DefaultKryoCustomizer {
register(Class::class.java, ClassSerializer) register(Class::class.java, ClassSerializer)
register(FileInputStream::class.java, InputStreamSerializer) register(FileInputStream::class.java, InputStreamSerializer)
register(CertPath::class.java, CertPathSerializer) 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(NotaryChangeWireTransaction::class.java, NotaryChangeWireTransactionSerializer)
register(PartyAndCertificate::class.java, PartyAndCertificateSerializer) register(PartyAndCertificate::class.java, PartyAndCertificateSerializer)

View File

@ -28,7 +28,6 @@ import net.corda.core.transactions.NotaryChangeWireTransaction
import net.corda.core.transactions.SignedTransaction import net.corda.core.transactions.SignedTransaction
import net.corda.core.transactions.WireTransaction import net.corda.core.transactions.WireTransaction
import net.corda.core.utilities.OpaqueBytes import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.SgxSupport
import net.corda.serialization.internal.serializationContextKey import net.corda.serialization.internal.serializationContextKey
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
@ -83,11 +82,8 @@ class ImmutableClassSerializer<T : Any>(val klass: KClass<T>) : Serializer<T>()
init { init {
// Verify that this class is immutable (all properties are final). // Verify that this class is immutable (all properties are final).
// We disable this check inside SGX as the reflection blows up. props.forEach {
if (!SgxSupport.isInsideEnclave) { 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" }
}
} }
} }

View File

@ -6,16 +6,15 @@ import net.corda.core.identity.CordaX500Name
import net.corda.core.identity.PartyAndCertificate import net.corda.core.identity.PartyAndCertificate
import net.corda.core.node.NodeInfo import net.corda.core.node.NodeInfo
import net.corda.core.utilities.NetworkHostAndPort 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.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.testing.core.ALICE_NAME import net.corda.testing.core.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import net.corda.testing.core.SerializationEnvironmentRule 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.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import java.security.KeyPair import java.security.KeyPair
@ -56,7 +55,6 @@ class SignedNodeInfoTest {
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@Ignore("TODO JDK17: Fixme")
fun `verifying composite keys only`() { fun `verifying composite keys only`() {
val aliceKeyPair = generateKeyPair() val aliceKeyPair = generateKeyPair()
val bobKeyPair = generateKeyPair() val bobKeyPair = generateKeyPair()

View File

@ -28,6 +28,6 @@ class ContentSignerBuilderTest {
.isThrownBy { .isThrownBy {
ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
} }
.withMessage("Incorrect key type EC for signature scheme NONEwithEdDSA") .withMessage("Incorrect key type EC for signature scheme Ed25519")
} }
} }

View File

@ -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.Crypto
import net.corda.core.crypto.SignatureScheme import net.corda.core.crypto.SignatureScheme
import net.corda.core.crypto.internal.cordaBouncyCastleProvider import net.corda.core.crypto.internal.cordaBouncyCastleProvider
import net.corda.core.utilities.days 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.config.CertificateStoreSupplier
import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.CertificateType
import net.corda.nodeapi.internal.crypto.X509Utilities 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.nodeapi.internal.crypto.loadOrCreateKeyStore
import net.corda.testing.core.ALICE_NAME
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.junit.Before import org.junit.Before
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
import java.io.FileOutputStream import java.io.FileOutputStream
import java.nio.file.Path 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.security.spec.ECGenParameterSpec
import java.time.Duration import java.time.Duration
import java.util.* import java.util.UUID
import javax.crypto.Cipher import javax.crypto.Cipher
import javax.security.auth.x500.X500Principal import javax.security.auth.x500.X500Principal
import kotlin.io.path.div import kotlin.io.path.div
@ -35,7 +34,7 @@ import kotlin.test.assertFailsWith
import kotlin.test.assertFalse import kotlin.test.assertFalse
import kotlin.test.assertTrue import kotlin.test.assertTrue
class BCCryptoServiceTests { class DefaultCryptoServiceTests {
companion object { companion object {
val clearData = "data".toByteArray() val clearData = "data".toByteArray()
} }
@ -61,15 +60,13 @@ class BCCryptoServiceTests {
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@Ignore("TODO JDK17: Fixme") fun `cryptoService generate key pair and sign both data and cert`() {
fun `BCCryptoService generate key pair and sign both data and cert`() { val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
// Testing every supported scheme. // Testing every supported scheme.
Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY }.forEach { generateKeyAndSignForScheme(cryptoService, it) }
&& it.signatureName != "SHA512WITHSPHINCS256"}.forEach { generateKeyAndSignForScheme(cryptoService, it) }
} }
private fun generateKeyAndSignForScheme(cryptoService: BCCryptoService, signatureScheme: SignatureScheme) { private fun generateKeyAndSignForScheme(cryptoService: DefaultCryptoService, signatureScheme: SignatureScheme) {
val alias = "signature${signatureScheme.schemeNumberID}" val alias = "signature${signatureScheme.schemeNumberID}"
val pubKey = cryptoService.generateKeyPair(alias, signatureScheme) val pubKey = cryptoService.generateKeyPair(alias, signatureScheme)
assertTrue { cryptoService.containsKey(alias) } assertTrue { cryptoService.containsKey(alias) }
@ -95,12 +92,10 @@ class BCCryptoServiceTests {
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@Ignore("TODO JDK17: Fixme") fun `cryptoService generate key pair and sign with existing schemes`() {
fun `BCCryptoService generate key pair and sign with existing schemes`() { val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
// Testing every supported scheme. // Testing every supported scheme.
Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY Crypto.supportedSignatureSchemes().filter { it != Crypto.COMPOSITE_KEY }.forEach {
&& it.signatureName != "SHA512WITHSPHINCS256"}.forEach {
val alias = "signature${it.schemeNumberID}" val alias = "signature${it.schemeNumberID}"
val pubKey = cryptoService.generateKeyPair(alias, it) val pubKey = cryptoService.generateKeyPair(alias, it)
assertTrue { cryptoService.containsKey(alias) } assertTrue { cryptoService.containsKey(alias) }
@ -110,9 +105,7 @@ class BCCryptoServiceTests {
} }
@Test(timeout=300_000) @Test(timeout=300_000)
@Ignore("TODO JDK17: Fixme") fun `cryptoService generate key pair and sign with passed signing algorithm`() {
fun `BCCryptoService generate key pair and sign with passed signing algorithm`() {
assertTrue{signAndVerify(signAlgo = "NONEwithRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")} assertTrue{signAndVerify(signAlgo = "NONEwithRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")}
assertTrue{signAndVerify(signAlgo = "MD2withRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")} assertTrue{signAndVerify(signAlgo = "MD2withRSA", alias = "myKeyAlias", keyTypeAlgo = "RSA")}
assertTrue{signAndVerify(signAlgo = "MD5withRSA", 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 { private fun signAndVerify(signAlgo: String, alias: String, keyTypeAlgo: String): Boolean {
val keyPairGenerator = KeyPairGenerator.getInstance(keyTypeAlgo) val keyPairGenerator = KeyPairGenerator.getInstance(keyTypeAlgo)
val keyPair = keyPairGenerator.genKeyPair() 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) } assertTrue { cryptoService.containsKey(alias) }
val signatureData = cryptoService.sign(alias, clearData, signAlgo) val signatureData = cryptoService.sign(alias, clearData, signAlgo)
return verify(signAlgo, cryptoService.getPublicKey(alias), signatureData, clearData) return verify(signAlgo, cryptoService.getPublicKey(alias), signatureData, clearData)
@ -175,7 +168,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `When key does not exist getPublicKey, sign and getSigner should throw`() { fun `When key does not exist getPublicKey, sign and getSigner should throw`() {
val nonExistingAlias = "nonExistingAlias" val nonExistingAlias = "nonExistingAlias"
val cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath) val cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore, wrappingKeyStorePath)
assertFalse { cryptoService.containsKey(nonExistingAlias) } assertFalse { cryptoService.containsKey(nonExistingAlias) }
assertFailsWith<CryptoServiceException> { cryptoService.getPublicKey(nonExistingAlias) } assertFailsWith<CryptoServiceException> { cryptoService.getPublicKey(nonExistingAlias) }
assertFailsWith<CryptoServiceException> { cryptoService.sign(nonExistingAlias, clearData) } assertFailsWith<CryptoServiceException> { cryptoService.sign(nonExistingAlias, clearData) }
@ -184,7 +177,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService supports degraded mode of wrapping`() { 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() val supportedMode = cryptoService.getWrappingMode()
assertThat(supportedMode).isEqualTo(WrappingMode.DEGRADED_WRAPPED) assertThat(supportedMode).isEqualTo(WrappingMode.DEGRADED_WRAPPED)
@ -192,7 +185,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService does not fail when requested to create same wrapping key twice with failIfExists is false`() { 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() val keyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(keyAlias) cryptoService.createWrappingKey(keyAlias)
@ -201,7 +194,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService does fail when requested to create same wrapping key twice with failIfExists is true`() { 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() val keyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(keyAlias) cryptoService.createWrappingKey(keyAlias)
@ -213,7 +206,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService fails when asked to generate wrapped key pair or sign, but the master key specified does not exist`() { 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() val wrappingKeyAlias = UUID.randomUUID().toString()
@ -230,7 +223,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService can generate wrapped key pair and sign with the private key successfully, using default algorithm`() { 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() val wrappingKeyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(wrappingKeyAlias) cryptoService.createWrappingKey(wrappingKeyAlias)
@ -239,7 +232,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService can generate wrapped key pair and sign with the private key successfully`() { 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() val wrappingKeyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(wrappingKeyAlias) cryptoService.createWrappingKey(wrappingKeyAlias)
@ -264,7 +257,7 @@ class BCCryptoServiceTests {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `cryptoService can sign with previously encoded version of wrapped key`() { 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() val wrappingKeyAlias = UUID.randomUUID().toString()
cryptoService.createWrappingKey(wrappingKeyAlias) cryptoService.createWrappingKey(wrappingKeyAlias)

View File

@ -139,7 +139,12 @@ class KryoTests(private val compression: CordaSerializationEncoding?) {
@Test(timeout=300_000) @Test(timeout=300_000)
fun `deserialised key pair functions the same as serialised one`() { 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 bitsToSign: ByteArray = Ints.toByteArray(0x01234567)
val wrongBits: ByteArray = Ints.toByteArray(0x76543210) val wrongBits: ByteArray = Ints.toByteArray(0x76543210)
val signature = keyPair.sign(bitsToSign) val signature = keyPair.sign(bitsToSign)

View File

@ -223,7 +223,6 @@ dependencies {
integrationTestImplementation "junit:junit:$junit_version" integrationTestImplementation "junit:junit:$junit_version"
integrationTestImplementation "org.assertj:assertj-core:${assertj_version}" integrationTestImplementation "org.assertj:assertj-core:${assertj_version}"
integrationTestImplementation "org.apache.qpid:qpid-jms-client:${protonj_version}" integrationTestImplementation "org.apache.qpid:qpid-jms-client:${protonj_version}"
integrationTestImplementation "net.i2p.crypto:eddsa:$eddsa_version"
// used by FinalityFlowErrorHandlingTest // used by FinalityFlowErrorHandlingTest
slowIntegrationTestImplementation project(':testing:cordapps:cashobservers') slowIntegrationTestImplementation project(':testing:cordapps:cashobservers')
@ -276,7 +275,6 @@ quasar {
"io.github.classgraph**", "io.github.classgraph**",
"io.netty*", "io.netty*",
"liquibase**", "liquibase**",
"net.i2p.crypto.**",
"nonapi.io.github.classgraph.**", "nonapi.io.github.classgraph.**",
"org.apiguardian.**", "org.apiguardian.**",
"org.bouncycastle**", "org.bouncycastle**",

View File

@ -65,7 +65,7 @@ tasks.register('buildCordaJAR', FatCapsule) {
applicationVersion = corda_release_version applicationVersion = corda_release_version
applicationId = "net.corda.node.Corda" applicationId = "net.corda.node.Corda"
// See experimental/quasar-hook/README.md for how to generate. // 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 quasarClassLoaderExclusion = "l(net.corda.core.serialization.internal.**)"
def quasarOptions = "m" def quasarOptions = "m"
javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarOptions}${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"] javaAgents = quasar_classifier ? ["quasar-core-${quasar_version}-${quasar_classifier}.jar=${quasarOptions}${quasarExcludeExpression}${quasarClassLoaderExclusion}"] : ["quasar-core-${quasar_version}.jar=${quasarExcludeExpression}${quasarClassLoaderExclusion}"]

View File

@ -3,7 +3,12 @@
--add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED
--add-opens=java.base/java.security=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.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.time=ALL-UNNAMED
--add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED
--add-opens=java.base/java.util.concurrent=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=java.sql/java.sql=ALL-UNNAMED
--add-opens=jdk.crypto.ec/sun.security.ec.ed=ALL-UNNAMED

View File

@ -7,7 +7,6 @@ import net.corda.core.flows.StartableByRPC
import net.corda.core.serialization.CheckpointCustomSerializer import net.corda.core.serialization.CheckpointCustomSerializer
import net.corda.testing.node.internal.CustomCordapp import net.corda.testing.node.internal.CustomCordapp
import net.corda.testing.node.internal.enclosedCordapp import net.corda.testing.node.internal.enclosedCordapp
import net.i2p.crypto.eddsa.EdDSAPublicKey
import org.assertj.core.api.Assertions import org.assertj.core.api.Assertions
import java.security.PublicKey import java.security.PublicKey
import java.time.Duration import java.time.Duration
@ -198,17 +197,4 @@ class TestCorDapp {
throw FlowException("Broken on purpose") throw FlowException("Broken on purpose")
} }
} }
@Suppress("unused")
class BrokenEdDSAPublicKeySerializer :
CheckpointCustomSerializer<EdDSAPublicKey, String> {
override fun toProxy(obj: EdDSAPublicKey): String {
throw FlowException("Broken on purpose")
}
override fun fromProxy(proxy: String): EdDSAPublicKey {
throw FlowException("Broken on purpose")
}
}
} }

View File

@ -150,7 +150,7 @@ import net.corda.nodeapi.internal.SignedNodeInfo
import net.corda.nodeapi.internal.cordapp.CordappLoader import net.corda.nodeapi.internal.cordapp.CordappLoader
import net.corda.nodeapi.internal.cordapp.cordappSchemas import net.corda.nodeapi.internal.cordapp.cordappSchemas
import net.corda.nodeapi.internal.cryptoservice.CryptoService 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.NodeLifecycleEvent
import net.corda.nodeapi.internal.lifecycle.NodeLifecycleEventsDistributor import net.corda.nodeapi.internal.lifecycle.NodeLifecycleEventsDistributor
import net.corda.nodeapi.internal.lifecycle.NodeServicesContext import net.corda.nodeapi.internal.lifecycle.NodeServicesContext
@ -1061,7 +1061,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
} }
protected open fun makeCryptoService(): CryptoService { protected open fun makeCryptoService(): CryptoService {
return BCCryptoService(configuration.myLegalName.x500Principal, configuration.signingCertificateStore) return DefaultCryptoService(configuration.myLegalName.x500Principal, configuration.signingCertificateStore)
} }
@VisibleForTesting @VisibleForTesting

View File

@ -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.X509Utilities.NODE_IDENTITY_KEY_ALIAS
import net.corda.nodeapi.internal.crypto.checkValidity import net.corda.nodeapi.internal.crypto.checkValidity
import net.corda.nodeapi.internal.cryptoservice.CryptoService 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.io.IOException
import java.math.BigInteger import java.math.BigInteger
import java.nio.file.NoSuchFileException import java.nio.file.NoSuchFileException
@ -54,8 +54,8 @@ class KeyStoreHandler(private val configuration: NodeConfiguration, private val
if (configuration.devMode) { if (configuration.devMode) {
configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy) configuration.configureWithDevSSLCertificate(cryptoService, devModeKeyEntropy)
// configureWithDevSSLCertificate is a devMode process that writes directly to keystore files, so // configureWithDevSSLCertificate is a devMode process that writes directly to keystore files, so
// we should re-synchronise BCCryptoService with the updated keystore file. // we should re-synchronise DefaultCryptoService with the updated keystore file.
if (cryptoService is BCCryptoService) { if (cryptoService is DefaultCryptoService) {
cryptoService.resyncKeystore() cryptoService.resyncKeystore()
} }
} }

View File

@ -17,7 +17,7 @@ import net.corda.nodeapi.internal.config.toProperties
import net.corda.nodeapi.internal.crypto.X509KeyStore import net.corda.nodeapi.internal.crypto.X509KeyStore
import net.corda.nodeapi.internal.crypto.X509Utilities import net.corda.nodeapi.internal.crypto.X509Utilities
import net.corda.nodeapi.internal.cryptoservice.CryptoService 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.installDevNodeCaCertPath
import net.corda.nodeapi.internal.loadDevCaTrustStore import net.corda.nodeapi.internal.loadDevCaTrustStore
import net.corda.nodeapi.internal.registerDevP2pCertificates import net.corda.nodeapi.internal.registerDevP2pCertificates
@ -195,7 +195,7 @@ fun MutualSslConfiguration.configureDevKeyAndTrustStores(myLegalName: CordaX500N
FileBasedCertificateStoreSupplier(keyStore.path, keyStore.storePassword, keyStore.entryPassword).get(true) FileBasedCertificateStoreSupplier(keyStore.path, keyStore.storePassword, keyStore.entryPassword).get(true)
.also { it.registerDevP2pCertificates(myLegalName) } .also { it.registerDevP2pCertificates(myLegalName) }
when (cryptoService) { when (cryptoService) {
is BCCryptoService, null -> { is DefaultCryptoService, null -> {
val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.storePassword, signingCertificateStore.entryPassword).get(true) val signingKeyStore = FileBasedCertificateStoreSupplier(signingCertificateStore.path, signingCertificateStore.storePassword, signingCertificateStore.entryPassword).get(true)
.also { .also {
it.installDevNodeCaCertPath(myLegalName) it.installDevNodeCaCertPath(myLegalName)

View File

@ -77,7 +77,7 @@ interface NodeConfiguration : ConfigurationWithOptionsContainer {
val baseDirectory: Path val baseDirectory: Path
val certificatesDirectory: Path val certificatesDirectory: Path
// signingCertificateStore is used to store certificate chains. // 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 signingCertificateStore: FileBasedCertificateStoreSupplier
val p2pSslOptions: MutualSslConfiguration val p2pSslOptions: MutualSslConfiguration

View File

@ -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.X509Utilities.DEFAULT_VALIDITY_WINDOW
import net.corda.nodeapi.internal.crypto.x509 import net.corda.nodeapi.internal.crypto.x509
import net.corda.nodeapi.internal.cryptoservice.CryptoService 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.asn1.x500.X500Name
import org.bouncycastle.openssl.jcajce.JcaPEMWriter import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.operator.ContentSigner import org.bouncycastle.operator.ContentSigner
@ -98,7 +98,7 @@ open class NetworkRegistrationHelper(
certificatesDirectory.safeSymbolicRead().createDirectories() certificatesDirectory.safeSymbolicRead().createDirectories()
// We need this in case cryptoService and certificateStore share the same KeyStore (for backwards compatibility purposes). // 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. // 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. // SELF_SIGNED_PRIVATE_KEY is used as progress indicator.
if (certStore.contains(nodeCaKeyAlias) && !certStore.contains(SELF_SIGNED_PRIVATE_KEY)) { if (certStore.contains(nodeCaKeyAlias) && !certStore.contains(SELF_SIGNED_PRIVATE_KEY)) {
@ -169,7 +169,7 @@ open class NetworkRegistrationHelper(
certificatesDirectory.safeSymbolicRead().createDirectories() certificatesDirectory.safeSymbolicRead().createDirectories()
// We need this in case cryptoService and certificateStore share the same KeyStore (for backwards compatibility purposes). // 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. // 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)) { if (!certStore.contains(nodeCaKeyAlias)) {
logProgress("Node CA key doesn't exist, program will now terminate...") logProgress("Node CA key doesn't exist, program will now terminate...")
@ -374,7 +374,7 @@ class NodeRegistrationConfiguration(
tlsCertCrlDistPoint = config.tlsCertCrlDistPoint, tlsCertCrlDistPoint = config.tlsCertCrlDistPoint,
certificatesDirectory = config.certificatesDirectory, certificatesDirectory = config.certificatesDirectory,
emailAddress = config.emailAddress, emailAddress = config.emailAddress,
cryptoService = BCCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore), cryptoService = DefaultCryptoService(config.myLegalName.x500Principal, config.signingCertificateStore),
certificateStore = config.signingCertificateStore.get(true), certificateStore = config.signingCertificateStore.get(true),
notaryServiceConfig = config.notary?.let { 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 // Validation of the presence of the notary service legal name is only done here and not in the top level configuration

View File

@ -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.DISTRIBUTED_NOTARY_KEY_ALIAS
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_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.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.ALICE_NAME
import net.corda.testing.core.BOB_NAME import net.corda.testing.core.BOB_NAME
import org.assertj.core.api.Assertions.assertThat import org.assertj.core.api.Assertions.assertThat
import org.assertj.core.api.Assertions.assertThatThrownBy import org.assertj.core.api.Assertions.assertThatThrownBy
import org.junit.Before import org.junit.Before
import org.junit.Ignore
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.rules.TemporaryFolder import org.junit.rules.TemporaryFolder
@ -37,7 +36,6 @@ import java.security.KeyPair
import java.security.PublicKey import java.security.PublicKey
import kotlin.io.path.div import kotlin.io.path.div
@Ignore("TODO JDK17: Fixme")
class KeyStoreHandlerTest { class KeyStoreHandlerTest {
@Rule @Rule
@JvmField @JvmField
@ -49,7 +47,7 @@ class KeyStoreHandlerTest {
private val keyStore get() = config.signingCertificateStore.get() private val keyStore get() = config.signingCertificateStore.get()
private lateinit var cryptoService: BCCryptoService private lateinit var cryptoService: DefaultCryptoService
private lateinit var keyStoreHandler: KeyStoreHandler private lateinit var keyStoreHandler: KeyStoreHandler
@ -66,7 +64,7 @@ class KeyStoreHandlerTest {
doReturn(ALICE_NAME).whenever(it).myLegalName doReturn(ALICE_NAME).whenever(it).myLegalName
doReturn(null).whenever(it).notary doReturn(null).whenever(it).notary
} }
cryptoService = BCCryptoService(ALICE_NAME.x500Principal, signingCertificateStore) cryptoService = DefaultCryptoService(ALICE_NAME.x500Principal, signingCertificateStore)
keyStoreHandler = KeyStoreHandler(config, cryptoService) keyStoreHandler = KeyStoreHandler(config, cryptoService)
} }
@ -192,7 +190,7 @@ class KeyStoreHandlerTest {
val devCertificateDir = tempFolder.root.toPath() / "certificates-dev" val devCertificateDir = tempFolder.root.toPath() / "certificates-dev"
val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(devCertificateDir) val signingCertificateStore = CertificateStoreStubs.Signing.withCertificatesDirectory(devCertificateDir)
val p2pSslOptions = CertificateStoreStubs.P2P.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(true).whenever(config).devMode
doReturn(signingCertificateStore).whenever(config).signingCertificateStore doReturn(signingCertificateStore).whenever(config).signingCertificateStore

View File

@ -955,8 +955,7 @@ class DriverDSLImpl(
val excludePackagePattern = "x(antlr**;bftsmart**;ch**;co.paralleluniverse**;com.codahale**;com.esotericsoftware**;" + 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.fasterxml**;com.google**;com.ibm**;com.intellij**;com.jcabi**;com.nhaarman**;com.opengamma**;" +
"com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" + "com.typesafe**;com.zaxxer**;de.javakaffee**;groovy**;groovyjarjarantlr**;groovyjarjarasm**;io.atomix**;" +
"io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;" + "io.github**;io.netty**;jdk**;joptsimple**;junit**;kotlin**;net.bytebuddy**;org.apache**;" +
"net.i2p**;org.apache**;" +
"org.assertj**;org.bouncycastle**;org.codehaus**;org.crsh**;org.dom4j**;org.fusesource**;org.h2**;" + "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.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**;" + "org.objenesis**;org.slf4j**;org.w3c**;org.xml**;org.yaml**;reflectasm**;rx**;org.jolokia**;" +