Search code examples
javaandroidencryptionelliptic-curveweb3-java

Web3j ECKeyPair to KeyPair


I'm trying to make an ECC with the generated key using web3j. I have the ECKeyPair object, but cipher.init() requires 2nd parameter to be Key object. ECKeyPair returns BigInteger of private key and public key, how can I convert them to KeyPair which holds PrivateKey and PublicKey object?

I've tried (reference: CryptoUtil.java):

private fun decodeKeyPair(ecKeyPair: ECKeyPair): KeyPair {
        val xp = getNamedCurveByName("secp256k1")
        val p = ECNamedCurveSpec("secp256k1", xp.curve, xp.g, xp.n, xp.h, null)
        val curve = convertCurve(p.curve)
        val g = EC5Util.convertPoint(curve, p.generator, false)
        val n = p.order
        val h = BigInteger.valueOf(p.cofactor.toLong())
        val dp = ECDomainParameters(curve, g, n, h)

        val bytes = Numeric.toBytesPadded(ecKeyPair.publicKey, 64)
        val x = Numeric.toBigInt(Arrays.copyOfRange(bytes, 0, 32))
        val y = Numeric.toBigInt(Arrays.copyOfRange(bytes, 32, 64))
        val q = curve.createPoint(x, y)
        val publicKey = BCECPublicKey(
           "EC",
            ECPublicKeyParameters(q, dp),
            BouncyCastleProvider.CONFIGURATION
        )
        val privateKey = BCECPrivateKey(
            "EC",
            ECPrivateKeyParameters(ecKeyPair.privateKey, dp),
            publicKey,
            p,
            BouncyCastleProvider.CONFIGURATION
        )
        return KeyPair(publicKey, privateKey)
    }

but this returns an error: Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'org.bouncycastle.math.ec.ECCurve org.bouncycastle.jce.spec.ECParameterSpec.getCurve()' on a null object reference

Is there any other way to convert Web3j ECKeyPair to KeyPair?


Solution

  • I finally got it after exploring around https://www.programcreek.com. Main thing here was "how to generate ECPoint using the public key string".

    Here's how I did it, instead of transforming the whole EcKeyPair object, I convert the keys separately.

    Public Key String to ECPublicKey:

    private fun toEcPublicKey(publicKey: String): ECPublicKey {
            val params = ECNamedCurveTable.getParameterSpec("secp256k1")
            val curveSpec = ECNamedCurveSpec("secp256k1", params.curve, params.g, params.n)
    
            //This is the part how to generate ECPoint manually from public key string.
            val pubKeyX = publicKey.substring(0, publicKey.length / 2)
            val pubKeyY = publicKey.substring(publicKey.length / 2)
            val ecPoint = ECPoint(BigInteger(pubKeyX, 16), BigInteger(pubKeyY, 16))
    
            val params2 = EC5Util.convertSpec(curveSpec.curve, params)
    
            val keySpec = java.security.spec.ECPublicKeySpec(ecPoint, params2)
            val factory = KeyFactory.getInstance("ECDSA")
            return factory.generatePublic(keySpec) as ECPublicKey
    }
    

    Private Key String to ECPrivateKey:

    private fun toEcPrivateKey(privateKey: String): ECPrivateKey {
            val ecKeyPair = ECKeyPair.create(Numeric.hexStringToByteArray(privateKey))
    
            val params = ECNamedCurveTable.getParameterSpec("secp256k1")
            val curveSpec = ECNamedCurveSpec("secp256k1", params.curve, params.g, params.n)
    
            val keySpec = java.security.spec.ECPrivateKeySpec(
                ecKeyPair.privateKey,
                curveSpec)
    
            val factory = KeyFactory.getInstance("ECDSA")
            return factory.generatePrivate(keySpec) as ECPrivateKey
    }
    

    String key inputs are generated from Web3j library.