Merge pull request #549 from corda/andr3ej-provider-setup-ent

ENT-1439 Refactor Provider setup
This commit is contained in:
Andrzej Cichocki 2018-03-14 13:16:37 +00:00 committed by GitHub
commit ca586eda18
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 74 deletions

View File

@ -10,9 +10,9 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_KEY
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.ASN1ObjectIdentifier
import java.security.AccessController
import java.security.PrivilegedAction
import java.security.Provider import java.security.Provider
class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") { class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
@ -21,20 +21,12 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
} }
init { init {
AccessController.doPrivileged(PrivilegedAction<Unit> { setup() }) put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", CompositeKeyFactory::class.java.name)
} put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", CompositeSignature::class.java.name)
put("Alg.Alias.KeyFactory.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM)
private fun setup() { put("Alg.Alias.KeyFactory.OID.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM)
put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", "net.corda.core.crypto.CompositeKeyFactory") put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", "net.corda.core.crypto.CompositeSignature") put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
val compositeKeyOID = CordaObjectIdentifier.COMPOSITE_KEY.id
put("Alg.Alias.KeyFactory.$compositeKeyOID", CompositeKey.KEY_ALGORITHM)
put("Alg.Alias.KeyFactory.OID.$compositeKeyOID", CompositeKey.KEY_ALGORITHM)
val compositeSignatureOID = CordaObjectIdentifier.COMPOSITE_SIGNATURE.id
put("Alg.Alias.Signature.$compositeSignatureOID", CompositeSignature.SIGNATURE_ALGORITHM)
put("Alg.Alias.Signature.OID.$compositeSignatureOID", CompositeSignature.SIGNATURE_ALGORITHM)
} }
} }

View File

