Search code examples
androidkotlinencryptionbouncycastle

Instance for X448 in Bouncy Castle gives error


I am trying to generate Key pairs with Bouncy Castle and use the following Bouncy implementation and code:

implementation 'org.bouncycastle:bcprov-jdk15to18:1.70'

According to: https://javadoc.io/static/org.bouncycastle/bcprov-jdk15on/1.70/org/bouncycastle/math/ec/rfc7748/X448.html

I create the following keyPair generator:

   val keyPairGenerator =
        KeyPairGenerator.getInstance("EC", BouncyCastleProvider.PROVIDER_NAME)
    keyPairGenerator.initialize(XDHParameterSpec(XDHParameterSpec.X448))
    val keyPair = keyPairGenerator.generateKeyPair()

    val publicKey = byteArrayToHex(keyPair.public.encoded)
    val privateKey = keyPair.private.encoded

But then I get the following error:

no such algorithm: EC for provider BC

I Tried diffrent providers but to no avail.

Am I missing something?

--

Removed after suggested to open a new Question


Solution

  • The issue is most likely caused by an older BC version being installed. This must first be removed with Security.removeProvider("BC").
    Then the current BC version must be added with Security.addProvider(BouncyCastleProvider()). Only then the instantiation of KeyPairGenerator may be performed.

    Furthermore, when instantiating KeyPairGenerator, XDHParameterSpec.X448 must be specified as first parameter ("EC" would generate a key pair for the EC curve prime239v1). The line keyPairGenerator.initialize(...) can be removed (it is redundant).

    The following implementation works on my machine:

    import org.bouncycastle.jcajce.spec.XDHParameterSpec
    import org.bouncycastle.jce.provider.BouncyCastleProvider
    import java.security.KeyPairGenerator
    
    ...
    
    Security.removeProvider("BC")
    Security.addProvider(BouncyCastleProvider())
    val keyPairGenerator = KeyPairGenerator.getInstance(XDHParameterSpec.X448, BouncyCastleProvider.PROVIDER_NAME) 
    val keyPair = keyPairGenerator.generateKeyPair()
    val privateKey = keyPair.private
    val publicKey = keyPair.public
    println(Base64.getEncoder().encodeToString(privateKey.encoded)) // e.g. MIGBAgEBMAUGAytlbwQ6BDiMQCdpnzxOu7SN4Sgft8nsDLmbWLxWd7aDz4g3ht54NEuX2PyaGDK1mVJuTQ+v2bM9Sm9xyBN44YE5AHT/KcCEEdk/je/GU/6kg+vt8okKJAuLspjaHU1aK2KGXOKPS9GQXAHKjf/rBKj9b2IQnyesXMqU
    println(Base64.getEncoder().encodeToString(publicKey.encoded))  // e.g. MEIwBQYDK2VvAzkAdP8pwIQR2T+N78ZT/qSD6+3yiQokC4uymNodTVorYoZc4o9L0ZBcAcqN/+sEqP1vYhCfJ6xcypQ=
    

    privateKey.encoded returns the DER encoded private key in PKCS#8 format (incl. public key), publicKey.encoded returns the DER encoded key in X.509/SPKI format.


    Alternatively, the X448 key pair can be generated as follows:

    import java.security.SecureRandom
    import org.bouncycastle.util.encoders.Hex
    import org.bouncycastle.crypto.params.X448PrivateKeyParameters
    
    ...
    
    val secureRandom = SecureRandom()
    val privateKeyParams = X448PrivateKeyParameters(secureRandom)
    val publicKeyParams = privateKeyParams.generatePublicKey()
    println(Hex.toHexString(privateKeyParams.encoded))  // e.g. 0x7472d12245a698ac9a52be9bc9ea0c90aacb598f084f16a18ea917d56fa5411005a18e844a4aa4508272ad593ac8c1be53284d908d0fc4c6
    println(Hex.toHexString(publicKeyParams.encoded))   // e.g. 0x9d49073bd1c0149b41cf66bd389ae743dca3bb59dc1988903c32b840fb68f8d67ccb3c56be4bc65687f11b1fc5f273d7cea329427d83a829 
    

    privateKeyParams.encoded returns the raw private key (56 bytes), publicKeyParams.encoded returns the raw public key (56 bytes).