faster key encoding/decoding and generic converters between key implementations

This commit is contained in:
Konstantinos Chalkias
2017-05-22 11:14:05 +01:00
committed by GitHub
parent 1bc4c490bc
commit 53276c1f06
8 changed files with 208 additions and 117 deletions

View File

@ -21,13 +21,20 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder
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.rsa.BCRSAPrivateKey
import org.bouncycastle.jcajce.provider.asymmetric.rsa.BCRSAPublicKey
import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter 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.pkcs.PKCS10CertificationRequest import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder
import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider 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 import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec
import sun.security.pkcs.PKCS8Key
import sun.security.util.DerValue
import sun.security.x509.X509Key
import java.math.BigInteger import java.math.BigInteger
import java.security.* import java.security.*
import java.security.KeyFactory import java.security.KeyFactory
@ -140,6 +147,10 @@ object Crypto {
SPHINCS256_SHA256 SPHINCS256_SHA256
).associateBy { it.schemeCodeName } ).associateBy { it.schemeCodeName }
// We need to group signature schemes per algorithm, so to quickly identify them during decoding.
// Please note there are schemes with the same algorithm, e.g. EC (or ECDSA) keys are used for both ECDSA_SECP256K1_SHA256 and ECDSA_SECP256R1_SHA256.
private val algorithmGroups = supportedSignatureSchemes.values.groupBy { it.algorithmName }
// This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider // This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider
// that could cause unexpected and suspicious behaviour. // 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. // i.e. if someone removes a Provider and then he/she adds a new one with the same name.
@ -167,37 +178,20 @@ object Crypto {
* @return a currently supported SignatureScheme. * @return a currently supported SignatureScheme.
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @throws IllegalArgumentException if the requested signature scheme is not supported.
*/ */
fun findSignatureScheme(schemeCodeName: String): SignatureScheme = supportedSignatureSchemes[schemeCodeName] ?: throw IllegalArgumentException("Unsupported key/algorithm for metadata schemeCodeName: $schemeCodeName") fun findSignatureScheme(schemeCodeName: String): SignatureScheme = supportedSignatureSchemes[schemeCodeName] ?: throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $schemeCodeName")
/** /**
* Retrieve the corresponding [SignatureScheme] based on the type of the input [Key]. * Retrieve the corresponding [SignatureScheme] based on the type of the input [Key].
* This function is usually called when requiring to verify signatures and the signing schemes must be defined. * This function is usually called when requiring to verify signatures and the signing schemes must be defined.
* Note that only the Corda platform standard schemes are supported (see [Crypto]). * For the supported signature schemes see [Crypto].
* Note that we always need to add an additional if-else statement when there are signature schemes
* with the same algorithmName, but with different parameters (e.g. now there are two ECDSA schemes, each using its own curve).
* @param key either private or public. * @param key either private or public.
* @return a currently supported SignatureScheme. * @return a currently supported SignatureScheme.
* @throws IllegalArgumentException if the requested key type is not supported. * @throws IllegalArgumentException if the requested key type is not supported.
*/ */
fun findSignatureScheme(key: Key): SignatureScheme { fun findSignatureScheme(key: Key): SignatureScheme {
for (sig in supportedSignatureSchemes.values) { val algorithm = matchingAlgorithmName(key.algorithm)
var algorithm = key.algorithm algorithmGroups[algorithm]?.filter { validateKey(it, key) }?.firstOrNull { return it }
if (algorithm == "EC") algorithm = "ECDSA" // required to read ECC keys from Keystore, because encoding may change algorithm name from ECDSA to EC. throw IllegalArgumentException("Unsupported key algorithm: ${key.algorithm} or invalid key format")
if (algorithm == "SPHINCS-256") algorithm = "SPHINCS256" // because encoding may change algorithm name from SPHINCS256 to SPHINCS-256.
if (algorithm == sig.algorithmName) {
// If more than one ECDSA schemes are supported, we should distinguish between them by checking their curve parameters.
if (algorithm == "EdDSA") {
if ((key is EdDSAPublicKey && publicKeyOnCurve(sig, key)) || (key is EdDSAPrivateKey && key.params == sig.algSpec)) {
return sig
} else break // use continue if in the future we support more than one Edwards curves.
} else if (algorithm == "ECDSA") {
if ((key is BCECPublicKey && publicKeyOnCurve(sig, key)) || (key is BCECPrivateKey && key.parameters == sig.algSpec)) {
return sig
} else continue
} else return sig // it's either RSA_SHA256 or SPHINCS-256.
}
}
throw IllegalArgumentException("Unsupported key/algorithm for the key: ${key.encoded.toBase58()}")
} }
/** /**
@ -209,11 +203,16 @@ object Crypto {
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
for ((_, _, _, providerName, algorithmName) in supportedSignatureSchemes.values) { val algorithm = matchingAlgorithmName(PKCS8Key.parseKey(DerValue(encodedKey)).algorithm)
// There are cases where the same key algorithm is applied to different signature schemes.
// Currently, this occurs with ECDSA as it applies to either secp256K1 or secp256R1 curves.
// In such a case, we should try and identify which of the candidate schemes is the correct one so as
// to generate the appropriate key.
for (signatureScheme in algorithmGroups[algorithm]!!) {
try { try {
return KeyFactory.getInstance(algorithmName, providerMap[providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey)) return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
// ignore it - only used to bypass the scheme that causes an exception. // ignore it - only used to bypass the scheme that causes an exception, as it has the same name, but different params.
} }
} }
throw IllegalArgumentException("This private key cannot be decoded, please ensure it is PKCS8 encoded and the signature scheme is supported.") throw IllegalArgumentException("This private key cannot be decoded, please ensure it is PKCS8 encoded and the signature scheme is supported.")
@ -258,11 +257,16 @@ object Crypto {
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun decodePublicKey(encodedKey: ByteArray): PublicKey { fun decodePublicKey(encodedKey: ByteArray): PublicKey {
for ((_, _, _, providerName, algorithmName) in supportedSignatureSchemes.values) { val algorithm = matchingAlgorithmName(X509Key.parse(DerValue(encodedKey)).algorithm)
// There are cases where the same key algorithm is applied to different signature schemes.
// Currently, this occurs with ECDSA as it applies to either secp256K1 or secp256R1 curves.
// In such a case, we should try and identify which of the candidate schemes is the correct one so as
// to generate the appropriate key.
for (signatureScheme in algorithmGroups[algorithm]!!) {
try { try {
return KeyFactory.getInstance(algorithmName, providerMap[providerName]).generatePublic(X509EncodedKeySpec(encodedKey)) return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
// ignore it - only used to bypass the scheme that causes an exception. // ignore it - only used to bypass the scheme that causes an exception, as it has the same name, but different params.
} }
} }
throw IllegalArgumentException("This public key cannot be decoded, please ensure it is X509 encoded and the signature scheme is supported.") throw IllegalArgumentException("This public key cannot be decoded, please ensure it is X509 encoded and the signature scheme is supported.")
@ -273,7 +277,7 @@ object Crypto {
* This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers. * This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param schemeCodeName a [String] that should match a key in supportedSignatureSchemes map (e.g. ECDSA_SECP256K1_SHA256). * @param schemeCodeName a [String] that should match a key in supportedSignatureSchemes map (e.g. ECDSA_SECP256K1_SHA256).
* @param encodedKey an X509 encoded public key. * @param encodedKey an X509 encoded public key.
* @throws IllegalArgumentException if the requested scheme is not supported * @throws IllegalArgumentException if the requested scheme is not supported.
* @throws InvalidKeySpecException if the given key specification * @throws InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key. * is inappropriate for this key factory to produce a public key.
*/ */
@ -285,7 +289,7 @@ object Crypto {
* This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers. * This should be used when the type key is known, e.g. during Kryo deserialisation or with key caches or key managers.
* @param signatureScheme a signature scheme (e.g. ECDSA_SECP256K1_SHA256). * @param signatureScheme a signature scheme (e.g. ECDSA_SECP256K1_SHA256).
* @param encodedKey an X509 encoded public key. * @param encodedKey an X509 encoded public key.
* @throws IllegalArgumentException if the requested scheme is not supported * @throws IllegalArgumentException if the requested scheme is not supported.
* @throws InvalidKeySpecException if the given key specification * @throws InvalidKeySpecException if the given key specification
* is inappropriate for this key factory to produce a public key. * is inappropriate for this key factory to produce a public key.
*/ */
@ -444,7 +448,7 @@ object Crypto {
*/ */
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class)
fun doVerify(publicKey: PublicKey, transactionSignature: TransactionSignature): Boolean { fun doVerify(publicKey: PublicKey, transactionSignature: TransactionSignature): Boolean {
if (publicKey != transactionSignature.metaData.publicKey) IllegalArgumentException("MetaData's publicKey: ${transactionSignature.metaData.publicKey.encoded.toBase58()} does not match the input clearData: ${publicKey.encoded.toBase58()}") if (publicKey != transactionSignature.metaData.publicKey) IllegalArgumentException("MetaData's publicKey: ${transactionSignature.metaData.publicKey.toStringShort()} does not match")
return Crypto.doVerify(publicKey, transactionSignature.signatureData, transactionSignature.metaData.bytes()) return Crypto.doVerify(publicKey, transactionSignature.signatureData, transactionSignature.metaData.bytes())
} }
@ -598,9 +602,9 @@ 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 <a href="https://safecurves.cr.yp.to/twist.html">Small subgroup and invalid-curve attacks</a> for a more descriptive explanation on such attacks.
* We use this function on [findSignatureScheme] for a [PublicKey]; 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're 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 I2P's 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.
@ -625,4 +629,66 @@ object Crypto {
/** Check if the requested [SignatureScheme] is supported by the system. */ /** Check if the requested [SignatureScheme] is supported by the system. */
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean = supportedSignatureSchemes[signatureScheme.schemeCodeName] === signatureScheme fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean = supportedSignatureSchemes[signatureScheme.schemeCodeName] === signatureScheme
// map algorithm names returned from Keystore (or after encode/decode) to the supported algorithm names.
private fun matchingAlgorithmName(algorithm: String): String {
return when (algorithm) {
"EC" -> "ECDSA"
"SPHINCS-256" -> "SPHINCS256"
"1.3.6.1.4.1.22554.2.1" -> "SPHINCS256" // Unfortunately, PKCS8Key and X509Key parsing return the OID as the algorithm name and not SPHINCS256.
else -> algorithm
}
}
// validate a key, by checking its algorithmic params.
private fun validateKey(signatureScheme: SignatureScheme, key: Key): Boolean {
return when (key) {
is PublicKey -> validatePublicKey(signatureScheme, key)
is PrivateKey -> validatePrivateKey(signatureScheme, key)
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
}
}
// 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 {
when (key) {
is BCECPublicKey, is EdDSAPublicKey -> return publicKeyOnCurve(signatureScheme, key)
is BCRSAPublicKey, is BCSphincs256PublicKey -> return true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size).
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
}
}
// check if a private key satisfies algorithm specs.
private fun validatePrivateKey(signatureScheme: SignatureScheme, key: PrivateKey): Boolean {
when (key) {
is BCECPrivateKey -> return key.parameters == signatureScheme.algSpec
is EdDSAPrivateKey -> return key.params == signatureScheme.algSpec
is BCRSAPrivateKey, is BCSphincs256PrivateKey -> return true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size).
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
}
}
/**
* Convert a public key to a supported implementation. This can be used to convert a SUN's EC key to an BC key.
* This method is usually required to retrieve a key (via its corresponding cert) from JKS keystores that by default return SUN implementations.
* @param key a public key.
* @return a supported implementation of the input public key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key.
*/
fun toSupportedPublicKey(key: PublicKey): PublicKey {
return Crypto.decodePublicKey(key.encoded)
}
/**
* Convert a private key to a supported implementation. This can be used to convert a SUN's EC key to an BC key.
* This method is usually required to retrieve keys from JKS keystores that by default return SUN implementations.
* @param key a private key.
* @return a supported implementation of the input private key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key.
*/
fun toSupportedPrivateKey(key: PrivateKey): PrivateKey {
return Crypto.decodePrivateKey(key.encoded)
}
} }

