Made all the members of Crypto.kt static so that Java users aren't forced to use Crypto.INSTANCE.

This commit is contained in:
Shams Asari 2017-08-15 23:16:24 +01:00
parent 576e1c3c20
commit 3860b22339
7 changed files with 194 additions and 103 deletions

View File

@ -13,7 +13,7 @@ import java.security.Signature
* This builder will use bouncy castle's JcaContentSignerBuilder as fallback for unknown algorithm. * This builder will use bouncy castle'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): ContentSigner {
val sigAlgId = signatureScheme.signatureOID val sigAlgId = signatureScheme.signatureOID
val sig = Signature.getInstance(signatureScheme.signatureName, provider).apply { val sig = Signature.getInstance(signatureScheme.signatureName, provider).apply {
if (random != null) { if (random != null) {

View File

@ -70,6 +70,7 @@ object Crypto {
* RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function. * RSA_SHA256 signature scheme using SHA256 as hash algorithm and MGF1 (with SHA256) as mask generation function.
* Note: Recommended key size >= 3072 bits. * Note: Recommended key size >= 3072 bits.
*/ */
@JvmField
val RSA_SHA256 = SignatureScheme( val RSA_SHA256 = SignatureScheme(
1, 1,
"RSA_SHA256", "RSA_SHA256",
@ -84,6 +85,7 @@ object Crypto {
) )
/** ECDSA signature scheme using the secp256k1 Koblitz curve. */ /** ECDSA signature scheme using the secp256k1 Koblitz curve. */
@JvmField
val ECDSA_SECP256K1_SHA256 = SignatureScheme( val ECDSA_SECP256K1_SHA256 = SignatureScheme(
2, 2,
"ECDSA_SECP256K1_SHA256", "ECDSA_SECP256K1_SHA256",
@ -98,6 +100,7 @@ object Crypto {
) )
/** ECDSA signature scheme using the secp256r1 (NIST P-256) curve. */ /** ECDSA signature scheme using the secp256r1 (NIST P-256) curve. */
@JvmField
val ECDSA_SECP256R1_SHA256 = SignatureScheme( val ECDSA_SECP256R1_SHA256 = SignatureScheme(
3, 3,
"ECDSA_SECP256R1_SHA256", "ECDSA_SECP256R1_SHA256",
@ -112,6 +115,7 @@ object Crypto {
) )
/** EdDSA signature scheme using the ed255519 twisted Edwards curve. */ /** EdDSA signature scheme using the ed255519 twisted Edwards curve. */
@JvmField
val EDDSA_ED25519_SHA512 = SignatureScheme( val EDDSA_ED25519_SHA512 = SignatureScheme(
4, 4,
"EDDSA_ED25519_SHA512", "EDDSA_ED25519_SHA512",
@ -131,7 +135,10 @@ object Crypto {
* SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers * SPHINCS-256 hash-based signature scheme. It provides 128bit security against post-quantum attackers
* at the cost of larger key sizes and loss of compatibility. * at the cost of larger key sizes and loss of compatibility.
*/ */
@JvmField
val SHA512_256 = DLSequence(arrayOf(NISTObjectIdentifiers.id_sha512_256)) val SHA512_256 = DLSequence(arrayOf(NISTObjectIdentifiers.id_sha512_256))
@JvmField
val SPHINCS256_SHA256 = SignatureScheme( val SPHINCS256_SHA256 = SignatureScheme(
5, 5,
"SPHINCS-256_SHA512", "SPHINCS-256_SHA512",
@ -146,13 +153,12 @@ object Crypto {
"at the cost of larger key sizes and loss of compatibility." "at the cost of larger key sizes and loss of compatibility."
) )
/** /** Corda composite key type */
* Corda composite key type @JvmField
*/
val COMPOSITE_KEY = SignatureScheme( val COMPOSITE_KEY = SignatureScheme(
6, 6,
"COMPOSITE", "COMPOSITE",
AlgorithmIdentifier(CordaObjectIdentifier.compositeKey), AlgorithmIdentifier(CordaObjectIdentifier.COMPOSITE_KEY),
emptyList(), emptyList(),
CordaSecurityProvider.PROVIDER_NAME, CordaSecurityProvider.PROVIDER_NAME,
CompositeKey.KEY_ALGORITHM, CompositeKey.KEY_ALGORITHM,
@ -163,13 +169,14 @@ object Crypto {
) )
/** Our default signature scheme if no algorithm is specified (e.g. for key generation). */ /** Our default signature scheme if no algorithm is specified (e.g. for key generation). */
@JvmField
val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512 val DEFAULT_SIGNATURE_SCHEME = EDDSA_ED25519_SHA512
/** /**
* Supported digital signature schemes. * Supported digital signature schemes.
* Note: Only the schemes added in this map will be supported (see [Crypto]). * Note: Only the schemes added in this map will be supported (see [Crypto]).
*/ */
val supportedSignatureSchemes = listOf( private val signatureSchemeMap: Map<String, SignatureScheme> = listOf(
RSA_SHA256, RSA_SHA256,
ECDSA_SECP256K1_SHA256, ECDSA_SECP256K1_SHA256,
ECDSA_SECP256R1_SHA256, ECDSA_SECP256R1_SHA256,
@ -183,15 +190,15 @@ object Crypto {
* algorithm identifiers. * algorithm identifiers.
*/ */
private val algorithmMap: Map<AlgorithmIdentifier, SignatureScheme> private val algorithmMap: Map<AlgorithmIdentifier, SignatureScheme>
= (supportedSignatureSchemes.values.flatMap { scheme -> scheme.alternativeOIDs.map { oid -> Pair(oid, scheme) } } = (signatureSchemeMap.values.flatMap { scheme -> scheme.alternativeOIDs.map { Pair(it, scheme) } }
+ supportedSignatureSchemes.values.map { Pair(it.signatureOID, it) }) + signatureSchemeMap.values.map { Pair(it.signatureOID, it) })
.toMap() .toMap()
// This map is required to defend against users that forcibly call Security.addProvider / Security.removeProvider // 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.
// The val is private to avoid any harmful state changes. // The val is private to avoid any harmful state changes.
val providerMap: Map<String, Provider> = mapOf( private val providerMap: Map<String, Provider> = mapOf(
BouncyCastleProvider.PROVIDER_NAME to getBouncyCastleProvider(), BouncyCastleProvider.PROVIDER_NAME to getBouncyCastleProvider(),
CordaSecurityProvider.PROVIDER_NAME to CordaSecurityProvider(), CordaSecurityProvider.PROVIDER_NAME to CordaSecurityProvider(),
"BCPQC" to BouncyCastlePQCProvider()) // unfortunately, provider's name is not final in BouncyCastlePQCProvider, so we explicitly set it. "BCPQC" to BouncyCastlePQCProvider()) // unfortunately, provider's name is not final in BouncyCastlePQCProvider, so we explicitly set it.
@ -201,6 +208,14 @@ object Crypto {
addKeyInfoConverter(EDDSA_ED25519_SHA512.signatureOID.algorithm, KeyInfoConverter(EDDSA_ED25519_SHA512)) addKeyInfoConverter(EDDSA_ED25519_SHA512.signatureOID.algorithm, KeyInfoConverter(EDDSA_ED25519_SHA512))
} }
@JvmStatic
fun supportedSignatureSchemes(): List<SignatureScheme> = ArrayList(signatureSchemeMap.values)
@JvmStatic
fun findProvider(name: String): Provider {
return providerMap[name] ?: throw IllegalArgumentException("Unrecognised provider: $name")
}
init { init {
// This registration is needed for reading back EdDSA key from java keystore. // 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. // 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.
@ -219,8 +234,10 @@ object Crypto {
} }
} }
@JvmStatic
fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme { fun findSignatureScheme(algorithm: AlgorithmIdentifier): SignatureScheme {
return algorithmMap[normaliseAlgorithmIdentifier(algorithm)] ?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm.algorithm.id}") return algorithmMap[normaliseAlgorithmIdentifier(algorithm)]
?: throw IllegalArgumentException("Unrecognised algorithm: ${algorithm.algorithm.id}")
} }
/** /**
@ -231,8 +248,11 @@ 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.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun findSignatureScheme(schemeCodeName: String): SignatureScheme = supportedSignatureSchemes[schemeCodeName] ?: throw IllegalArgumentException("Unsupported key/algorithm for schemeCodeName: $schemeCodeName") fun findSignatureScheme(schemeCodeName: String): SignatureScheme {
return signatureSchemeMap[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].
@ -242,7 +262,7 @@ object Crypto {
* @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.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun findSignatureScheme(key: PublicKey): SignatureScheme { fun findSignatureScheme(key: PublicKey): SignatureScheme {
val keyInfo = SubjectPublicKeyInfo.getInstance(key.encoded) val keyInfo = SubjectPublicKeyInfo.getInstance(key.encoded)
return findSignatureScheme(keyInfo.algorithm) return findSignatureScheme(keyInfo.algorithm)
@ -256,7 +276,7 @@ object Crypto {
* @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.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun findSignatureScheme(key: PrivateKey): SignatureScheme { fun findSignatureScheme(key: PrivateKey): SignatureScheme {
val keyInfo = PrivateKeyInfo.getInstance(key.encoded) val keyInfo = PrivateKeyInfo.getInstance(key.encoded)
return findSignatureScheme(keyInfo.privateKeyAlgorithm) return findSignatureScheme(keyInfo.privateKeyAlgorithm)
@ -269,11 +289,12 @@ object Crypto {
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun decodePrivateKey(encodedKey: ByteArray): PrivateKey { fun decodePrivateKey(encodedKey: ByteArray): PrivateKey {
val keyInfo = PrivateKeyInfo.getInstance(encodedKey) val keyInfo = PrivateKeyInfo.getInstance(encodedKey)
val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm) val signatureScheme = findSignatureScheme(keyInfo.privateKeyAlgorithm)
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey)) val keyFactory = KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} }
/** /**
@ -284,8 +305,11 @@ object Crypto {
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class) @JvmStatic
fun decodePrivateKey(schemeCodeName: String, encodedKey: ByteArray): PrivateKey = decodePrivateKey(findSignatureScheme(schemeCodeName), encodedKey) @Throws(InvalidKeySpecException::class)
fun decodePrivateKey(schemeCodeName: String, encodedKey: ByteArray): PrivateKey {
return decodePrivateKey(findSignatureScheme(schemeCodeName), encodedKey)
}
/** /**
* Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name. * Decode a PKCS8 encoded key to its [PrivateKey] object based on the input scheme code name.
@ -295,13 +319,18 @@ object Crypto {
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class) @JvmStatic
@Throws(InvalidKeySpecException::class)
fun decodePrivateKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PrivateKey { fun decodePrivateKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PrivateKey {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
try { try {
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePrivate(PKCS8EncodedKeySpec(encodedKey)) val keyFactory = KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
return keyFactory.generatePrivate(PKCS8EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
throw InvalidKeySpecException("This private key cannot be decoded, please ensure it is PKCS8 encoded and that it corresponds to the input scheme's code name.", ikse) throw InvalidKeySpecException("This private key cannot be decoded, please ensure it is PKCS8 encoded and that " +
"it corresponds to the input scheme's code name.", ikse)
} }
} }
@ -312,11 +341,12 @@ object Crypto {
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for this key factory to produce a private key. * is inappropriate for this key factory to produce a private key.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun decodePublicKey(encodedKey: ByteArray): PublicKey { fun decodePublicKey(encodedKey: ByteArray): PublicKey {
val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey) val subjectPublicKeyInfo = SubjectPublicKeyInfo.getInstance(encodedKey)
val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm) val signatureScheme = findSignatureScheme(subjectPublicKeyInfo.algorithm)
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePublic(X509EncodedKeySpec(encodedKey)) val keyFactory = KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
return keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))
} }
/** /**
@ -328,8 +358,11 @@ object Crypto {
* @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.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class) @JvmStatic
fun decodePublicKey(schemeCodeName: String, encodedKey: ByteArray): PublicKey = decodePublicKey(findSignatureScheme(schemeCodeName), encodedKey) @Throws(InvalidKeySpecException::class)
fun decodePublicKey(schemeCodeName: String, encodedKey: ByteArray): PublicKey {
return decodePublicKey(findSignatureScheme(schemeCodeName), encodedKey)
}
/** /**
* Decode an X509 encoded key to its [PrivateKey] object based on the input scheme code name. * Decode an X509 encoded key to its [PrivateKey] object based on the input scheme code name.
@ -340,19 +373,25 @@ object Crypto {
* @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.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeySpecException::class) @JvmStatic
@Throws(InvalidKeySpecException::class)
fun decodePublicKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PublicKey { fun decodePublicKey(signatureScheme: SignatureScheme, encodedKey: ByteArray): PublicKey {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
try { try {
return KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]).generatePublic(X509EncodedKeySpec(encodedKey)) val keyFactory = KeyFactory.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
return keyFactory.generatePublic(X509EncodedKeySpec(encodedKey))
} catch (ikse: InvalidKeySpecException) { } catch (ikse: InvalidKeySpecException) {
throw throw InvalidKeySpecException("This public key cannot be decoded, please ensure it is X509 encoded and that it corresponds to the input scheme's code name.", ikse) throw throw InvalidKeySpecException("This public key cannot be decoded, please ensure it is X509 encoded and " +
"that it corresponds to the input scheme's code name.", ikse)
} }
} }
/** /**
* Generic way to sign [ByteArray] data with a [PrivateKey]. Strategy on on identifying the actual signing scheme is based * Generic way to sign [ByteArray] data with a [PrivateKey]. Strategy on on identifying the actual signing scheme is based
* on the [PrivateKey] type, but if the schemeCodeName is known, then better use doSign(signatureScheme: String, privateKey: PrivateKey, clearData: ByteArray). * on the [PrivateKey] type, but if the schemeCodeName is known, then better use
* doSign(signatureScheme: String, privateKey: PrivateKey, clearData: ByteArray).
* @param privateKey the signer's [PrivateKey]. * @param privateKey the signer's [PrivateKey].
* @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root). * @param clearData the data/message to be signed in [ByteArray] form (usually the Merkle root).
* @return the digital signature (in [ByteArray]) on the input message. * @return the digital signature (in [ByteArray]) on the input message.
@ -360,7 +399,8 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(privateKey), privateKey, clearData) fun doSign(privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(privateKey), privateKey, clearData)
/** /**
@ -373,8 +413,11 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @JvmStatic
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray) = doSign(findSignatureScheme(schemeCodeName), privateKey, clearData) @Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(schemeCodeName: String, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
return doSign(findSignatureScheme(schemeCodeName), privateKey, clearData)
}
/** /**
* Generic way to sign [ByteArray] data with a [PrivateKey] and a known [Signature]. * Generic way to sign [ByteArray] data with a [PrivateKey] and a known [Signature].
@ -386,11 +429,14 @@ object Crypto {
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray { fun doSign(signatureScheme: SignatureScheme, privateKey: PrivateKey, clearData: ByteArray): ByteArray {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
require(clearData.isNotEmpty()) { "Signing of an empty array is not permitted!" }
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
if (clearData.isEmpty()) throw Exception("Signing of an empty array is not permitted!")
signature.initSign(privateKey) signature.initSign(privateKey)
signature.update(clearData) signature.update(clearData)
return signature.sign() return signature.sign()
@ -398,20 +444,24 @@ object Crypto {
/** /**
* Generic way to sign [SignableData] objects with a [PrivateKey]. * Generic way to sign [SignableData] objects with a [PrivateKey].
* [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as a timestamp or partial and blind signature indicators. * [SignableData] is a wrapper over the transaction's id (Merkle root) in order to attach extra information, such as
* @param privateKey the signer's [PrivateKey]. * a timestamp or partial and blind signature indicators.
* @param keyPair the signer's [KeyPair].
* @param signableData a [SignableData] object that adds extra information to a transaction. * @param signableData a [SignableData] object that adds extra information to a transaction.
* @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and the signature metadata. * @return a [TransactionSignature] object than contains the output of a successful signing, signer's public key and
* the signature metadata.
* @throws IllegalArgumentException if the signature scheme is not supported for this private key. * @throws IllegalArgumentException if the signature scheme is not supported for this private key.
* @throws InvalidKeyException if the private key is invalid. * @throws InvalidKeyException if the private key is invalid.
* @throws SignatureException if signing is not possible due to malformed data or private key. * @throws SignatureException if signing is not possible due to malformed data or private key.
*/ */
@Throws(IllegalArgumentException::class, InvalidKeyException::class, SignatureException::class) @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature { fun doSign(keyPair: KeyPair, signableData: SignableData): TransactionSignature {
val sigKey: SignatureScheme = findSignatureScheme(keyPair.private) val sigKey: SignatureScheme = findSignatureScheme(keyPair.private)
val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public) val sigMetaData: SignatureScheme = findSignatureScheme(keyPair.public)
if (sigKey != sigMetaData) throw IllegalArgumentException("Metadata schemeCodeName: ${sigMetaData.schemeCodeName}" + require(sigKey == sigMetaData) {
" is not aligned with the key type: ${sigKey.schemeCodeName}.") "Metadata schemeCodeName: ${sigMetaData.schemeCodeName} is not aligned with the key type: ${sigKey.schemeCodeName}."
}
val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes) val signatureBytes = doSign(sigKey.schemeCodeName, keyPair.private, signableData.serialize().bytes)
return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata) return TransactionSignature(signatureBytes, keyPair.public, signableData.signatureMetadata)
} }
@ -430,8 +480,11 @@ object Crypto {
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty. * @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/ */
@JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class) @Throws(InvalidKeyException::class, SignatureException::class)
fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData) fun doVerify(schemeCodeName: String, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
return doVerify(findSignatureScheme(schemeCodeName), publicKey, signatureData, clearData)
}
/** /**
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type. * Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type.
@ -448,8 +501,11 @@ object Crypto {
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty. * @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @JvmStatic
fun doVerify(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = doVerify(findSignatureScheme(publicKey), publicKey, signatureData, clearData) @Throws(InvalidKeyException::class, SignatureException::class)
fun doVerify(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
return doVerify(findSignatureScheme(publicKey), publicKey, signatureData, clearData)
}
/** /**
* Method to verify a digital signature. * Method to verify a digital signature.
@ -465,9 +521,12 @@ object Crypto {
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty. * @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doVerify(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { fun doVerify(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
if (signatureData.isEmpty()) throw IllegalArgumentException("Signature data is empty!") if (signatureData.isEmpty()) throw IllegalArgumentException("Signature data is empty!")
if (clearData.isEmpty()) throw IllegalArgumentException("Clear data is empty, nothing to verify!") if (clearData.isEmpty()) throw IllegalArgumentException("Clear data is empty, nothing to verify!")
val verificationResult = isValid(signatureScheme, publicKey, signatureData, clearData) val verificationResult = isValid(signatureScheme, publicKey, signatureData, clearData)
@ -490,14 +549,16 @@ object Crypto {
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty. * @throws IllegalArgumentException if the signature scheme is not supported or if any of the clear or signature data is empty.
*/ */
@Throws(InvalidKeyException::class, SignatureException::class, IllegalArgumentException::class) @JvmStatic
@Throws(InvalidKeyException::class, SignatureException::class)
fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean { fun doVerify(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
val signableData = SignableData(txId, transactionSignature.signatureMetadata) val signableData = SignableData(txId, transactionSignature.signatureMetadata)
return Crypto.doVerify(transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes) return Crypto.doVerify(transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes)
} }
/** /**
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type. * Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the
* input public key's type.
* It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature * It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws, * do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
* as it avoids the risk of failing to test the result. * as it avoids the risk of failing to test the result.
@ -507,14 +568,20 @@ object Crypto {
* the passed-in signatureData is improperly encoded or of the wrong type, * the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
*/ */
@JvmStatic
@Throws(SignatureException::class) @Throws(SignatureException::class)
fun isValid(txId: SecureHash, transactionSignature: TransactionSignature): Boolean { fun isValid(txId: SecureHash, transactionSignature: TransactionSignature): Boolean {
val signableData = SignableData(txId, transactionSignature.signatureMetadata) val signableData = SignableData(txId, transactionSignature.signatureMetadata)
return isValid(findSignatureScheme(transactionSignature.by), transactionSignature.by, transactionSignature.bytes, signableData.serialize().bytes) return isValid(
findSignatureScheme(transactionSignature.by),
transactionSignature.by,
transactionSignature.bytes,
signableData.serialize().bytes)
} }
/** /**
* Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the input public key's type. * Utility to simplify the act of verifying a digital signature by identifying the signature scheme used from the
* input public key's type.
* It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature * It returns true if it succeeds and false if not. In comparison to [doVerify] if the key and signature
* do not match it returns false rather than throwing an exception. Normally you should use the function which throws, * do not match it returns false rather than throwing an exception. Normally you should use the function which throws,
* as it avoids the risk of failing to test the result. * as it avoids the risk of failing to test the result.
@ -527,8 +594,11 @@ object Crypto {
* the passed-in signatureData is improperly encoded or of the wrong type, * the passed-in signatureData is improperly encoded or of the wrong type,
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
*/ */
@JvmStatic
@Throws(SignatureException::class) @Throws(SignatureException::class)
fun isValid(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray) = isValid(findSignatureScheme(publicKey), publicKey, signatureData, clearData) fun isValid(publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
return isValid(findSignatureScheme(publicKey), publicKey, signatureData, clearData)
}
/** /**
* Method to verify a digital signature. In comparison to [doVerify] if the key and signature * Method to verify a digital signature. In comparison to [doVerify] if the key and signature
@ -544,9 +614,12 @@ object Crypto {
* if this signatureData scheme is unable to process the input data provided, if the verification is not possible. * if this signatureData scheme is unable to process the input data provided, if the verification is not possible.
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @throws IllegalArgumentException if the requested signature scheme is not supported.
*/ */
@Throws(SignatureException::class, IllegalArgumentException::class) @JvmStatic
@Throws(SignatureException::class)
fun isValid(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean { fun isValid(signatureScheme: SignatureScheme, publicKey: PublicKey, signatureData: ByteArray, clearData: ByteArray): Boolean {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName]) val signature = Signature.getInstance(signatureScheme.signatureName, providerMap[signatureScheme.providerName])
signature.initVerify(publicKey) signature.initVerify(publicKey)
signature.update(clearData) signature.update(clearData)
@ -560,7 +633,7 @@ object Crypto {
* @return a KeyPair for the requested signature scheme code name. * @return a KeyPair for the requested signature scheme code name.
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @throws IllegalArgumentException if the requested signature scheme is not supported.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun generateKeyPair(schemeCodeName: String): KeyPair = generateKeyPair(findSignatureScheme(schemeCodeName)) fun generateKeyPair(schemeCodeName: String): KeyPair = generateKeyPair(findSignatureScheme(schemeCodeName))
/** /**
@ -570,10 +643,12 @@ object Crypto {
* @return a new [KeyPair] for the requested [SignatureScheme]. * @return a new [KeyPair] for the requested [SignatureScheme].
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @throws IllegalArgumentException if the requested signature scheme is not supported.
*/ */
@Throws(IllegalArgumentException::class)
@JvmOverloads @JvmOverloads
@JvmStatic
fun generateKeyPair(signatureScheme: SignatureScheme = DEFAULT_SIGNATURE_SCHEME): KeyPair { fun generateKeyPair(signatureScheme: SignatureScheme = DEFAULT_SIGNATURE_SCHEME): KeyPair {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
"Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
}
val keyPairGenerator = KeyPairGenerator.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName]) val keyPairGenerator = KeyPairGenerator.getInstance(signatureScheme.algorithmName, providerMap[signatureScheme.providerName])
if (signatureScheme.algSpec != null) if (signatureScheme.algSpec != null)
keyPairGenerator.initialize(signatureScheme.algSpec, newSecureRandom()) keyPairGenerator.initialize(signatureScheme.algSpec, newSecureRandom())
@ -638,13 +713,17 @@ object Crypto {
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @throws IllegalArgumentException if the requested signature scheme is not supported.
* @throws UnsupportedOperationException if deterministic key generation is not supported for this particular scheme. * @throws UnsupportedOperationException if deterministic key generation is not supported for this particular scheme.
*/ */
@JvmStatic
fun deriveKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair { fun deriveKeyPair(signatureScheme: SignatureScheme, privateKey: PrivateKey, seed: ByteArray): KeyPair {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
when (signatureScheme) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> return deriveKeyPairECDSA(signatureScheme.algSpec as ECParameterSpec, privateKey, seed) }
EDDSA_ED25519_SHA512 -> return deriveKeyPairEdDSA(privateKey, seed) return when (signatureScheme) {
ECDSA_SECP256R1_SHA256, ECDSA_SECP256K1_SHA256 -> deriveKeyPairECDSA(signatureScheme.algSpec as ECParameterSpec, privateKey, seed)
EDDSA_ED25519_SHA512 -> deriveKeyPairEdDSA(privateKey, seed)
else -> throw UnsupportedOperationException("Although supported for signing, deterministic key derivation is " +
"not currently implemented for ${signatureScheme.schemeCodeName}")
} }
throw UnsupportedOperationException("Although supported for signing, deterministic key derivation is not currently implemented for ${signatureScheme.schemeCodeName}")
} }
/** /**
@ -656,6 +735,7 @@ object Crypto {
* @throws IllegalArgumentException if the requested signature scheme is not supported. * @throws IllegalArgumentException if the requested signature scheme is not supported.
* @throws UnsupportedOperationException if deterministic key generation is not supported for this particular scheme. * @throws UnsupportedOperationException if deterministic key generation is not supported for this particular scheme.
*/ */
@JvmStatic
fun deriveKeyPair(privateKey: PrivateKey, seed: ByteArray): KeyPair { fun deriveKeyPair(privateKey: PrivateKey, seed: ByteArray): KeyPair {
return deriveKeyPair(findSignatureScheme(privateKey), privateKey, seed) return deriveKeyPair(findSignatureScheme(privateKey), privateKey, seed)
} }
@ -728,11 +808,13 @@ object Crypto {
* @return a new [KeyPair] from an entropy input. * @return a new [KeyPair] from an entropy input.
* @throws IllegalArgumentException if the requested signature scheme is not supported for KeyPair generation using an entropy input. * @throws IllegalArgumentException if the requested signature scheme is not supported for KeyPair generation using an entropy input.
*/ */
@JvmStatic
fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair { fun deriveKeyPairFromEntropy(signatureScheme: SignatureScheme, entropy: BigInteger): KeyPair {
when (signatureScheme) { return when (signatureScheme) {
EDDSA_ED25519_SHA512 -> return deriveEdDSAKeyPairFromEntropy(entropy) EDDSA_ED25519_SHA512 -> deriveEdDSAKeyPairFromEntropy(entropy)
else -> throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair " +
"generation: ${signatureScheme.schemeCodeName}")
} }
throw IllegalArgumentException("Unsupported signature scheme for fixed entropy-based key pair generation: ${signatureScheme.schemeCodeName}")
} }
/** /**
@ -740,6 +822,7 @@ object Crypto {
* @param entropy a [BigInteger] value. * @param entropy a [BigInteger] value.
* @return a new [KeyPair] from an entropy input. * @return a new [KeyPair] from an entropy input.
*/ */
@JvmStatic
fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy) fun deriveKeyPairFromEntropy(entropy: BigInteger): KeyPair = deriveKeyPairFromEntropy(DEFAULT_SIGNATURE_SCHEME, entropy)
// custom key pair generator from entropy. // custom key pair generator from entropy.
@ -766,8 +849,12 @@ object Crypto {
} }
private class KeyInfoConverter(val signatureScheme: SignatureScheme) : AsymmetricKeyInfoConverter { private class KeyInfoConverter(val signatureScheme: SignatureScheme) : AsymmetricKeyInfoConverter {
override fun generatePublic(keyInfo: SubjectPublicKeyInfo?): PublicKey? = keyInfo?.let { decodePublicKey(signatureScheme, it.encoded) } override fun generatePublic(keyInfo: SubjectPublicKeyInfo?): PublicKey? {
override fun generatePrivate(keyInfo: PrivateKeyInfo?): PrivateKey? = keyInfo?.let { decodePrivateKey(signatureScheme, it.encoded) } return keyInfo?.let { decodePublicKey(signatureScheme, it.encoded) }
}
override fun generatePrivate(keyInfo: PrivateKeyInfo?): PrivateKey? {
return keyInfo?.let { decodePrivateKey(signatureScheme, it.encoded) }
}
} }
/** /**
@ -784,22 +871,29 @@ object Crypto {
* @return true if the point lies on the curve or false if it doesn't. * @return true if the point lies on the curve or false if it doesn't.
* @throws IllegalArgumentException if the requested signature scheme or the key type is not supported. * @throws IllegalArgumentException if the requested signature scheme or the key type is not supported.
*/ */
@Throws(IllegalArgumentException::class) @JvmStatic
fun publicKeyOnCurve(signatureScheme: SignatureScheme, publicKey: PublicKey): Boolean { fun publicKeyOnCurve(signatureScheme: SignatureScheme, publicKey: PublicKey): Boolean {
require(isSupportedSignatureScheme(signatureScheme)) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}" } require(isSupportedSignatureScheme(signatureScheme)) {
when (publicKey) { "Unsupported key/algorithm for schemeCodeName: ${signatureScheme.schemeCodeName}"
is BCECPublicKey -> return (publicKey.parameters == signatureScheme.algSpec && !publicKey.q.isInfinity && publicKey.q.isValid) }
is EdDSAPublicKey -> return (publicKey.params == signatureScheme.algSpec && !isEdDSAPointAtInfinity(publicKey) && publicKey.a.isOnCurve) return when (publicKey) {
is BCECPublicKey -> publicKey.parameters == signatureScheme.algSpec && !publicKey.q.isInfinity && publicKey.q.isValid
is EdDSAPublicKey -> publicKey.params == signatureScheme.algSpec && !isEdDSAPointAtInfinity(publicKey) && publicKey.a.isOnCurve
else -> throw IllegalArgumentException("Unsupported key type: ${publicKey::class}") else -> throw IllegalArgumentException("Unsupported key type: ${publicKey::class}")
} }
} }
// return true if EdDSA publicKey is point at infinity. // return true if EdDSA publicKey is point at infinity.
// For EdDSA a custom function is required as it is not supported by the I2P implementation. // For EdDSA a custom function is required as it is not supported by the I2P implementation.
private fun isEdDSAPointAtInfinity(publicKey: EdDSAPublicKey) = publicKey.a.toP3() == (EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3) private fun isEdDSAPointAtInfinity(publicKey: EdDSAPublicKey): Boolean {
return publicKey.a.toP3() == (EDDSA_ED25519_SHA512.algSpec as EdDSANamedCurveSpec).curve.getZero(GroupElement.Representation.P3)
}
/** Check if the requested [SignatureScheme] is supported by the system. */ /** Check if the requested [SignatureScheme] is supported by the system. */
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean = supportedSignatureSchemes[signatureScheme.schemeCodeName] === signatureScheme @JvmStatic
fun isSupportedSignatureScheme(signatureScheme: SignatureScheme): Boolean {
return signatureScheme.schemeCodeName in signatureSchemeMap
}
// validate a key, by checking its algorithmic params. // validate a key, by checking its algorithmic params.
private fun validateKey(signatureScheme: SignatureScheme, key: Key): Boolean { private fun validateKey(signatureScheme: SignatureScheme, key: Key): Boolean {
@ -812,19 +906,19 @@ object Crypto {
// check if a public key satisfies algorithm specs (for ECC: key should lie on the curve and not being point-at-infinity). // check if a public key satisfies algorithm specs (for ECC: key should lie on the curve and not being point-at-infinity).
private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean { private fun validatePublicKey(signatureScheme: SignatureScheme, key: PublicKey): Boolean {
when (key) { return when (key) {
is BCECPublicKey, is EdDSAPublicKey -> return publicKeyOnCurve(signatureScheme, key) is BCECPublicKey, is EdDSAPublicKey -> publicKeyOnCurve(signatureScheme, key)
is BCRSAPublicKey, is BCSphincs256PublicKey -> return true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size). is BCRSAPublicKey, is BCSphincs256PublicKey -> true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size).
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}") else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
} }
} }
// check if a private key satisfies algorithm specs. // check if a private key satisfies algorithm specs.
private fun validatePrivateKey(signatureScheme: SignatureScheme, key: PrivateKey): Boolean { private fun validatePrivateKey(signatureScheme: SignatureScheme, key: PrivateKey): Boolean {
when (key) { return when (key) {
is BCECPrivateKey -> return key.parameters == signatureScheme.algSpec is BCECPrivateKey -> key.parameters == signatureScheme.algSpec
is EdDSAPrivateKey -> return key.params == signatureScheme.algSpec is EdDSAPrivateKey -> 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). is BCRSAPrivateKey, is BCSphincs256PrivateKey -> true // TODO: Check if non-ECC keys satisfy params (i.e. approved/valid RSA modulus size).
else -> throw IllegalArgumentException("Unsupported key type: ${key::class}") else -> throw IllegalArgumentException("Unsupported key type: ${key::class}")
} }
} }
@ -838,21 +932,20 @@ object Crypto {
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key. * is inappropriate for a supported key factory to produce a private key.
*/ */
fun toSupportedPublicKey(key: SubjectPublicKeyInfo): PublicKey { @JvmStatic
return Crypto.decodePublicKey(key.encoded) fun toSupportedPublicKey(key: SubjectPublicKeyInfo): PublicKey = decodePublicKey(key.encoded)
}
/** /**
* Convert a public key to a supported implementation. This can be used to convert a SUN's EC key to an BC key. * 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. * 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. * @param key a public key.
* @return a supported implementation of the input public key. * @return a supported implementation of the input public key.
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key. * is inappropriate for a supported key factory to produce a private key.
*/ */
fun toSupportedPublicKey(key: PublicKey): PublicKey { @JvmStatic
return Crypto.decodePublicKey(key.encoded) fun toSupportedPublicKey(key: PublicKey): PublicKey = 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. * Convert a private key to a supported implementation. This can be used to convert a SUN's EC key to an BC key.
@ -862,7 +955,6 @@ object Crypto {
* @throws IllegalArgumentException on not supported scheme or if the given key specification * @throws IllegalArgumentException on not supported scheme or if the given key specification
* is inappropriate for a supported key factory to produce a private key. * is inappropriate for a supported key factory to produce a private key.
*/ */
fun toSupportedPrivateKey(key: PrivateKey): PrivateKey { @JvmStatic
return Crypto.decodePrivateKey(key.encoded) fun toSupportedPrivateKey(key: PrivateKey): PrivateKey = decodePrivateKey(key.encoded)
}
} }

View File

@ -42,7 +42,7 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
fun getInstance(asn1: ASN1Primitive): PublicKey { fun getInstance(asn1: ASN1Primitive): PublicKey {
val keyInfo = SubjectPublicKeyInfo.getInstance(asn1) val keyInfo = SubjectPublicKeyInfo.getInstance(asn1)
require(keyInfo.algorithm.algorithm == CordaObjectIdentifier.compositeKey) require(keyInfo.algorithm.algorithm == CordaObjectIdentifier.COMPOSITE_KEY)
val sequence = ASN1Sequence.getInstance(keyInfo.parsePublicKey()) val sequence = ASN1Sequence.getInstance(keyInfo.parsePublicKey())
val threshold = ASN1Integer.getInstance(sequence.getObjectAt(0)).positiveValue.toInt() val threshold = ASN1Integer.getInstance(sequence.getObjectAt(0)).positiveValue.toInt()
val sequenceOfChildren = ASN1Sequence.getInstance(sequence.getObjectAt(1)) val sequenceOfChildren = ASN1Sequence.getInstance(sequence.getObjectAt(1))
@ -177,7 +177,7 @@ class CompositeKey private constructor(val threshold: Int, children: List<NodeAn
} }
keyVector.add(ASN1Integer(threshold.toLong())) keyVector.add(ASN1Integer(threshold.toLong()))
keyVector.add(DERSequence(childrenVector)) keyVector.add(DERSequence(childrenVector))
return SubjectPublicKeyInfo(AlgorithmIdentifier(CordaObjectIdentifier.compositeKey), DERSequence(keyVector)).encoded return SubjectPublicKeyInfo(AlgorithmIdentifier(CordaObjectIdentifier.COMPOSITE_KEY), DERSequence(keyVector)).encoded
} }
override fun getFormat() = ASN1Encoding.DER override fun getFormat() = ASN1Encoding.DER

View File

@ -11,7 +11,7 @@ import java.security.spec.AlgorithmParameterSpec
*/ */
class CompositeSignature : Signature(SIGNATURE_ALGORITHM) { class CompositeSignature : Signature(SIGNATURE_ALGORITHM) {
companion object { companion object {
val SIGNATURE_ALGORITHM = "COMPOSITESIG" const val SIGNATURE_ALGORITHM = "COMPOSITESIG"
fun getService(provider: Provider) = Provider.Service(provider, "Signature", SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, emptyList(), emptyMap()) fun getService(provider: Provider) = Provider.Service(provider, "Signature", SIGNATURE_ALGORITHM, CompositeSignature::class.java.name, emptyList(), emptyMap())
} }

View File

@ -3,14 +3,13 @@ package net.corda.core.crypto.provider
import net.corda.core.crypto.composite.CompositeKey import net.corda.core.crypto.composite.CompositeKey
import net.corda.core.crypto.composite.CompositeSignature import net.corda.core.crypto.composite.CompositeSignature
import org.bouncycastle.asn1.ASN1ObjectIdentifier import org.bouncycastle.asn1.ASN1ObjectIdentifier
import org.bouncycastle.asn1.x509.AlgorithmIdentifier
import java.security.AccessController import java.security.AccessController
import java.security.PrivilegedAction import java.security.PrivilegedAction
import java.security.Provider import java.security.Provider
class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") { class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME security provider wrapper") {
companion object { companion object {
val PROVIDER_NAME = "Corda" const val PROVIDER_NAME = "Corda"
} }
init { init {
@ -21,7 +20,7 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", "net.corda.core.crypto.composite.KeyFactory") put("KeyFactory.${CompositeKey.KEY_ALGORITHM}", "net.corda.core.crypto.composite.KeyFactory")
put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", "net.corda.core.crypto.composite.CompositeSignature") put("Signature.${CompositeSignature.SIGNATURE_ALGORITHM}", "net.corda.core.crypto.composite.CompositeSignature")
val compositeKeyOID = CordaObjectIdentifier.compositeKey.id val compositeKeyOID = CordaObjectIdentifier.COMPOSITE_KEY.id
put("Alg.Alias.KeyFactory.$compositeKeyOID", CompositeKey.KEY_ALGORITHM) put("Alg.Alias.KeyFactory.$compositeKeyOID", CompositeKey.KEY_ALGORITHM)
put("Alg.Alias.KeyFactory.OID.$compositeKeyOID", CompositeKey.KEY_ALGORITHM) put("Alg.Alias.KeyFactory.OID.$compositeKeyOID", CompositeKey.KEY_ALGORITHM)
put("Alg.Alias.Signature.$compositeKeyOID", CompositeSignature.SIGNATURE_ALGORITHM) put("Alg.Alias.Signature.$compositeKeyOID", CompositeSignature.SIGNATURE_ALGORITHM)
@ -32,6 +31,6 @@ class CordaSecurityProvider : Provider(PROVIDER_NAME, 0.1, "$PROVIDER_NAME secur
object CordaObjectIdentifier { object CordaObjectIdentifier {
// UUID-based OID // UUID-based OID
// TODO: Register for an OID space and issue our own shorter OID // TODO: Register for an OID space and issue our own shorter OID
val compositeKey = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791002") @JvmField val COMPOSITE_KEY = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791002")
val compositeSignature = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791003") @JvmField val COMPOSITE_SIGNATURE = ASN1ObjectIdentifier("2.25.30086077608615255153862931087626791003")
} }

View File

@ -343,7 +343,7 @@ class CryptoUtilsTest {
// test list of supported algorithms // test list of supported algorithms
@Test @Test
fun `Check supported algorithms`() { fun `Check supported algorithms`() {
val algList: List<String> = Crypto.supportedSignatureSchemes.keys.toList() val algList: List<String> = Crypto.supportedSignatureSchemes().map { it.schemeCodeName }
val expectedAlgSet = setOf("RSA_SHA256", "ECDSA_SECP256K1_SHA256", "ECDSA_SECP256R1_SHA256", "EDDSA_ED25519_SHA512", "SPHINCS-256_SHA512", "COMPOSITE") val expectedAlgSet = setOf("RSA_SHA256", "ECDSA_SECP256K1_SHA256", "ECDSA_SECP256R1_SHA256", "EDDSA_ED25519_SHA512", "SPHINCS-256_SHA512", "COMPOSITE")
assertTrue { Sets.symmetricDifference(expectedAlgSet, algList.toSet()).isEmpty(); } assertTrue { Sets.symmetricDifference(expectedAlgSet, algList.toSet()).isEmpty(); }
} }

View File

@ -211,7 +211,7 @@ object X509Utilities {
nameConstraints: NameConstraints? = null): X509CertificateHolder { nameConstraints: NameConstraints? = null): X509CertificateHolder {
val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private) val signatureScheme = Crypto.findSignatureScheme(issuerKeyPair.private)
val provider = Crypto.providerMap[signatureScheme.providerName] val provider = Crypto.findProvider(signatureScheme.providerName)
val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints) val builder = createCertificate(certificateType, issuer, subject, subjectPublicKey, validityWindow, nameConstraints)
val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider) val signer = ContentSignerBuilder.build(signatureScheme, issuerKeyPair.private, provider)
@ -225,7 +225,7 @@ object X509Utilities {
* Create certificate signing request using provided information. * Create certificate signing request using provided information.
*/ */
fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest { fun createCertificateSigningRequest(subject: X500Name, keyPair: KeyPair, signatureScheme: SignatureScheme): PKCS10CertificationRequest {
val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.providerMap[signatureScheme.providerName]) val signer = ContentSignerBuilder.build(signatureScheme, keyPair.private, Crypto.findProvider(signatureScheme.providerName))
return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).build(signer) return JcaPKCS10CertificationRequestBuilder(subject, keyPair.public).build(signer)
} }