@ -10,19 +10,17 @@
package net.corda.core.crypto package net.corda.core.crypto
import net.corda.core.internal.X509EdDSAEngine import net.corda.core.crypto.internal.*
import net.corda.core.serialization.serialize import net.corda.core.serialization.serialize
import net.i2p.crypto.eddsa.EdDSAEngine import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.EdDSASecurityProvider
import net.i2p.crypto.eddsa.math.GroupElement import net.i2p.crypto.eddsa.math.GroupElement
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec 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.DERNull import org.bouncycastle.asn1.DERNull
import org.bouncycastle.asn1.DLSequence import org.bouncycastle.asn1.DLSequence
import org.bouncycastle.asn1.bc.BCObjectIdentifiers import org.bouncycastle.asn1.bc.BCObjectIdentifiers
@ -37,7 +35,6 @@ 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.rsa.BCRSAPrivateKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec
@ -47,7 +44,6 @@ 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.pqc.jcajce.provider.BouncyCastlePQCProvider
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
@ -84,7 +80,7 @@ object Crypto {
"RSA_SHA256", "RSA_SHA256",
AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption, null), AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption, null),
listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)), listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)),
BouncyCastleProvider.PROVIDER_NAME, cordaBouncyCastleProvider.name,
"RSA", "RSA",
"SHA256WITHRSA", "SHA256WITHRSA",
null, null,
@ -99,7 +95,7 @@ object Crypto {
"ECDSA_SECP256K1_SHA256", "ECDSA_SECP256K1_SHA256",
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256k1), AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256k1),
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1)), listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1)),
BouncyCastleProvider.PROVIDER_NAME, cordaBouncyCastleProvider.name,
"ECDSA", "ECDSA",
"SHA256withECDSA", "SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256k1"), ECNamedCurveTable.getParameterSpec("secp256k1"),
@ -114,7 +110,7 @@ object Crypto {
"ECDSA_SECP256R1_SHA256", "ECDSA_SECP256R1_SHA256",
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256r1), AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256r1),
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256r1)), listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256r1)),
BouncyCastleProvider.PROVIDER_NAME, cordaBouncyCastleProvider.name,
"ECDSA", "ECDSA",
"SHA256withECDSA", "SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256r1"), ECNamedCurveTable.getParameterSpec("secp256r1"),
@ -128,14 +124,13 @@ object Crypto {
* Not to be confused with the EdDSA variants, Ed25519ctx and Ed25519ph. * Not to be confused with the EdDSA variants, Ed25519ctx and Ed25519ph.
*/ */
@JvmField @JvmField
val EDDSA_ED25519_SHA512 = SignatureScheme( val EDDSA_ED25519_SHA512: SignatureScheme = SignatureScheme(
4, 4,
"EDDSA_ED25519_SHA512", "EDDSA_ED25519_SHA512",
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00 AlgorithmIdentifier(`id-Curve25519ph`, null),
AlgorithmIdentifier(ASN1ObjectIdentifier("1.3.101.112"), 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 in i2p library.
// We added EdDSA to bouncy castle for certificate signing. // We added EdDSA to bouncy castle for certificate signing.
BouncyCastleProvider.PROVIDER_NAME, cordaBouncyCastleProvider.name,
"1.3.101.112", "1.3.101.112",
EdDSAEngine.SIGNATURE_ALGORITHM, EdDSAEngine.SIGNATURE_ALGORITHM,
EdDSANamedCurveTable.getByName("ED25519"), EdDSANamedCurveTable.getByName("ED25519"),
@ -158,7 +153,7 @@ object Crypto {
"SPHINCS-256_SHA512", "SPHINCS-256_SHA512",
AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, DLSequence(arrayOf(ASN1Integer(0), SHA512_256))), AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, DLSequence(arrayOf(ASN1Integer(0), SHA512_256))),
listOf(AlgorithmIdentifier(BCObjectIdentifiers.sphincs256, DLSequence(arrayOf(ASN1Integer(0), SHA512_256)))), listOf(AlgorithmIdentifier(BCObjectIdentifiers.sphincs256, DLSequence(arrayOf(ASN1Integer(0), SHA512_256)))),
"BCPQC", bouncyCastlePQCProvider.name,
"SPHINCS256", "SPHINCS256",
"SHA512WITHSPHINCS256", "SHA512WITHSPHINCS256",
SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256), SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256),
@ -175,7 +170,7 @@ object Crypto {
"COMPOSITE", "COMPOSITE",
AlgorithmIdentifier(CordaObjectIdentifier.COMPOSITE_KEY), AlgorithmIdentifier(CordaObjectIdentifier.COMPOSITE_KEY),
emptyList(), emptyList(),
CordaSecurityProvider.PROVIDER_NAME, cordaSecurityProvider.name,
CompositeKey.KEY_ALGORITHM, CompositeKey.KEY_ALGORITHM,
CompositeSignature.SIGNATURE_ALGORITHM, CompositeSignature.SIGNATURE_ALGORITHM,
null, null,
@ -209,22 +204,6 @@ object Crypto {
+ signatureSchemeMap.values.map { Pair(it.signatureOID, it) }) + signatureSchemeMap.values.map { Pair(it.signatureOID, it) })
.toMap() .toMap()
// This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider
// that could cause unexpected and suspicious behaviour.
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
// The val is private to avoid any harmful state changes.
private val providerMap: Map<String, Provider> = mapOf(
BouncyCastleProvider.PROVIDER_NAME to getBouncyCastleProvider(),
CordaSecurityProvider.PROVIDER_NAME to CordaSecurityProvider(),
"BCPQC" to BouncyCastlePQCProvider()) // Unfortunately, provider's name is not final in BouncyCastlePQCProvider, so we explicitly set it.
private fun getBouncyCastleProvider() = BouncyCastleProvider().apply {
putAll(EdDSASecurityProvider())
// Override the normal EdDSA engine with one which can handle X509 keys.
put("Signature.${EdDSAEngine.SIGNATURE_ALGORITHM}", X509EdDSAEngine::class.qualifiedName)
addKeyInfoConverter(EDDSA_ED25519_SHA512.signatureOID.algorithm, KeyInfoConverter(EDDSA_ED25519_SHA512))
}
@JvmStatic @JvmStatic
fun supportedSignatureSchemes(): List<SignatureScheme> = ArrayList(signatureSchemeMap.values) fun supportedSignatureSchemes(): List<SignatureScheme> = ArrayList(signatureSchemeMap.values)
@ -233,13 +212,6 @@ object Crypto {
return providerMap[name] ?: throw IllegalArgumentException("Unrecognised provider: $name") return providerMap[name] ?: throw IllegalArgumentException("Unrecognised provider: $name")
} }
init {
// 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(getBouncyCastleProvider())
Security.addProvider(CordaSecurityProvider())
}
/** /**
* Normalise an algorithm identifier by converting [DERNull] parameters into a Kotlin null value. * Normalise an algorithm identifier by converting [DERNull] parameters into a Kotlin null value.
*/ */
@ -886,7 +858,7 @@ object Crypto {
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray. // Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray { private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
// Compute hmac(privateKey, seed). // Compute hmac(privateKey, seed).
val mac = Mac.getInstance("HmacSHA512", providerMap[BouncyCastleProvider.PROVIDER_NAME]) 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 EdDSAPrivateKey -> privateKey.geta()
@ -897,16 +869,6 @@ object Crypto {
return mac.doFinal(seed) return mac.doFinal(seed)
} }
private class KeyInfoConverter(val signatureScheme: SignatureScheme) : AsymmetricKeyInfoConverter {
override fun generatePublic(keyInfo: SubjectPublicKeyInfo?): PublicKey? {
return keyInfo?.let { decodePublicKey(signatureScheme, it.encoded) }
}
override fun generatePrivate(keyInfo: PrivateKeyInfo?): PrivateKey? {
return keyInfo?.let { decodePrivateKey(signatureScheme, it.encoded) }
}
}
/** /**
* 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.

View File

@ -13,13 +13,13 @@
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.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
import net.corda.core.utilities.toBase58 import net.corda.core.utilities.toBase58
import net.corda.core.utilities.toSHA256Bytes import net.corda.core.utilities.toSHA256Bytes
import java.math.BigInteger import java.math.BigInteger
import net.corda.core.utilities.SgxSupport
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.security.* import java.security.*
@ -203,14 +203,6 @@ private class DummySecureRandomSpi : SecureRandomSpi() {
} }
object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null) object DummySecureRandom : SecureRandom(DummySecureRandomSpi(), null)
private val _newSecureRandom: () -> SecureRandom by lazy {
when {
SgxSupport.isInsideEnclave -> { { DummySecureRandom } }
System.getProperty("os.name") == "Linux" -> { { SecureRandom.getInstance("NativePRNGNonBlocking") } }
else -> { { SecureRandom.getInstanceStrong() } }
}
}
/** /**
* Get an instance of [SecureRandom] to avoid blocking, due to waiting for additional entropy, when possible. * Get an instance of [SecureRandom] to avoid blocking, due to waiting for additional entropy, when possible.
* In this version, the NativePRNGNonBlocking is exclusively used on Linux OS to utilize dev/urandom because in high traffic * In this version, the NativePRNGNonBlocking is exclusively used on Linux OS to utilize dev/urandom because in high traffic
@ -230,7 +222,7 @@ private val _newSecureRandom: () -> SecureRandom by lazy {
* 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 = _newSecureRandom() fun newSecureRandom(): SecureRandom = platformSecureRandomFactory()
/** /**
* 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

View File

@ -0,0 +1,56 @@
package net.corda.core.crypto.internal
import net.corda.core.crypto.CordaSecurityProvider
import net.corda.core.crypto.Crypto.EDDSA_ED25519_SHA512
import net.corda.core.crypto.Crypto.decodePrivateKey
import net.corda.core.crypto.Crypto.decodePublicKey
import net.corda.core.crypto.DummySecureRandom
import net.corda.core.internal.X509EdDSAEngine
import net.corda.core.utilities.SgxSupport
import net.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSASecurityProvider
import org.apache.commons.lang.SystemUtils
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider
import java.security.SecureRandom
import java.security.Security
internal val cordaSecurityProvider = CordaSecurityProvider().also {
Security.insertProviderAt(it, 1) // The position is 1-based.
}
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
internal val `id-Curve25519ph` = ASN1ObjectIdentifier("1.3.101.112")
internal 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)
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)
})
}.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)
}
internal val bouncyCastlePQCProvider = BouncyCastlePQCProvider().apply {
require(name == "BCPQC") // The constant it comes from is not final.
}
// This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider
// that could cause unexpected and suspicious behaviour.
// i.e. if someone removes a Provider and then he/she adds a new one with the same name.
// The val is private to avoid any harmful state changes.
internal val providerMap = listOf(cordaBouncyCastleProvider, cordaSecurityProvider, bouncyCastlePQCProvider).map { it.name to it }.toMap()
internal val platformSecureRandomFactory: () -> SecureRandom = when {
SgxSupport.isInsideEnclave -> {
{ DummySecureRandom }
}
SystemUtils.IS_OS_LINUX -> {
{ SecureRandom.getInstance("NativePRNGNonBlocking") }
}
else -> SecureRandom::getInstanceStrong
}

View File

@ -13,10 +13,13 @@
package net.corda.node package net.corda.node
import net.corda.core.crypto.CordaSecurityProvider
import net.corda.core.crypto.Crypto
import kotlin.system.exitProcess import kotlin.system.exitProcess
import net.corda.node.internal.EnterpriseNode import net.corda.node.internal.EnterpriseNode
fun main(args: Array<String>) { fun main(args: Array<String>) {
Crypto.findProvider(CordaSecurityProvider.PROVIDER_NAME) // Install our SecureRandom before e.g. UUID asks for one.
// Pass the arguments to the Node factory. In the Enterprise edition, this line is modified to point to a subclass. // Pass the arguments to the Node factory. In the Enterprise edition, this line is modified to point to a subclass.
// It will exit the process in case of startup failure and is not intended to be used by embedders. If you want // It will exit the process in case of startup failure and is not intended to be used by embedders. If you want
// to embed Node in your own container, instantiate it directly and set up the configuration objects yourself. // to embed Node in your own container, instantiate it directly and set up the configuration objects yourself.