mirror of
https://github.com/corda/corda.git
synced 2024-12-24 07:06:44 +00:00
ENT-4628: Harmonize net.corda.nodeapi.internal.crypto between OS and ENT (#5820)
* ENT-4628: Harmonize net.corda.nodeapi.internal.crypto between OS and ENT * ENT-4628: Fix detekt
This commit is contained in:
parent
af30e40397
commit
8d5781db43
@ -223,6 +223,7 @@
|
|||||||
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$RetryFlow$()</ID>
|
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$RetryFlow$()</ID>
|
||||||
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$ThrowingFlow$()</ID>
|
<ID>EmptyDefaultConstructor:FlowRetryTest.kt$ThrowingFlow$()</ID>
|
||||||
<ID>EmptyElseBlock:CordaCliWrapper.kt${ }</ID>
|
<ID>EmptyElseBlock:CordaCliWrapper.kt${ }</ID>
|
||||||
|
<ID>EmptyIfBlock:ContentSignerBuilder.kt$ContentSignerBuilder.SignatureOutputStream$if (alreadySigned) throw IllegalStateException("Cannot write to already signed object")</ID>
|
||||||
<ID>EmptyIfBlock:InMemoryIdentityService.kt$InMemoryIdentityService${ }</ID>
|
<ID>EmptyIfBlock:InMemoryIdentityService.kt$InMemoryIdentityService${ }</ID>
|
||||||
<ID>EmptyKtFile:KryoHook.kt$.KryoHook.kt</ID>
|
<ID>EmptyKtFile:KryoHook.kt$.KryoHook.kt</ID>
|
||||||
<ID>EmptyKtFile:ValidatingNotaryService.kt$.ValidatingNotaryService.kt</ID>
|
<ID>EmptyKtFile:ValidatingNotaryService.kt$.ValidatingNotaryService.kt</ID>
|
||||||
@ -1316,8 +1317,6 @@
|
|||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$parseSecureHashConfiguration(configuration.blacklistedAttachmentSigningKeys) { "Error while adding signing key $it to blacklistedAttachmentSigningKeys" }</ID>
|
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$parseSecureHashConfiguration(configuration.blacklistedAttachmentSigningKeys) { "Error while adding signing key $it to blacklistedAttachmentSigningKeys" }</ID>
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$parseSecureHashConfiguration(configuration.cordappSignerKeyFingerprintBlacklist) { "Error while adding key fingerprint $it to blacklistedAttachmentSigningKeys" }</ID>
|
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$parseSecureHashConfiguration(configuration.cordappSignerKeyFingerprintBlacklist) { "Error while adding key fingerprint $it to blacklistedAttachmentSigningKeys" }</ID>
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$private</ID>
|
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$private</ID>
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$throw ConfigurationException("The name '$legalName' for $NODE_IDENTITY_ALIAS_PREFIX doesn't match what's in the key store: $subject")</ID>
|
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$throw ConfigurationException("The name of the notary service '$serviceLegalName' for $DISTRIBUTED_NOTARY_ALIAS_PREFIX doesn't " + "match what's in the key store: $subject. You might need to adjust the configuration of `notary.serviceLegalName`.")</ID>
|
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$throw IllegalStateException("CryptoService and signingCertificateStore are not aligned, the entry for key-alias: $alias is only found in $keyExistsIn")</ID>
|
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$throw IllegalStateException("CryptoService and signingCertificateStore are not aligned, the entry for key-alias: $alias is only found in $keyExistsIn")</ID>
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(configuration.cordappDirectories), attachments).tokenize()</ID>
|
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$val cordappProvider = CordappProviderImpl(cordappLoader, CordappConfigFileProvider(configuration.cordappDirectories), attachments).tokenize()</ID>
|
||||||
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$val servicesForResolution = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersStorage, transactionStorage).also { attachments.servicesForResolution = it }</ID>
|
<ID>MaxLineLength:AbstractNode.kt$AbstractNode$val servicesForResolution = ServicesForResolutionImpl(identityService, attachments, cordappProvider, networkParametersStorage, transactionStorage).also { attachments.servicesForResolution = it }</ID>
|
||||||
@ -1845,6 +1844,7 @@
|
|||||||
<ID>MaxLineLength:ConstraintsUtils.kt$input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.")</ID>
|
<ID>MaxLineLength:ConstraintsUtils.kt$input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.")</ID>
|
||||||
<ID>MaxLineLength:ConstraintsUtils.kt$input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.")</ID>
|
<ID>MaxLineLength:ConstraintsUtils.kt$input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.")</ID>
|
||||||
<ID>MaxLineLength:ConstraintsUtils.kt$when { // These branches should not happen, as this has been already checked. input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.") input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.") // Transition to the same constraint. input == output -> true // You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal. input is AlwaysAcceptAttachmentConstraint && output !is AlwaysAcceptAttachmentConstraint -> false // Nothing can be migrated from the HashConstraint except a HashConstraint with the same Hash. (This check is redundant, but added for clarity) input is HashAttachmentConstraint && output is HashAttachmentConstraint -> input == output // Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint. input !is HashAttachmentConstraint && output is HashAttachmentConstraint -> true // The SignatureAttachmentConstraint allows migration from a Signature constraint with the same key. // TODO - we don't support currently third party signers. When we do, the output key will have to be stronger then the input key. input is SignatureAttachmentConstraint && output is SignatureAttachmentConstraint -> input.key == output.key // HashAttachmentConstraint can be transformed to a SignatureAttachmentConstraint when hash constraint verification checking disabled. HashAttachmentConstraint.disableHashConstraints && input is HashAttachmentConstraint && output is SignatureAttachmentConstraint -> true // You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future. input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint -> attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys) else -> false }</ID>
|
<ID>MaxLineLength:ConstraintsUtils.kt$when { // These branches should not happen, as this has been already checked. input is AutomaticPlaceholderConstraint || output is AutomaticPlaceholderConstraint -> throw IllegalArgumentException("Illegal constraint: AutomaticPlaceholderConstraint.") input.isAutomaticHashConstraint() || output.isAutomaticHashConstraint() -> throw IllegalArgumentException("Illegal constraint: AutomaticHashConstraint.") // Transition to the same constraint. input == output -> true // You can't transition from the AlwaysAcceptAttachmentConstraint to anything else, as it could hide something illegal. input is AlwaysAcceptAttachmentConstraint && output !is AlwaysAcceptAttachmentConstraint -> false // Nothing can be migrated from the HashConstraint except a HashConstraint with the same Hash. (This check is redundant, but added for clarity) input is HashAttachmentConstraint && output is HashAttachmentConstraint -> input == output // Anything (except the AlwaysAcceptAttachmentConstraint) can be transformed to a HashAttachmentConstraint. input !is HashAttachmentConstraint && output is HashAttachmentConstraint -> true // The SignatureAttachmentConstraint allows migration from a Signature constraint with the same key. // TODO - we don't support currently third party signers. When we do, the output key will have to be stronger then the input key. input is SignatureAttachmentConstraint && output is SignatureAttachmentConstraint -> input.key == output.key // HashAttachmentConstraint can be transformed to a SignatureAttachmentConstraint when hash constraint verification checking disabled. HashAttachmentConstraint.disableHashConstraints && input is HashAttachmentConstraint && output is SignatureAttachmentConstraint -> true // You can transition from the WhitelistConstraint to the SignatureConstraint only if all signers of the JAR are required to sign in the future. input is WhitelistedByZoneAttachmentConstraint && output is SignatureAttachmentConstraint -> attachment.signerKeys.isNotEmpty() && output.key.keys.containsAll(attachment.signerKeys) else -> false }</ID>
|
||||||
|
<ID>MaxLineLength:ContentSignerBuilder.kt$ContentSignerBuilder.SignatureOutputStream$private fun checkNotSigned(func: () -> Unit)</ID>
|
||||||
<ID>MaxLineLength:ContractAttachment.kt$ContractAttachment$return "ContractAttachment(attachment=${attachment.id}, contracts='$allContracts', uploader='$uploader', signed='$isSigned', version='$version')"</ID>
|
<ID>MaxLineLength:ContractAttachment.kt$ContractAttachment$return "ContractAttachment(attachment=${attachment.id}, contracts='$allContracts', uploader='$uploader', signed='$isSigned', version='$version')"</ID>
|
||||||
<ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$PrepareTransaction : FlowLogic</ID>
|
<ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$PrepareTransaction : FlowLogic</ID>
|
||||||
<ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappsForAllNodes = listOf(enclosedCordapp()))</ID>
|
<ID>MaxLineLength:ContractHierarchyTest.kt$ContractHierarchyTest$mockNet = InternalMockNetwork(networkSendManuallyPumped = false, threadPerNode = true, cordappsForAllNodes = listOf(enclosedCordapp()))</ID>
|
||||||
|
@ -12,8 +12,9 @@ import net.corda.nodeapi.internal.config.SslConfiguration
|
|||||||
import net.corda.nodeapi.internal.crypto.CertificateType
|
import net.corda.nodeapi.internal.crypto.CertificateType
|
||||||
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.crypto.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_ALIAS
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.security.KeyPair
|
import java.security.KeyPair
|
||||||
@ -41,7 +42,7 @@ object DevIdentityGenerator {
|
|||||||
val nodeKeyStore = signingCertStore.get(true).also { it.installDevNodeCaCertPath(legalName) }
|
val nodeKeyStore = signingCertStore.get(true).also { it.installDevNodeCaCertPath(legalName) }
|
||||||
p2pSslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(legalName) }
|
p2pSslConfig.keyStore.get(true).also { it.registerDevP2pCertificates(legalName) }
|
||||||
|
|
||||||
val identity = nodeKeyStore.storeLegalIdentity("$NODE_IDENTITY_ALIAS_PREFIX-private-key")
|
val identity = nodeKeyStore.storeLegalIdentity(NODE_IDENTITY_KEY_ALIAS)
|
||||||
return identity.party
|
return identity.party
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +87,7 @@ object DevIdentityGenerator {
|
|||||||
private fun setPrivateKey(keyStore: X509KeyStore, keyPair: KeyPair, notaryPrincipal: X500Principal) {
|
private fun setPrivateKey(keyStore: X509KeyStore, keyPair: KeyPair, notaryPrincipal: X500Principal) {
|
||||||
val serviceKeyCert = createCertificate(keyPair.public, notaryPrincipal)
|
val serviceKeyCert = createCertificate(keyPair.public, notaryPrincipal)
|
||||||
keyStore.setPrivateKey(
|
keyStore.setPrivateKey(
|
||||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
DISTRIBUTED_NOTARY_KEY_ALIAS,
|
||||||
keyPair.private,
|
keyPair.private,
|
||||||
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
|
listOf(serviceKeyCert, DEV_INTERMEDIATE_CA.certificate, DEV_ROOT_CA.certificate),
|
||||||
DEV_CA_KEY_STORE_PASS // Unfortunately we have to use the same password for private key due to Artemis limitation, for more details please see:
|
DEV_CA_KEY_STORE_PASS // Unfortunately we have to use the same password for private key due to Artemis limitation, for more details please see:
|
||||||
@ -97,7 +98,7 @@ object DevIdentityGenerator {
|
|||||||
|
|
||||||
private fun setCompositeKey(keyStore: X509KeyStore, compositeKey: PublicKey, notaryPrincipal: X500Principal) {
|
private fun setCompositeKey(keyStore: X509KeyStore, compositeKey: PublicKey, notaryPrincipal: X500Principal) {
|
||||||
val compositeKeyCert = createCertificate(compositeKey, notaryPrincipal)
|
val compositeKeyCert = createCertificate(compositeKey, notaryPrincipal)
|
||||||
keyStore.setCertificate("$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key", compositeKeyCert)
|
keyStore.setCertificate(DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS, compositeKeyCert)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createCertificate(publicKey: PublicKey, principal: X500Principal): X509Certificate {
|
private fun createCertificate(publicKey: PublicKey, principal: X500Principal): X509Certificate {
|
||||||
|
@ -16,9 +16,15 @@ import java.security.Signature
|
|||||||
* This builder will use BouncyCastle's JcaContentSignerBuilder as fallback for unknown algorithm.
|
* This builder will use BouncyCastle's JcaContentSignerBuilder as fallback for unknown algorithm.
|
||||||
*/
|
*/
|
||||||
object ContentSignerBuilder {
|
object ContentSignerBuilder {
|
||||||
fun build(signatureScheme: SignatureScheme, privateKey: PrivateKey, provider: Provider, random: SecureRandom? = null): ContentSigner {
|
fun build(signatureScheme: SignatureScheme, privateKey: PrivateKey, provider: Provider,
|
||||||
|
random: SecureRandom? = null, optimised: Boolean = true): ContentSigner {
|
||||||
val sigAlgId = signatureScheme.signatureOID
|
val sigAlgId = signatureScheme.signatureOID
|
||||||
val sig = Instances.getSignatureInstance(signatureScheme.signatureName, provider).apply {
|
val signatureInstance = if (optimised)
|
||||||
|
Instances.getSignatureInstance(signatureScheme.signatureName, provider)
|
||||||
|
else
|
||||||
|
Signature.getInstance(signatureScheme.signatureName, provider)
|
||||||
|
|
||||||
|
val sig = signatureInstance.apply {
|
||||||
// TODO special handling for Sphincs due to a known BouncyCastle's Sphincs bug we reported.
|
// TODO special handling for Sphincs due to a known BouncyCastle's Sphincs bug we reported.
|
||||||
// It is fixed in BC 161b12, so consider updating the below if-statement after updating BouncyCastle.
|
// It is fixed in BC 161b12, so consider updating the below if-statement after updating BouncyCastle.
|
||||||
if (random != null && signatureScheme != SPHINCS256_SHA256) {
|
if (random != null && signatureScheme != SPHINCS256_SHA256) {
|
||||||
@ -28,17 +34,28 @@ object ContentSignerBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return object : ContentSigner {
|
return object : ContentSigner {
|
||||||
private val stream = SignatureOutputStream(sig)
|
private val stream = SignatureOutputStream(sig, optimised)
|
||||||
override fun getAlgorithmIdentifier(): AlgorithmIdentifier = sigAlgId
|
override fun getAlgorithmIdentifier(): AlgorithmIdentifier = sigAlgId
|
||||||
override fun getOutputStream(): OutputStream = stream
|
override fun getOutputStream(): OutputStream = stream
|
||||||
override fun getSignature(): ByteArray = stream.signature
|
override fun getSignature(): ByteArray = stream.signature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SignatureOutputStream(private val sig: Signature) : OutputStream() {
|
private class SignatureOutputStream(private val sig: Signature, private val optimised: Boolean) : OutputStream() {
|
||||||
internal val signature: ByteArray get() = sig.sign()
|
private var alreadySigned = false
|
||||||
override fun write(bytes: ByteArray, off: Int, len: Int) = sig.update(bytes, off, len)
|
internal val signature: ByteArray by lazy {
|
||||||
override fun write(bytes: ByteArray) = sig.update(bytes)
|
try {
|
||||||
override fun write(b: Int) = sig.update(b.toByte())
|
alreadySigned = true
|
||||||
|
sig.sign()
|
||||||
|
} finally {
|
||||||
|
if (optimised) {
|
||||||
|
Instances.releaseSignatureInstance(sig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private fun checkNotSigned(func: () -> Unit) { if (alreadySigned) throw IllegalStateException("Cannot write to already signed object"); func()}
|
||||||
|
override fun write(bytes: ByteArray, off: Int, len: Int) = checkNotSigned { sig.update(bytes, off, len) }
|
||||||
|
override fun write(bytes: ByteArray) = checkNotSigned { sig.update(bytes) }
|
||||||
|
override fun write(b: Int) = checkNotSigned { sig.update(b.toByte()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,18 @@ const val KEYSTORE_TYPE = "JKS"
|
|||||||
* @param keyStoreFilePath location of KeyStore file.
|
* @param keyStoreFilePath location of KeyStore file.
|
||||||
* @param storePassword password to open the store. This does not have to be the same password as any keys stored,
|
* @param storePassword password to open the store. This does not have to be the same password as any keys stored,
|
||||||
* but for SSL purposes this is recommended.
|
* but for SSL purposes this is recommended.
|
||||||
|
* @param keystoreType the type of the keystore to be loaded. Defaults to "JKS".
|
||||||
|
* @param provider KeyStore provider, optional.
|
||||||
* @return returns the KeyStore opened/created.
|
* @return returns the KeyStore opened/created.
|
||||||
*/
|
*/
|
||||||
fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String): KeyStore {
|
fun loadOrCreateKeyStore(keyStoreFilePath: Path, storePassword: String, keystoreType: String = KEYSTORE_TYPE,
|
||||||
|
provider: Provider? = null): KeyStore {
|
||||||
val pass = storePassword.toCharArray()
|
val pass = storePassword.toCharArray()
|
||||||
val keyStore = KeyStore.getInstance(KEYSTORE_TYPE)
|
val keyStore = if (provider != null) {
|
||||||
|
KeyStore.getInstance(keystoreType, provider)
|
||||||
|
} else {
|
||||||
|
KeyStore.getInstance(keystoreType)
|
||||||
|
}
|
||||||
if (keyStoreFilePath.exists()) {
|
if (keyStoreFilePath.exists()) {
|
||||||
keyStoreFilePath.read { keyStore.load(it, pass) }
|
keyStoreFilePath.read { keyStore.load(it, pass) }
|
||||||
} else {
|
} else {
|
||||||
@ -144,6 +151,6 @@ fun KeyStore.getX509Certificate(alias: String): X509Certificate {
|
|||||||
*/
|
*/
|
||||||
fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey {
|
fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey {
|
||||||
val keyPass = keyPassword.toCharArray()
|
val keyPass = keyPassword.toCharArray()
|
||||||
val key = getKey(alias, keyPass) as PrivateKey
|
val key = requireNotNull(getKey(alias, keyPass)) { "Key for alias: '$alias' cannot be found" } as PrivateKey
|
||||||
return Crypto.toSupportedPrivateKey(key)
|
return Crypto.toSupportedPrivateKey(key)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ import java.util.*
|
|||||||
import javax.security.auth.x500.X500Principal
|
import javax.security.auth.x500.X500Principal
|
||||||
|
|
||||||
object X509Utilities {
|
object X509Utilities {
|
||||||
|
// Note that this default value only applies to BCCryptoService. Other implementations of CryptoService may have to use different
|
||||||
|
// schemes (for instance `UtimacoCryptoService.DEFAULT_IDENTITY_SIGNATURE_SCHEME`).
|
||||||
val DEFAULT_IDENTITY_SIGNATURE_SCHEME = Crypto.EDDSA_ED25519_SHA512
|
val DEFAULT_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
|
||||||
|
|
||||||
@ -47,15 +49,31 @@ object X509Utilities {
|
|||||||
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
const val CORDA_CLIENT_TLS = "cordaclienttls"
|
||||||
const val CORDA_CLIENT_CA = "cordaclientca"
|
const val CORDA_CLIENT_CA = "cordaclientca"
|
||||||
|
|
||||||
// TODO These don't need to be prefixes, but can be the full aliases. However, because they are used as key aliases
|
|
||||||
// we should ensure that:
|
|
||||||
// a) they always contain valid characters, preferably [A-Za-z0-9] in order to be supported by the majority of
|
|
||||||
// crypto service implementations (i.e., HSMs).
|
|
||||||
// b) they are at most 127 chars in length (i.e., as of 2018, Azure Key Vault does not support bigger aliases).
|
|
||||||
const val NODE_IDENTITY_ALIAS_PREFIX = "identity"
|
|
||||||
// TODO Hyphen (-) seems to be supported by the major HSM vendors, but we should consider remove it in the
|
// TODO Hyphen (-) seems to be supported by the major HSM vendors, but we should consider remove it in the
|
||||||
// future and stick to [A-Za-z0-9].
|
// future and stick to [A-Za-z0-9].
|
||||||
const val DISTRIBUTED_NOTARY_ALIAS_PREFIX = "distributed-notary"
|
const val NODE_IDENTITY_KEY_ALIAS = "identity-private-key"
|
||||||
|
const val DISTRIBUTED_NOTARY_KEY_ALIAS = "distributed-notary-private-key"
|
||||||
|
const val DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS = "distributed-notary-composite-key"
|
||||||
|
|
||||||
|
const val TLS_CERTIFICATE_DAYS_TO_EXPIRY_WARNING_THRESHOLD = 30
|
||||||
|
private const val KEY_ALIAS_REGEX = "[a-z0-9-]+"
|
||||||
|
private const val KEY_ALIAS_MAX_LENGTH = 100
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the provided key alias does not exceed maximum length and
|
||||||
|
* only contains alphanumeric characters.
|
||||||
|
*/
|
||||||
|
fun isKeyAliasValid(alias: String): Boolean {
|
||||||
|
if (alias.length > KEY_ALIAS_MAX_LENGTH) return false
|
||||||
|
return KEY_ALIAS_REGEX.toRegex().matches(alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error message to be displayed to the user when the alias validation fails.
|
||||||
|
*/
|
||||||
|
fun invalidKeyAliasErrorMessage(alias: String): String {
|
||||||
|
return "Alias '$alias' must contain only lowercase alphanumeric characters and not exceed 100 characters length."
|
||||||
|
}
|
||||||
|
|
||||||
val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
val DEFAULT_VALIDITY_WINDOW = Pair(0.millis, 3650.days)
|
||||||
|
|
||||||
@ -378,6 +396,24 @@ fun PKCS10CertificationRequest.isSignatureValid(): Boolean {
|
|||||||
return this.isSignatureValid(JcaContentVerifierProviderBuilder().build(this.subjectPublicKeyInfo))
|
return this.isSignatureValid(JcaContentVerifierProviderBuilder().build(this.subjectPublicKeyInfo))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check certificate validity or print warning if expiry is within 30 days
|
||||||
|
*/
|
||||||
|
fun X509Certificate.checkValidity(errorMessage: () -> Any, warningBlock: (daysToExpiry: Int) -> Unit, date: Date = Date()) {
|
||||||
|
try {
|
||||||
|
checkValidity(date)
|
||||||
|
}
|
||||||
|
catch (e: CertificateException) {
|
||||||
|
throw IllegalArgumentException(errorMessage().toString(), e)
|
||||||
|
}
|
||||||
|
// Number of full days until midnight of expiry date: today is not included
|
||||||
|
val daysToExpiry = ChronoUnit.DAYS.between(date.toInstant(), notAfter.toInstant()).toInt()
|
||||||
|
if (daysToExpiry < X509Utilities.TLS_CERTIFICATE_DAYS_TO_EXPIRY_WARNING_THRESHOLD) {
|
||||||
|
// Also include today, e.g. return 1 for tomorrow expiry
|
||||||
|
warningBlock(daysToExpiry + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
* Wraps a [CertificateFactory] to remove boilerplate. It's unclear whether [CertificateFactory] is threadsafe so best
|
||||||
* so assume this class is not.
|
* so assume this class is not.
|
||||||
|
@ -10,7 +10,7 @@ import net.corda.core.messaging.startFlow
|
|||||||
import net.corda.core.utilities.getOrThrow
|
import net.corda.core.utilities.getOrThrow
|
||||||
import net.corda.node.internal.NodeStartup
|
import net.corda.node.internal.NodeStartup
|
||||||
import net.corda.node.services.Permissions.Companion.startFlow
|
import net.corda.node.services.Permissions.Companion.startFlow
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX
|
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS
|
||||||
import net.corda.nodeapi.internal.installDevNodeCaCertPath
|
import net.corda.nodeapi.internal.installDevNodeCaCertPath
|
||||||
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
|
||||||
@ -92,7 +92,7 @@ class BootTests {
|
|||||||
}
|
}
|
||||||
val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
val logFolder = alice.baseDirectory / NodeStartup.LOGS_DIRECTORY_NAME
|
||||||
val logFile = logFolder.list { it.filter { a -> a.isRegularFile() && a.fileName.toString().startsWith("node") }.findFirst().get() }
|
val logFile = logFolder.list { it.filter { a -> a.isRegularFile() && a.fileName.toString().startsWith("node") }.findFirst().get() }
|
||||||
val lines = logFile.readLines { lines -> lines.filter { "$NODE_IDENTITY_ALIAS_PREFIX-private-key" in it }.toArray() }
|
val lines = logFile.readLines { lines -> lines.filter { NODE_IDENTITY_KEY_ALIAS in it }.toArray() }
|
||||||
assertTrue(lines.count() > 0)
|
assertTrue(lines.count() > 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,8 +150,9 @@ import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_CA
|
|||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_CLIENT_TLS
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
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.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_ALIAS_PREFIX
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_ALIAS
|
||||||
|
import net.corda.nodeapi.internal.crypto.X509Utilities.NODE_IDENTITY_KEY_ALIAS
|
||||||
import net.corda.nodeapi.internal.cryptoservice.CryptoServiceFactory
|
import net.corda.nodeapi.internal.cryptoservice.CryptoServiceFactory
|
||||||
import net.corda.nodeapi.internal.cryptoservice.SupportedCryptoServices
|
import net.corda.nodeapi.internal.cryptoservice.SupportedCryptoServices
|
||||||
import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
|
import net.corda.nodeapi.internal.cryptoservice.bouncycastle.BCCryptoService
|
||||||
@ -969,7 +970,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
* Note that obtainIdentity returns a KeyPair with an [AliasPrivateKey].
|
* Note that obtainIdentity returns a KeyPair with an [AliasPrivateKey].
|
||||||
*/
|
*/
|
||||||
private fun obtainIdentity(): Pair<PartyAndCertificate, KeyPair> {
|
private fun obtainIdentity(): Pair<PartyAndCertificate, KeyPair> {
|
||||||
val legalIdentityPrivateKeyAlias = "$NODE_IDENTITY_ALIAS_PREFIX-private-key"
|
val legalIdentityPrivateKeyAlias = "$NODE_IDENTITY_KEY_ALIAS"
|
||||||
|
|
||||||
var signingCertificateStore = configuration.signingCertificateStore.get()
|
var signingCertificateStore = configuration.signingCertificateStore.get()
|
||||||
if (!cryptoService.containsKey(legalIdentityPrivateKeyAlias) && !signingCertificateStore.contains(legalIdentityPrivateKeyAlias)) {
|
if (!cryptoService.containsKey(legalIdentityPrivateKeyAlias) && !signingCertificateStore.contains(legalIdentityPrivateKeyAlias)) {
|
||||||
@ -1000,7 +1001,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
|
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
|
||||||
val legalName = configuration.myLegalName
|
val legalName = configuration.myLegalName
|
||||||
if (subject != legalName) {
|
if (subject != legalName) {
|
||||||
throw ConfigurationException("The name '$legalName' for $NODE_IDENTITY_ALIAS_PREFIX doesn't match what's in the key store: $subject")
|
throw ConfigurationException("The configured legalName '$legalName' doesn't match what's in the key store: $subject")
|
||||||
}
|
}
|
||||||
|
|
||||||
return getPartyAndCertificatePlusAliasKeyPair(certificates, legalIdentityPrivateKeyAlias)
|
return getPartyAndCertificatePlusAliasKeyPair(certificates, legalIdentityPrivateKeyAlias)
|
||||||
@ -1016,8 +1017,8 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
/** Loads pre-generated notary service cluster identity. */
|
/** Loads pre-generated notary service cluster identity. */
|
||||||
private fun loadNotaryClusterIdentity(serviceLegalName: CordaX500Name): Pair<PartyAndCertificate, KeyPair> {
|
private fun loadNotaryClusterIdentity(serviceLegalName: CordaX500Name): Pair<PartyAndCertificate, KeyPair> {
|
||||||
val privateKeyAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key"
|
val privateKeyAlias = "$DISTRIBUTED_NOTARY_KEY_ALIAS"
|
||||||
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-composite-key"
|
val compositeKeyAlias = "$DISTRIBUTED_NOTARY_COMPOSITE_KEY_ALIAS"
|
||||||
|
|
||||||
val signingCertificateStore = configuration.signingCertificateStore.get()
|
val signingCertificateStore = configuration.signingCertificateStore.get()
|
||||||
val privateKeyAliasCertChain = try {
|
val privateKeyAliasCertChain = try {
|
||||||
@ -1040,7 +1041,7 @@ abstract class AbstractNode<S>(val configuration: NodeConfiguration,
|
|||||||
|
|
||||||
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
|
val subject = CordaX500Name.build(certificates.first().subjectX500Principal)
|
||||||
if (subject != serviceLegalName) {
|
if (subject != serviceLegalName) {
|
||||||
throw ConfigurationException("The name of the notary service '$serviceLegalName' for $DISTRIBUTED_NOTARY_ALIAS_PREFIX doesn't " +
|
throw ConfigurationException("The name of the notary service '$serviceLegalName' doesn't " +
|
||||||
"match what's in the key store: $subject. You might need to adjust the configuration of `notary.serviceLegalName`.")
|
"match what's in the key store: $subject. You might need to adjust the configuration of `notary.serviceLegalName`.")
|
||||||
}
|
}
|
||||||
return getPartyAndCertificatePlusAliasKeyPair(certificates, privateKeyAlias)
|
return getPartyAndCertificatePlusAliasKeyPair(certificates, privateKeyAlias)
|
||||||
|
@ -21,7 +21,7 @@ import net.corda.nodeapi.internal.crypto.CertificateType
|
|||||||
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.crypto.X509Utilities.CORDA_ROOT_CA
|
import net.corda.nodeapi.internal.crypto.X509Utilities.CORDA_ROOT_CA
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_ALIAS_PREFIX
|
import net.corda.nodeapi.internal.crypto.X509Utilities.DISTRIBUTED_NOTARY_KEY_ALIAS
|
||||||
import net.corda.nodeapi.internal.crypto.X509Utilities.createSelfSignedCACertificate
|
import net.corda.nodeapi.internal.crypto.X509Utilities.createSelfSignedCACertificate
|
||||||
import net.corda.testing.core.ALICE_NAME
|
import net.corda.testing.core.ALICE_NAME
|
||||||
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
import net.corda.testing.internal.createDevIntermediateCaCertPath
|
||||||
@ -193,7 +193,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
assertThat(config.p2pSslOptions.keyStore.getOptional()).isNull()
|
||||||
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
assertThat(config.p2pSslOptions.trustStore.getOptional()).isNull()
|
||||||
|
|
||||||
val serviceIdentityAlias = "$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key"
|
val serviceIdentityAlias = DISTRIBUTED_NOTARY_KEY_ALIAS
|
||||||
|
|
||||||
nodeKeystore.run {
|
nodeKeystore.run {
|
||||||
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
|
assertFalse(contains(X509Utilities.CORDA_INTERMEDIATE_CA))
|
||||||
@ -255,7 +255,7 @@ class NetworkRegistrationHelperTest {
|
|||||||
certService,
|
certService,
|
||||||
config.certificatesDirectory / networkRootTrustStoreFileName,
|
config.certificatesDirectory / networkRootTrustStoreFileName,
|
||||||
networkRootTrustStorePassword,
|
networkRootTrustStorePassword,
|
||||||
"$DISTRIBUTED_NOTARY_ALIAS_PREFIX-private-key",
|
DISTRIBUTED_NOTARY_KEY_ALIAS,
|
||||||
CertRole.SERVICE_IDENTITY)
|
CertRole.SERVICE_IDENTITY)
|
||||||
else -> throw IllegalArgumentException("Unsupported cert role.")
|
else -> throw IllegalArgumentException("Unsupported cert role.")
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user