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
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_KEY
import net.corda.core.crypto.CordaObjectIdentifier.COMPOSITE_SIGNATURE
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import java.security.AccessController
import java.security.PrivilegedAction
import java.security.Provider
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 {
AccessController.doPrivileged(PrivilegedAction<Unit> { setup() })
}
private fun setup() {
put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", "net.corda.core.crypto.CompositeKeyFactory")
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", "net.corda.core.crypto.CompositeSignature")
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)
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)
put("Alg.Alias.KeyFactory.OID.$COMPOSITE_KEY", CompositeKey.KEY_ALGORITHM)
put("Alg.Alias.Signature.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
put("Alg.Alias.Signature.OID.$COMPOSITE_SIGNATURE", CompositeSignature.SIGNATURE_ALGORITHM)
}
}

View File

@ -10,19 +10,17 @@
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.i2p.crypto.eddsa.EdDSAEngine
import net.i2p.crypto.eddsa.EdDSAPrivateKey
import net.i2p.crypto.eddsa.EdDSAPublicKey
import net.i2p.crypto.eddsa.EdDSASecurityProvider
import net.i2p.crypto.eddsa.math.GroupElement
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec
import org.bouncycastle.asn1.ASN1Integer
import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.DERNull
import org.bouncycastle.asn1.DLSequence
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.rsa.BCRSAPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter
import org.bouncycastle.jce.ECNamedCurveTable
import org.bouncycastle.jce.provider.BouncyCastleProvider
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.FixedPointCombMultiplier
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.BCSphincs256PublicKey
import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
@ -84,7 +80,7 @@ object Crypto {
"RSA_SHA256",
AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption, null),
listOf(AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, null)),
BouncyCastleProvider.PROVIDER_NAME,
cordaBouncyCastleProvider.name,
"RSA",
"SHA256WITHRSA",
null,
@ -99,7 +95,7 @@ object Crypto {
"ECDSA_SECP256K1_SHA256",
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256k1),
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256k1)),
BouncyCastleProvider.PROVIDER_NAME,
cordaBouncyCastleProvider.name,
"ECDSA",
"SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256k1"),
@ -114,7 +110,7 @@ object Crypto {
"ECDSA_SECP256R1_SHA256",
AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256, SECObjectIdentifiers.secp256r1),
listOf(AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, SECObjectIdentifiers.secp256r1)),
BouncyCastleProvider.PROVIDER_NAME,
cordaBouncyCastleProvider.name,
"ECDSA",
"SHA256withECDSA",
ECNamedCurveTable.getParameterSpec("secp256r1"),
@ -128,14 +124,13 @@ object Crypto {
* Not to be confused with the EdDSA variants, Ed25519ctx and Ed25519ph.
*/
@JvmField
val EDDSA_ED25519_SHA512 = SignatureScheme(
val EDDSA_ED25519_SHA512: SignatureScheme = SignatureScheme(
4,
"EDDSA_ED25519_SHA512",
// OID taken from https://tools.ietf.org/html/draft-ietf-curdle-pkix-00
AlgorithmIdentifier(ASN1ObjectIdentifier("1.3.101.112"), null),
AlgorithmIdentifier(`id-Curve25519ph`, null),
emptyList(), // Both keys and the signature scheme use the same OID in i2p library.
// We added EdDSA to bouncy castle for certificate signing.
BouncyCastleProvider.PROVIDER_NAME,
cordaBouncyCastleProvider.name,
"1.3.101.112",
EdDSAEngine.SIGNATURE_ALGORITHM,
EdDSANamedCurveTable.getByName("ED25519"),
@ -158,7 +153,7 @@ object Crypto {
"SPHINCS-256_SHA512",
AlgorithmIdentifier(BCObjectIdentifiers.sphincs256_with_SHA512, DLSequence(arrayOf(ASN1Integer(0), SHA512_256))),
listOf(AlgorithmIdentifier(BCObjectIdentifiers.sphincs256, DLSequence(arrayOf(ASN1Integer(0), SHA512_256)))),
"BCPQC",
bouncyCastlePQCProvider.name,
"SPHINCS256",
"SHA512WITHSPHINCS256",
SPHINCS256KeyGenParameterSpec(SPHINCS256KeyGenParameterSpec.SHA512_256),
@ -175,7 +170,7 @@ object Crypto {
"COMPOSITE",
AlgorithmIdentifier(CordaObjectIdentifier.COMPOSITE_KEY),
emptyList(),
CordaSecurityProvider.PROVIDER_NAME,
cordaSecurityProvider.name,
CompositeKey.KEY_ALGORITHM,
CompositeSignature.SIGNATURE_ALGORITHM,
null,
@ -209,22 +204,6 @@ object Crypto {
+ signatureSchemeMap.values.map { Pair(it.signatureOID, it) })
.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
fun supportedSignatureSchemes(): List<SignatureScheme> = ArrayList(signatureSchemeMap.values)
@ -233,13 +212,6 @@ object Crypto {
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.
*/
@ -886,7 +858,7 @@ object Crypto {
// Compute the HMAC-SHA512 using a privateKey as the MAC_key and a seed ByteArray.
private fun deriveHMAC(privateKey: PrivateKey, seed: ByteArray): ByteArray {
// Compute hmac(privateKey, seed).
val mac = Mac.getInstance("HmacSHA512", providerMap[BouncyCastleProvider.PROVIDER_NAME])
val mac = Mac.getInstance("HmacSHA512", cordaBouncyCastleProvider)
val keyData = when (privateKey) {
is BCECPrivateKey -> privateKey.d.toByteArray()
is EdDSAPrivateKey -> privateKey.geta()
@ -897,16 +869,6 @@ object Crypto {
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.
* Point-at-infinity is not permitted as well.

View File

@ -13,13 +13,13 @@
package net.corda.core.crypto
import net.corda.core.contracts.PrivacySalt
import net.corda.core.crypto.internal.platformSecureRandomFactory
import net.corda.core.serialization.SerializationDefaults
import net.corda.core.serialization.serialize
import net.corda.core.utilities.OpaqueBytes
import net.corda.core.utilities.toBase58
import net.corda.core.utilities.toSHA256Bytes
import java.math.BigInteger
import net.corda.core.utilities.SgxSupport
import java.nio.ByteBuffer
import java.security.*
@ -203,14 +203,6 @@ private class DummySecureRandomSpi : SecureRandomSpi() {
}
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.
* 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.
*/
@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

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
import net.corda.core.crypto.CordaSecurityProvider
import net.corda.core.crypto.Crypto
import kotlin.system.exitProcess
import net.corda.node.internal.EnterpriseNode
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.
// 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.