View File

@ -71,11 +71,9 @@ fun KeyPair.sign(bytesToSign: OpaqueBytes, party: Party) = sign(bytesToSign.byte
// implementation of CompositeSignature. // implementation of CompositeSignature.
@Throws(InvalidKeyException::class) @Throws(InvalidKeyException::class)
fun KeyPair.sign(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable { fun KeyPair.sign(bytesToSign: ByteArray, party: Party): DigitalSignature.LegallyIdentifiable {
// Quick workaround when we have CompositeKey as Party owningKey.
if (party.owningKey is CompositeKey) throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
val sig = sign(bytesToSign) val sig = sign(bytesToSign)
val sigKey = when (party.owningKey) { // Quick workaround when we have CompositeKey as Party owningKey.
is CompositeKey -> throw InvalidKeyException("Signing for parties with CompositeKey not supported.")
else -> party.owningKey
}
return DigitalSignature.LegallyIdentifiable(party, sig.bytes) return DigitalSignature.LegallyIdentifiable(party, sig.bytes)
} }

View File

@ -16,10 +16,10 @@ object KeyStoreUtilities {
/** /**
* Helper method to either open an existing keystore for modification, or create a new blank keystore. * Helper method to either open an existing keystore for modification, or create a new blank keystore.
* @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.
* @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): KeyStore {
val pass = storePassword.toCharArray() val pass = storePassword.toCharArray()
@ -34,11 +34,11 @@ object KeyStoreUtilities {
} }
/** /**
* Helper method to open an existing keystore for modification/read * Helper method to open an existing keystore for modification/read.
* @param keyStoreFilePath location of KeyStore file which must exist, or this will throw FileNotFoundException * @param keyStoreFilePath location of KeyStore file which must exist, or this will throw FileNotFoundException.
* @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.
* @return returns the KeyStore opened * @return returns the KeyStore opened.
* @throws IOException if there was an error reading the key store from the file. * @throws IOException if there was an error reading the key store from the file.
* @throws KeyStoreException if the password is incorrect or the key store is damaged. * @throws KeyStoreException if the password is incorrect or the key store is damaged.
*/ */
@ -48,11 +48,11 @@ object KeyStoreUtilities {
} }
/** /**
* Helper method to open an existing keystore for modification/read * Helper method to open an existing keystore for modification/read.
* @param input stream containing a KeyStore e.g. loaded from a resource file * @param input stream containing a KeyStore e.g. loaded from a resource 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.
* @return returns the KeyStore opened * @return returns the KeyStore opened.
* @throws IOException if there was an error reading the key store from the stream. * @throws IOException if there was an error reading the key store from the stream.
* @throws KeyStoreException if the password is incorrect or the key store is damaged. * @throws KeyStoreException if the password is incorrect or the key store is damaged.
*/ */
@ -68,12 +68,12 @@ object KeyStoreUtilities {
} }
/** /**
* Helper extension method to add, or overwrite any key data in store * Helper extension method to add, or overwrite any key data in store.
* @param alias name to record the private key and certificate chain under * @param alias name to record the private key and certificate chain under.
* @param key cryptographic key to store * @param key cryptographic key to store.
* @param password password for unlocking the key entry in the future. This does not have to be the same password as any keys stored, * @param password password for unlocking the key entry in the future. 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 chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert * @param chain the sequence of certificates starting with the public key certificate for this key and extending to the root CA cert.
*/ */
fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<Certificate>) { fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain: Array<Certificate>) {
if (containsAlias(alias)) { if (containsAlias(alias)) {
@ -83,9 +83,9 @@ fun KeyStore.addOrReplaceKey(alias: String, key: Key, password: CharArray, chain
} }
/** /**
* Helper extension method to add, or overwrite any public certificate data in store * Helper extension method to add, or overwrite any public certificate data in store.
* @param alias name to record the public certificate under * @param alias name to record the public certificate under.
* @param cert certificate to store * @param cert certificate to store.
*/ */
fun KeyStore.addOrReplaceCertificate(alias: String, cert: Certificate) { fun KeyStore.addOrReplaceCertificate(alias: String, cert: Certificate) {
if (containsAlias(alias)) { if (containsAlias(alias)) {
@ -96,8 +96,8 @@ fun KeyStore.addOrReplaceCertificate(alias: String, cert: Certificate) {
/** /**
* Helper method save KeyStore to storage * Helper method save KeyStore to storage.
* @param keyStoreFilePath the file location to save to * @param keyStoreFilePath the file location to save to.
* @param storePassword password to access the store in future. This does not have to be the same password as any keys stored, * @param storePassword password to access the store in future. 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.
*/ */
@ -108,31 +108,47 @@ fun KeyStore.store(out: OutputStream, password: String) = store(out, password.to
/** /**
* Extract public and private keys from a KeyStore file assuming storage alias is known. * Extract public and private keys from a KeyStore file assuming storage alias is known.
* @param keyPassword Password to unlock the private key entries * @param alias The name to lookup the Key and Certificate chain from.
* @param alias The name to lookup the Key and Certificate chain from * @param keyPassword Password to unlock the private key entries.
* @return The KeyPair found in the KeyStore under the specified alias * @return The KeyPair found in the KeyStore under the specified alias.
*/ */
fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertificateAndKey(alias, keyPassword).keyPair fun KeyStore.getKeyPair(alias: String, keyPassword: String): KeyPair = getCertificateAndKeyPair(alias, keyPassword).keyPair
/** /**
* Helper method to load a Certificate and KeyPair from their KeyStore. * Helper method to load a Certificate and KeyPair from their KeyStore.
* The access details should match those of the createCAKeyStoreAndTrustStore call used to manufacture the keys. * The access details should match those of the createCAKeyStoreAndTrustStore call used to manufacture the keys.
* @param keyPassword The password for the PrivateKey (not the store access password)
* @param alias The name to search for the data. Typically if generated with the methods here this will be one of * @param alias The name to search for the data. Typically if generated with the methods here this will be one of
* CERT_PRIVATE_KEY_ALIAS, ROOT_CA_CERT_PRIVATE_KEY_ALIAS, INTERMEDIATE_CA_PRIVATE_KEY_ALIAS defined above * CERT_PRIVATE_KEY_ALIAS, ROOT_CA_CERT_PRIVATE_KEY_ALIAS, INTERMEDIATE_CA_PRIVATE_KEY_ALIAS defined above.
* @param keyPassword The password for the PrivateKey (not the store access password).
*/ */
fun KeyStore.getCertificateAndKey(alias: String, keyPassword: String): CertificateAndKey { fun KeyStore.getCertificateAndKeyPair(alias: String, keyPassword: String): CertificateAndKeyPair {
val keyPass = keyPassword.toCharArray()
val key = getKey(alias, keyPass) as PrivateKey
val cert = getCertificate(alias) as X509Certificate val cert = getCertificate(alias) as X509Certificate
// Using Crypto.decodePublicKey to convert X509Key to bouncy castle public key implementation. return CertificateAndKeyPair(cert, KeyPair(Crypto.toSupportedPublicKey(cert.publicKey), getSupportedKey(alias, keyPassword)))
// Using Crypto.decodePrivateKey to convert sun provider key implementation to bouncy castle private key implementation.
return CertificateAndKey(cert, KeyPair(Crypto.decodePublicKey(cert.publicKey.encoded), Crypto.decodePrivateKey(key.encoded)))
} }
/** /**
* Extract public X509 certificate from a KeyStore file assuming storage alias is know * Extract public X509 certificate from a KeyStore file assuming storage alias is known.
* @param alias The name to lookup the Key and Certificate chain from * @param alias The name to lookup the Key and Certificate chain from.
* @return The X509Certificate found in the KeyStore under the specified alias * @return The X509Certificate found in the KeyStore under the specified alias.
*/ */
fun KeyStore.getX509Certificate(alias: String): X509Certificate = getCertificate(alias) as X509Certificate fun KeyStore.getX509Certificate(alias: String): X509Certificate = getCertificate(alias) as X509Certificate
/**
* Extract a private key from a KeyStore file assuming storage alias is known.
* By default, a JKS keystore returns PrivateKey implementations supported by the SUN provider.
* For instance, if one imports a BouncyCastle ECC key, JKS will return a SUN ECC key implementation on getKey.
* To convert to a supported implementation, an encode->decode method is applied to the keystore's returned object.
* @param alias The name to lookup the Key.
* @param keyPassword Password to unlock the private key entries.
* @return the requested private key in supported type.
* @throws KeyStoreException if the keystore has not been initialized.
* @throws NoSuchAlgorithmException if the algorithm for recovering the key cannot be found (not supported from the Keystore provider).
* @throws UnrecoverableKeyException if the key cannot be recovered (e.g., the given password is wrong).
* @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key.
*/
fun KeyStore.getSupportedKey(alias: String, keyPassword: String): PrivateKey {
val keyPass = keyPassword.toCharArray()
val key = getKey(alias, keyPass) as PrivateKey
return Crypto.toSupportedPrivateKey(key)
}

View File

@ -44,12 +44,12 @@ object X509Utilities {
private val DEFAULT_VALIDITY_WINDOW = Pair(0, 365 * 10) private val DEFAULT_VALIDITY_WINDOW = Pair(0, 365 * 10)
/** /**
* Helper method to get a notBefore and notAfter pair from current day bounded by parent certificate validity range * Helper method to get a notBefore and notAfter pair from current day bounded by parent certificate validity range.
* @param daysBefore number of days to roll back returned start date relative to current date * @param daysBefore number of days to roll back returned start date relative to current date.
* @param daysAfter number of days to roll forward returned end date relative to current date * @param daysAfter number of days to roll forward returned end date relative to current date.
* @param parentNotBefore if provided is used to lower bound the date interval returned * @param parentNotBefore if provided is used to lower bound the date interval returned.
* @param parentNotAfter if provided is used to upper bound the date interval returned * @param parentNotAfter if provided is used to upper bound the date interval returned.
* Note we use Date rather than LocalDate as the consuming java.security and BouncyCastle certificate apis all use Date * Note we use Date rather than LocalDate as the consuming java.security and BouncyCastle certificate apis all use Date.
* Thus we avoid too many round trip conversions. * Thus we avoid too many round trip conversions.
*/ */
private fun getCertificateValidityWindow(daysBefore: Int, daysAfter: Int, parentNotBefore: Date? = null, parentNotAfter: Date? = null): Pair<Date, Date> { private fun getCertificateValidityWindow(daysBefore: Int, daysAfter: Int, parentNotBefore: Date? = null, parentNotAfter: Date? = null): Pair<Date, Date> {
@ -96,57 +96,56 @@ object X509Utilities {
/* /*
* Create a de novo root self-signed X509 v3 CA cert and [KeyPair]. * Create a de novo root self-signed X509 v3 CA cert and [KeyPair].
* @param subject the cert Subject will be populated with the domain string * @param subject the cert Subject will be populated with the domain string.
* @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided. * @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new root CA Cert and its [KeyPair] for signing downstream certificates. * @return A data class is returned containing the new root CA Cert and its [KeyPair] for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 2 to be in line with commercially available certificates * Note the generated certificate tree is capped at max depth of 2 to be in line with commercially available certificates.
*/ */
@JvmStatic @JvmStatic
fun createSelfSignedCACert(subject: X500Name, keyPair: KeyPair, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey { fun createSelfSignedCACert(subject: X500Name, keyPair: KeyPair, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKeyPair {
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second) val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second)
val cert = Crypto.createCertificate(subject, keyPair, subject, keyPair.public, CA_KEY_USAGE, CA_KEY_PURPOSES, window, pathLength = 2) val cert = Crypto.createCertificate(subject, keyPair, subject, keyPair.public, CA_KEY_USAGE, CA_KEY_PURPOSES, window, pathLength = 2)
return CertificateAndKey(cert, keyPair) return CertificateAndKeyPair(cert, keyPair)
} }
@JvmStatic @JvmStatic
fun createSelfSignedCACert(subject: X500Name, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, fun createSelfSignedCACert(subject: X500Name, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME,
validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKeyPair
= createSelfSignedCACert(subject, generateKeyPair(signatureScheme), validityWindow) = createSelfSignedCACert(subject, generateKeyPair(signatureScheme), validityWindow)
/** /**
* Create a de novo root intermediate X509 v3 CA cert and KeyPair. * Create a de novo root intermediate X509 v3 CA cert and KeyPair.
* @param subject subject of the generated certificate. * @param subject subject of the generated certificate.
* @param ca The Public certificate and KeyPair of the root CA certificate above this used to sign it * @param ca The Public certificate and KeyPair of the root CA certificate above this used to sign it.
* @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided. * @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates. * @return A data class is returned containing the new intermediate CA Cert and its KeyPair for signing downstream certificates.
* Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates * Note the generated certificate tree is capped at max depth of 1 below this to be in line with commercially available certificates.
*/ */
@JvmStatic @JvmStatic
fun createIntermediateCert(subject: X500Name, ca: CertificateAndKey, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKey { fun createIntermediateCert(subject: X500Name, ca: CertificateAndKeyPair, signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME, validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): CertificateAndKeyPair {
val keyPair = generateKeyPair(signatureScheme) val keyPair = generateKeyPair(signatureScheme)
val issuer = X509CertificateHolder(ca.certificate.encoded).subject val issuer = X509CertificateHolder(ca.certificate.encoded).subject
val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, ca.certificate.notBefore, ca.certificate.notAfter) val window = getCertificateValidityWindow(validityWindow.first, validityWindow.second, ca.certificate.notBefore, ca.certificate.notAfter)
val cert = Crypto.createCertificate(issuer, ca.keyPair, subject, keyPair.public, CA_KEY_USAGE, CA_KEY_PURPOSES, window, pathLength = 1) val cert = Crypto.createCertificate(issuer, ca.keyPair, subject, keyPair.public, CA_KEY_USAGE, CA_KEY_PURPOSES, window, pathLength = 1)
return CertificateAndKey(cert, keyPair) return CertificateAndKeyPair(cert, keyPair)
} }
/** /**
* Create an X509v3 certificate suitable for use in TLS roles. * Create an X509v3 certificate suitable for use in TLS roles.
* @param subject The contents to put in the subject field of the certificate * @param subject The contents to put in the subject field of the certificate.
* @param publicKey The PublicKey to be wrapped in the certificate * @param publicKey The PublicKey to be wrapped in the certificate.
* @param ca The Public certificate and KeyPair of the parent CA that will sign this certificate * @param ca The Public certificate and KeyPair of the parent CA that will sign this certificate.
* @param subjectAlternativeNameDomains A set of alternate DNS names to be supported by the certificate during validation of the TLS handshakes * @param subjectAlternativeNameDomains A set of alternate DNS names to be supported by the certificate during validation of the TLS handshakes.
* @param subjectAlternativeNameIps A set of alternate IP addresses to be supported by the certificate during validation of the TLS handshakes * @param subjectAlternativeNameIps A set of alternate IP addresses to be supported by the certificate during validation of the TLS handshakes.
* @param signatureScheme The signature scheme which will be used to generate keys and certificate. Default to [DEFAULT_TLS_SIGNATURE_SCHEME] if not provided.
* @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided. * @param validityWindow The certificate's validity window. Default to [DEFAULT_VALIDITY_WINDOW] if not provided.
* @return The generated X509Certificate suitable for use as a Server/Client certificate in TLS. * @return The generated X509Certificate suitable for use as a Server/Client certificate in TLS.
* This certificate is not marked as a CA cert to be similar in nature to commercial certificates. * This certificate is not marked as a CA cert to be similar in nature to commercial certificates.
*/ */
@JvmStatic @JvmStatic
fun createServerCert(subject: X500Name, publicKey: PublicKey, fun createServerCert(subject: X500Name, publicKey: PublicKey,
ca: CertificateAndKey, ca: CertificateAndKeyPair,
subjectAlternativeNameDomains: List<String>, subjectAlternativeNameDomains: List<String>,
subjectAlternativeNameIps: List<String>, subjectAlternativeNameIps: List<String>,
validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): X509Certificate { validityWindow: Pair<Int, Int> = DEFAULT_VALIDITY_WINDOW): X509Certificate {
@ -168,8 +167,8 @@ object X509Utilities {
* @param targetCertAndKey certificate the path ends at. * @param targetCertAndKey certificate the path ends at.
* @param revocationEnabled whether revocation of certificates in the path should be checked. * @param revocationEnabled whether revocation of certificates in the path should be checked.
*/ */
fun createCertificatePath(rootCertAndKey: CertificateAndKey, fun createCertificatePath(rootCertAndKey: CertificateAndKeyPair,
targetCertAndKey: CertificateAndKey, targetCertAndKey: CertificateAndKeyPair,
revocationEnabled: Boolean): CertPathBuilderResult { revocationEnabled: Boolean): CertPathBuilderResult {
val intermediateCertificates = setOf(targetCertAndKey.certificate) val intermediateCertificates = setOf(targetCertAndKey.certificate)
val certStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(intermediateCertificates)) val certStore = CertStore.getInstance("Collection", CollectionCertStoreParameters(intermediateCertificates))
@ -189,9 +188,9 @@ object X509Utilities {
} }
/** /**
* Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection * Helper method to store a .pem/.cer format file copy of a certificate if required for import into a PC/Mac, or for inspection.
* @param x509Certificate certificate to save * @param x509Certificate certificate to save.
* @param filename Target filename * @param filename Target filename.
*/ */
@JvmStatic @JvmStatic
fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, filename: Path) { fun saveCertificateAsPEMFile(x509Certificate: X509Certificate, filename: Path) {
@ -203,9 +202,9 @@ object X509Utilities {
} }
/** /**
* Helper method to load back a .pem/.cer format file copy of a certificate * Helper method to load back a .pem/.cer format file copy of a certificate.
* @param filename Source filename * @param filename Source filename.
* @return The X509Certificate that was encoded in the file * @return The X509Certificate that was encoded in the file.
*/ */
@JvmStatic @JvmStatic
fun loadCertificateFromPEMFile(filename: Path): X509Certificate { fun loadCertificateFromPEMFile(filename: Path): X509Certificate {
@ -217,14 +216,14 @@ object X509Utilities {
} }
/** /**
* An all in wrapper to manufacture a server certificate and keys all stored in a KeyStore suitable for running TLS on the local machine * An all in wrapper to manufacture a server certificate and keys all stored in a KeyStore suitable for running TLS on the local machine.
* @param keyStoreFilePath KeyStore path to save output to * @param keyStoreFilePath KeyStore path to save output to.
* @param storePassword access password for KeyStore * @param storePassword access password for KeyStore.
* @param keyPassword PrivateKey access password for the generated keys. * @param keyPassword PrivateKey access password for the generated keys.
* It is recommended that this is the same as the storePassword as most TLS libraries assume they are the same. * It is recommended that this is the same as the storePassword as most TLS libraries assume they are the same.
* @param caKeyStore KeyStore containing CA keys generated by createCAKeyStoreAndTrustStore * @param caKeyStore KeyStore containing CA keys generated by createCAKeyStoreAndTrustStore.
* @param caKeyPassword password to unlock private keys in the CA KeyStore * @param caKeyPassword password to unlock private keys in the CA KeyStore.
* @return The KeyStore created containing a private key, certificate chain and root CA public cert for use in TLS applications * @return The KeyStore created containing a private key, certificate chain and root CA public cert for use in TLS applications.
*/ */
fun createKeystoreForSSL(keyStoreFilePath: Path, fun createKeystoreForSSL(keyStoreFilePath: Path,
storePassword: String, storePassword: String,
@ -234,8 +233,8 @@ object X509Utilities {
commonName: X500Name, commonName: X500Name,
signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME): KeyStore { signatureScheme: SignatureScheme = DEFAULT_TLS_SIGNATURE_SCHEME): KeyStore {
val rootCA = caKeyStore.getCertificateAndKey(CORDA_ROOT_CA_PRIVATE_KEY, caKeyPassword) val rootCA = caKeyStore.getCertificateAndKeyPair(CORDA_ROOT_CA_PRIVATE_KEY, caKeyPassword)
val intermediateCA = caKeyStore.getCertificateAndKey(CORDA_INTERMEDIATE_CA_PRIVATE_KEY, caKeyPassword) val intermediateCA = caKeyStore.getCertificateAndKeyPair(CORDA_INTERMEDIATE_CA_PRIVATE_KEY, caKeyPassword)
val serverKey = generateKeyPair(signatureScheme) val serverKey = generateKeyPair(signatureScheme)
val host = InetAddress.getLocalHost() val host = InetAddress.getLocalHost()
@ -259,7 +258,7 @@ object X509Utilities {
/** /**
* Rebuild the distinguished name, adding a postfix to the common name. If no common name is present, this throws an * Rebuild the distinguished name, adding a postfix to the common name. If no common name is present, this throws an
* exception * exception.
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun X500Name.appendToCommonName(commonName: String): X500Name = mutateCommonName { attr -> attr.toString() + commonName } fun X500Name.appendToCommonName(commonName: String): X500Name = mutateCommonName { attr -> attr.toString() + commonName }
@ -269,7 +268,7 @@ fun X500Name.appendToCommonName(commonName: String): X500Name = mutateCommonName
* adds one. * adds one.
*/ */
@Throws(IllegalArgumentException::class) @Throws(IllegalArgumentException::class)
fun X500Name.replaceCommonName(commonName: String): X500Name = mutateCommonName { attr -> commonName } fun X500Name.replaceCommonName(commonName: String): X500Name = mutateCommonName { _ -> commonName }
/** /**
* Rebuild the distinguished name, replacing the common name with a value generated from the provided function. * Rebuild the distinguished name, replacing the common name with a value generated from the provided function.
@ -307,4 +306,4 @@ class CertificateStream(val input: InputStream) {
fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate fun nextCertificate(): X509Certificate = certificateFactory.generateCertificate(input) as X509Certificate
} }
data class CertificateAndKey(val certificate: X509Certificate, val keyPair: KeyPair) data class CertificateAndKeyPair(val certificate: X509Certificate, val keyPair: KeyPair)

View File

@ -1,7 +1,7 @@
package net.corda.core.identity package net.corda.core.identity
import net.corda.core.contracts.PartyAndReference import net.corda.core.contracts.PartyAndReference
import net.corda.core.crypto.CertificateAndKey import net.corda.core.crypto.CertificateAndKeyPair
import net.corda.core.crypto.toBase58String import net.corda.core.crypto.toBase58String
import net.corda.core.serialization.OpaqueBytes import net.corda.core.serialization.OpaqueBytes
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
@ -27,7 +27,7 @@ import java.security.PublicKey
* @see CompositeKey * @see CompositeKey
*/ */
class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) { class Party(val name: X500Name, owningKey: PublicKey) : AbstractParty(owningKey) {
constructor(certAndKey: CertificateAndKey) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public) constructor(certAndKey: CertificateAndKeyPair) : this(X500Name(certAndKey.certificate.subjectDN.name), certAndKey.keyPair.public)
override fun toString() = name.toString() override fun toString() = name.toString()
override fun nameOrNull(): X500Name? = name override fun nameOrNull(): X500Name? = name

View File

@ -2,7 +2,6 @@ package net.corda.core.crypto
import net.corda.core.div import net.corda.core.div
import net.corda.testing.MEGA_CORP import net.corda.testing.MEGA_CORP
import net.i2p.crypto.eddsa.EdDSAEngine
import net.corda.testing.getTestX509Name import net.corda.testing.getTestX509Name
import org.bouncycastle.asn1.x500.X500Name import org.bouncycastle.asn1.x500.X500Name
import org.bouncycastle.asn1.x509.GeneralName import org.bouncycastle.asn1.x509.GeneralName
@ -177,14 +176,14 @@ class X509UtilitiesTest {
// Load signing intermediate CA cert // Load signing intermediate CA cert
val caKeyStore = KeyStoreUtilities.loadKeyStore(tmpCAKeyStore, "cakeystorepass") val caKeyStore = KeyStoreUtilities.loadKeyStore(tmpCAKeyStore, "cakeystorepass")
val caCertAndKey = caKeyStore.getCertificateAndKey(X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY, "cakeypass") val caCertAndKey = caKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_INTERMEDIATE_CA_PRIVATE_KEY, "cakeypass")
// Generate server cert and private key and populate another keystore suitable for SSL // Generate server cert and private key and populate another keystore suitable for SSL
X509Utilities.createKeystoreForSSL(tmpServerKeyStore, "serverstorepass", "serverkeypass", caKeyStore, "cakeypass", MEGA_CORP.name) X509Utilities.createKeystoreForSSL(tmpServerKeyStore, "serverstorepass", "serverkeypass", caKeyStore, "cakeypass", MEGA_CORP.name)
// Load back server certificate // Load back server certificate
val serverKeyStore = KeyStoreUtilities.loadKeyStore(tmpServerKeyStore, "serverstorepass") val serverKeyStore = KeyStoreUtilities.loadKeyStore(tmpServerKeyStore, "serverstorepass")
val serverCertAndKey = serverKeyStore.getCertificateAndKey(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY, "serverkeypass") val serverCertAndKey = serverKeyStore.getCertificateAndKeyPair(X509Utilities.CORDA_CLIENT_CA_PRIVATE_KEY, "serverkeypass")
serverCertAndKey.certificate.checkValidity(Date()) serverCertAndKey.certificate.checkValidity(Date())
serverCertAndKey.certificate.verify(caCertAndKey.certificate.publicKey) serverCertAndKey.certificate.verify(caCertAndKey.certificate.publicKey)
@ -349,4 +348,18 @@ class X509UtilitiesTest {
return keyStore return keyStore
} }
@Test
fun `Get correct private key type from Keystore`() {
val keyPair = Crypto.generateKeyPair(Crypto.ECDSA_SECP256R1_SHA256)
val selfSignCert = X509Utilities.createSelfSignedCACert(X500Name("CN=Test"), keyPair)
val keyStore = KeyStoreUtilities.loadOrCreateKeyStore(tempFile("testKeystore.jks"), "keystorepassword")
keyStore.setKeyEntry("Key", keyPair.private, "keypassword".toCharArray(), arrayOf(selfSignCert.certificate))
val keyFromKeystore = keyStore.getKey("Key", "keypassword".toCharArray())
val keyFromKeystoreCasted = keyStore.getSupportedKey("Key", "keypassword")
assertTrue(keyFromKeystore is java.security.interfaces.ECPrivateKey) // by default JKS returns SUN EC key
assertTrue(keyFromKeystoreCasted is org.bouncycastle.jce.interfaces.ECPrivateKey)
}
} }

View File

@ -605,7 +605,7 @@ abstract class AbstractNode(open val configuration: NodeConfiguration,
val identityAndKey = if (configuration.keyStoreFile.exists() && keystore.containsAlias(privateKeyAlias)) { val identityAndKey = if (configuration.keyStoreFile.exists() && keystore.containsAlias(privateKeyAlias)) {
// Get keys from keystore. // Get keys from keystore.
val (cert, keyPair) = keystore.getCertificateAndKey(privateKeyAlias, configuration.keyStorePassword) val (cert, keyPair) = keystore.getCertificateAndKeyPair(privateKeyAlias, configuration.keyStorePassword)
val loadedServiceName = X509CertificateHolder(cert.encoded).subject val loadedServiceName = X509CertificateHolder(cert.encoded).subject
if (X509CertificateHolder(cert.encoded).subject != serviceName) { if (X509CertificateHolder(cert.encoded).subject != serviceName) {
throw ConfigurationException("The legal name in the config file doesn't match the stored identity keystore:" + throw ConfigurationException("The legal name in the config file doesn't match the stored identity keystore:" +

View File

@ -1,6 +1,5 @@
package net.corda.node.services.network package net.corda.node.services.network
import net.corda.core.crypto.CertificateAndKey
import net.corda.core.crypto.X509Utilities import net.corda.core.crypto.X509Utilities
import net.corda.core.crypto.generateKeyPair import net.corda.core.crypto.generateKeyPair
import net.corda.core.identity.AnonymousParty import net.corda.core.identity.AnonymousParty