Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Class cast exception in Notification Class #108

Open
iyashsoni opened this issue Nov 19, 2019 · 6 comments
Open

Class cast exception in Notification Class #108

iyashsoni opened this issue Nov 19, 2019 · 6 comments

Comments

@iyashsoni
Copy link

I am using library version 5.0.2

I get the following error org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey cannot be cast to org.bouncycastle.jce.interfaces.ECPublicKey when I try and instantiate the Notification class at runtime.

In my setup, as we are bundling the entire server in a war file, due to re-bundling, signature breaks for bouncycastle libs, So I'm using a custom class loader to accommodate Subscription class cryptographic operations.

But in the Notification Class, there is a cast as below:

    public Notification(String endpoint, PublicKey userPublicKey, byte[] userAuth, byte[] payload, int ttl) {
        this(endpoint, (ECPublicKey)userPublicKey, userAuth, payload, ttl);
    }

This is from org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey to org.bouncycastle.jce.interfaces.ECPublicKey, which is a valid cast. But for some reason it gives me error at run time saying it is invalid.

We have added compile dependency in our project pom to avoid build issues due to missing bouncycastle lib, as we are only loading it through reflection.

@martijndwars, Can you help please?

Please let me know if you need additional info.
Thanks

@charankumarpalla
Copy link

I face a similar issue

java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey cannot be cast to org.bouncycastle.jce.interfaces.ECPublicKey
    at nl.martijndwars.webpush.Notification.<init>(Notification.java:50)

@matewka
Copy link

matewka commented Apr 16, 2020

@martijndwars can you help on this?
I'm fighting this problem for 3 days already, with no luck.

To make sure whether it's on my side or lib's side, I've created an example Node script that runs web-push Node implementation. I applied the same keys, credentials etc. that I use in Java. In Node everything runs just fine. In Java, I'm getting this error:

java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPrivateKey
	at nl.martijndwars.webpush.Utils.verifyKeyPair(Utils.java:104) ~[web-push-5.1.0.jar:na]
	at nl.martijndwars.webpush.PushService.preparePost(PushService.java:184) ~[web-push-5.1.0.jar:na]
	at nl.martijndwars.webpush.PushService.sendAsync(PushService.java:160) ~[web-push-5.1.0.jar:na]
	at nl.martijndwars.webpush.PushService.send(PushService.java:142) ~[web-push-5.1.0.jar:na]
	at nl.martijndwars.webpush.PushService.send(PushService.java:146) ~[web-push-5.1.0.jar:na]
...

Here's a (simplified) Java code that I'm using:

if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
    Security.addProvider(new BouncyCastleProvider());
}

PushService pushService;
try {
    // The provided keys below are of type String. They were generated with
    //    ./gradlew run --args="generate-key"
    // I also tried generating them with the Node package by running:
    //    npx web-push generate-vapid-keys [--json]
    // ALSO, I tried to pass them like that:
    //    new KeyPair(Utils.loadPublicKey(publicKey), Utils.loadPrivateKey(privateKey))
    // None of which worked.
    pushService = new PushService(publicKey, privateKey);
} catch (GeneralSecurityException e) {
    System.out.println("GeneralSecurityException during PushService instantiation.");
    return;
}

return subscriptions.stream().map(subscription -> {
    Subscription notificationSubscription = new Subscription(
            subscription.getEndpoint(),
            new Subscription.Keys(subscription.getKey(), subscription.getAuth()));

    try {
        Notification notification = new Notification(
                notificationSubscription, message.getMessage(), Urgency.HIGH);
        HttpResponse response = pushService.send(notification);
    } catch (Exception e) {
        // ...
    }

    // ...
})

@debabratapatra
Copy link

I am also facing the same issue. @matewka did you find any solution? Thanks

@matewka
Copy link

matewka commented May 11, 2021

@debabratapatra unfortunately not. I've abandoned this feature temporarily in my project.

@iyashsoni
Copy link
Author

iyashsoni commented May 11, 2021

Hi @debabratapatra @matewka I have solved this issue. Apologies for long and delayed answer but this will solve your problem.

So the problem is because of 2 different class loaders. The class cast exception that you see is because one class is loaded probably from a local jar, while the other is loaded from somewhere in your dependency tree.

To avoid this problem, what I had to do was write a lot of Java Reflection. Do the following:

  1. Pull webpush library source into your Java Project (ie copy everything under webpush-java/src/main/java/nl/martijndwars/webpush/* to your project)
  2. re-write the webpush Lib classes to use the classes loaded using ReflectionUtil as shown below.

Here's a ReflectionUtil class that I wrote, you can reuse the same:

package com.yashsoni.push;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.Provider;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;

public class BouncyCastleReflectionUtil {
    private static final URLClassLoader bcUrlClassLoader = URLClassLoader.newInstance(getBouncyCastleLibs());
    private static Class<?> BCProviderClass;
    private static Class<?> ECPointClass;
    private static Class<?> ECCurveClass;
    private static Class<?> ECPublicKeySpecClass;
    private static Class<?> ECPrivateKeySpecClass;
    private static Class<?> ECPublicKeyClass;
    private static Class<?> ECPrivateKeyClass;
    private static Class<?> Base64Class;
    private static Class<?> ECParameterSpecClass;
    private static Class<?> BigIntegersClass;
    private static Class<?> HKDFBytesGeneratorClass;
    private static Class<?> HKDFParametersClass;
    private static Class<?> DigestClass;
    private static Class<?> SHA256DigestClass;
    private static Class<?> ECNamedCurveTableClass;
    private static Method getParameterSpecMethod;
    private static Method decodePointMethod;
    private static Method getCurveMethod;
    private static Method decodeMethod;
    private static Method getGMethod;
    private static Method getQMethod;
    private static Method getDMethod;
    private static Method getEncodedMethod;
    private static Method multiplyMethod;
    private static Method equalsMethod;
    private static Method hkdfInitMethod;
    private static Method hkdfGenerateBytesMethod;
    private static Method fromUnsignedByteArrayMethod;
    private static String bouncyCastleProviderName;

    static {
        try {
            // Base
            BCProviderClass =
                    Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider", true, bcUrlClassLoader);
            bouncyCastleProviderName = (String) Class.
                    forName("org.bouncycastle.jce.provider.BouncyCastleProvider", true, bcUrlClassLoader).
                    getDeclaredField("PROVIDER_NAME").get(null);

            // Classes
            ECPublicKeyClass = Class.forName("org.bouncycastle.jce.interfaces.ECPublicKey", true, bcUrlClassLoader);
            ECPrivateKeyClass = Class.forName("org.bouncycastle.jce.interfaces.ECPrivateKey", true, bcUrlClassLoader);
            ECPointClass = Class.forName("org.bouncycastle.math.ec.ECPoint", true, bcUrlClassLoader);
            ECCurveClass = Class.forName("org.bouncycastle.math.ec.ECCurve", true, bcUrlClassLoader);
            ECPublicKeySpecClass = Class.forName("org.bouncycastle.jce.spec.ECPublicKeySpec", true, bcUrlClassLoader);
            ECPrivateKeySpecClass = Class.forName("org.bouncycastle.jce.spec.ECPrivateKeySpec", true, bcUrlClassLoader);
            ECParameterSpecClass = Class.forName("org.bouncycastle.jce.spec.ECParameterSpec", true, bcUrlClassLoader);
            ECNamedCurveTableClass = Class.forName("org.bouncycastle.jce.ECNamedCurveTable", true, bcUrlClassLoader);
            Base64Class = Class.forName("org.bouncycastle.util.encoders.Base64", true, bcUrlClassLoader);
            HKDFParametersClass = Class.forName("org.bouncycastle.crypto.params.HKDFParameters", true, bcUrlClassLoader);
            HKDFBytesGeneratorClass = Class.forName("org.bouncycastle.crypto.generators.HKDFBytesGenerator", true, bcUrlClassLoader);
            DigestClass = Class.forName("org.bouncycastle.crypto.Digest", true, bcUrlClassLoader);
            SHA256DigestClass = Class.forName("org.bouncycastle.crypto.digests.SHA256Digest", true, bcUrlClassLoader);
            BigIntegersClass = Class.forName("org.bouncycastle.util.BigIntegers", true, bcUrlClassLoader);
            Class<?> derivationParametersClass = Class.forName("org.bouncycastle.crypto.DerivationParameters", true, bcUrlClassLoader);

            // Methods
            getParameterSpecMethod = Class.forName("org.bouncycastle.jce.ECNamedCurveTable", true, bcUrlClassLoader).getMethod("getParameterSpec", String.class);
            getCurveMethod = Class.forName("org.bouncycastle.jce.spec.ECParameterSpec", true, bcUrlClassLoader).getMethod("getCurve");
            getGMethod = Class.forName("org.bouncycastle.jce.spec.ECParameterSpec", true, bcUrlClassLoader).getMethod("getG");
            getQMethod = ECPublicKeyClass.getMethod("getQ");
            getDMethod = ECPrivateKeyClass.getMethod("getD");
            multiplyMethod = ECPointClass.getMethod("multiply", BigInteger.class);
            getEncodedMethod = ECPointClass.getMethod("getEncoded", boolean.class);
            equalsMethod = ECPointClass.getMethod("equals", ECPointClass);
            decodePointMethod = ECCurveClass.getMethod("decodePoint", byte[].class);
            decodeMethod = Class.forName("org.bouncycastle.util.encoders.Base64", true, bcUrlClassLoader).getMethod("decode", String.class);
            fromUnsignedByteArrayMethod = BigIntegersClass.getMethod("fromUnsignedByteArray", byte[].class);
            hkdfInitMethod = HKDFBytesGeneratorClass.getMethod("init", derivationParametersClass);
            hkdfGenerateBytesMethod = HKDFBytesGeneratorClass.getMethod("generateBytes", byte[].class, int.class, int.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static URLClassLoader getBcUrlClassLoader() {
        return bcUrlClassLoader;
    }

    private static URL[] getBouncyCastleLibs() {
        final String sourceMethod = "getBouncyCastleLibs";
        String bouncyCastleLibLocation;
       
        logger.debug(sourceMethod, "Setting bouncyCastleLibLocation as : " + "config" + File.separator + "cells" + File.separator + "lib");
        bouncyCastleLibLocation = "config" +  File.separator + "lib"; // this is where your bouncycastle jar file is.
        logger.debug(sourceMethod, "bouncyCastleLibLocation dir location : " + bouncyCastleLibLocation);

        File dir = new File(bouncyCastleLibLocation);
        logger.debug(sourceMethod, "Libs dir location : " + dir.getAbsolutePath());
        String[] files = dir.list();

        logger.debug(sourceMethod, "Files in libs dir : " + Arrays.toString(files));

        ArrayList<URL> urlArrayList = new ArrayList<>();
        if (files != null) {
            try {
                for (String aFile : files) {
                    if (aFile.endsWith(".jar")) {
                        urlArrayList.add(new File(bouncyCastleLibLocation + File.separator + aFile).toURI().toURL());
                    }
                }
            } catch (MalformedURLException me) {
                logger.error(sourceMethod, "MalformedURLException in getBouncyCastleUrlCL", me);
            }

        } else {
            logger.error(sourceMethod, "Failed loading Bouncy Castle Library");
        }
        URL[] urls = new URL[urlArrayList.size()];
        urls = urlArrayList.toArray(urls);
        logger.debug(sourceMethod, "Lib URLs : " + Arrays.toString(urls));
        return urls;
    }

    static String getBouncyCastleProviderName() {
        return bouncyCastleProviderName;
    }

    public static Class getClass(BouncyCastleProviderClasses className) {
        switch (className) {
            case ECPublicKey:
                return ECPublicKeyClass;
            case ECPrivateKey:
                return ECPrivateKeyClass;
            case Base64:
                return Base64Class;
            case ECCurve:
                return ECCurveClass;
            case ECPoint:
                return ECPointClass;
            case BCProvider:
                return BCProviderClass;
            case ECPublicKeySpec:
                return ECPublicKeySpecClass;
            case BigIntegers:
                return BigIntegersClass;
            case Digest:
                return DigestClass;
            case SHA256Digest:
                return SHA256DigestClass;
            case HKDFParameters:
                return HKDFParametersClass;
            case HKDFBytesGenerator:
                return HKDFBytesGeneratorClass;
            case ECParameterSpec:
                return ECParameterSpecClass;
            case ECPrivateKeySpec:
                return ECPrivateKeySpecClass;
            case ECNamedCurveTable:
                return ECNamedCurveTableClass;
            default:
                return null;
        }
    }

    public static Method getMethod(BouncyCastleProviderMethods methodName) {
        switch (methodName) {
            case decode:
                return decodeMethod;
            case getCurve:
                return getCurveMethod;
            case decodePoint:
                return decodePointMethod;
            case getParameterSpec:
                return getParameterSpecMethod;
            case hkdfInit:
                return hkdfInitMethod;
            case hkdfGenerateBytes:
                return hkdfGenerateBytesMethod;
            case getD:
                return getDMethod;
            case getG:
                return getGMethod;
            case getQ:
                return getQMethod;
            case equals:
                return equalsMethod;
            case multiply:
                return multiplyMethod;
            case fromUnsignedByteArray:
                return fromUnsignedByteArrayMethod;
            case getEncoded:
                return getEncodedMethod;
            default:
                return null;
        }
    }

    public static void addSecurityProvider() {
        if (Security.getProvider(bouncyCastleProviderName) == null) {
            try {
                logger.debug("addSecurityProvider", "Adding Security Provider to JCE");
                Security.insertProviderAt((Provider) BCProviderClass.getConstructor().newInstance(), 1);
            } catch (InstantiationException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
                logger.error("addSecurityProvider", "Failed to add Security Provider.");
                e.printStackTrace();
            }
        }
    }

    public enum BouncyCastleProviderClasses {
        ECPublicKey, ECPrivateKey, ECPoint, BCProvider, ECCurve, Base64,
        ECPublicKeySpec, ECPrivateKeySpec, ECParameterSpec, BigIntegers, HKDFBytesGenerator, HKDFParameters,
        SHA256Digest, Digest, ECNamedCurveTable, DerivationParameters
    }

    public enum BouncyCastleProviderMethods {
        getParameterSpec, decodePoint, getCurve, decode, hkdfInit,
        hkdfGenerateBytes, getG, getD, getQ, fromUnsignedByteArray, multiply, equals, getEncoded
    }
}

Now, rewrite the webpush-java/src/main/java/nl/martijndwars/webpush/Utils.java class as follows:

package com.yashsoni.push;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.*;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class Utils {
    public static final String CURVE = "prime256v1";
    public static final String ALGORITHM = "ECDH";

    /**
     * Get the uncompressed encoding of the public key point. The resulting array
     * should be 65 bytes length and start with 0x04 followed by the x and y
     * coordinates (32 bytes each).
     *
     * @param publicKey
     * @return
     */
    public static byte[] encode(ECPublicKey publicKey) {
        try {
            Method getQMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getQ);
            Method getEncodedMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getEncoded);

            Object q = getQMethod.invoke(publicKey);
            return (byte[]) getEncodedMethod.invoke(q, false);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] encode(ECPrivateKey privateKey) {
        try {
            Method getDMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getD);
            return ((BigInteger) getDMethod.invoke(privateKey)).toByteArray();
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Load the public key from a URL-safe base64 encoded string. Takes into
     * account the different encodings, including point compression.
     *
     * @param encodedPublicKey
     */
    public static PublicKey loadPublicKey(String encodedPublicKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
        byte[] decodedPublicKey = Base64Encoder.decode(encodedPublicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, BouncyCastleReflectionUtil.getBouncyCastleProviderName());

        try {
            Class<?> ECCurveClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECCurve);
            Class<?> ECPointClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPoint);
            Class<?> ECPublicKeySpecClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPublicKeySpec);

            Method getParameterSpecMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getParameterSpec);
            Method getCurveMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getCurve);
            Method decodePointMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.decodePoint);

            Object ecSpec = getParameterSpecMethod.invoke(null, "secp256r1");
            Object ecCurve = ECCurveClass.cast(getCurveMethod.invoke(ecSpec));
            Object ecPoint = ECPointClass.cast(decodePointMethod.invoke(ecCurve, decodedPublicKey));
            Constructor pubSpecConstructor = ECPublicKeySpecClass.getConstructors()[0];
            Object pubSpec = pubSpecConstructor.newInstance(ecPoint, ecSpec);
            return keyFactory.generatePublic((KeySpec) pubSpec);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Load the private key from a URL-safe base64 encoded string
     *
     * @param encodedPrivateKey
     * @return
     * @throws NoSuchProviderException
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static PrivateKey loadPrivateKey(String encodedPrivateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
        try {
            byte[] decodedPrivateKey = Base64Encoder.decode(encodedPrivateKey);
            Class ECPrivateKeySpecClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPrivateKeySpec);
            Class ECParameterSpecClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECParameterSpec);
            Constructor ECPrivateKeySpecConstructor = ECPrivateKeySpecClass.getConstructor(BigInteger.class, ECParameterSpecClass);
            Method fromUnsignedByteArrayMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.fromUnsignedByteArray);
            Method getParameterSpecMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getParameterSpec);

            BigInteger s = (BigInteger) fromUnsignedByteArrayMethod.invoke(null, decodedPrivateKey);
            Object parameterSpec = getParameterSpecMethod.invoke(null, CURVE);
            Object privateKeySpec = ECPrivateKeySpecConstructor.newInstance(s, parameterSpec);

            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, BouncyCastleReflectionUtil.getBouncyCastleProviderName());
            return keyFactory.generatePrivate((KeySpec) privateKeySpec);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Load a public key from the private key.
     *
     * @param privateKey
     * @return
     */
    public static ECPublicKey loadPublicKey(ECPrivateKey privateKey) throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException {
        try {
            Class ECParameterSpecClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECParameterSpec);
            Class ECPrivateKeyClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPrivateKey);
            Class ECPublicKeySpecClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPublicKeySpec);
            Class ECPointClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPoint);

            Constructor ECPublicKeySpecConstructor = ECPublicKeySpecClass.getConstructor(ECPointClass, ECParameterSpecClass);
            Method getParameterSpecMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getParameterSpec);
            Method getGMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getG);
            Method getDMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getD);
            Method multiplyMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.multiply);
            Method getEncodedMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getEncoded);
            Method getCurveMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getCurve);
            Method decodePointMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.decodePoint);

            Object ecSpec = ECParameterSpecClass.cast(getParameterSpecMethod.invoke(null, CURVE));
            Object g = getGMethod.invoke(ecSpec);
            Object privateKeyCasted = ECPrivateKeyClass.cast(privateKey);
            Object d = getDMethod.invoke(privateKeyCasted);
            Object Q = multiplyMethod.invoke(g, d);
            byte[] publicDerBytes = (byte[]) getEncodedMethod.invoke(Q, false);

            Object curve = getCurveMethod.invoke(ecSpec);
            Object point = decodePointMethod.invoke(curve, publicDerBytes);
            Object pubSpec = ECPublicKeySpecConstructor.newInstance(point, ecSpec);
            KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM, BouncyCastleReflectionUtil.getBouncyCastleProviderName());
            return (ECPublicKey) keyFactory.generatePublic((KeySpec) pubSpec);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Verify that the private key belongs to the public key.
     *
     * @param privateKey
     * @param publicKey
     * @return
     */
    public static boolean verifyKeyPair(PrivateKey privateKey, PublicKey publicKey) {
        try {
            Class ECNamedCurveTableClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECNamedCurveTable);
            Class ECParameterSpecClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECParameterSpec);
            Class ECPointClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPoint);
            Class ECPublicKeyClass = BouncyCastleReflectionUtil.getClass(BouncyCastleReflectionUtil.BouncyCastleProviderClasses.ECPublicKey);

            Method getParameterSpecMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getParameterSpec);
            Method getGMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getG);
            Method multiplyMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.multiply);
            Method equalsMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.equals);
            Method getQMethod = BouncyCastleReflectionUtil.getMethod(BouncyCastleReflectionUtil.BouncyCastleProviderMethods.getQ);

            Object curveParameters = ECParameterSpecClass.cast(getParameterSpecMethod.invoke(null, CURVE));
            Object g = getGMethod.invoke(curveParameters);
            Object sG = multiplyMethod.invoke(g, ((java.security.interfaces.ECPrivateKey) privateKey).getS());
            Object pubKey = ECPublicKeyClass.cast(publicKey);
            Object ecPointToCompare = getQMethod.invoke(pubKey);
            return (boolean) equalsMethod.invoke(sG, ecPointToCompare);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Utility to concat byte arrays
     */
    public static byte[] concat(byte[]... arrays) {
        int lastPos = 0;

        byte[] combined = new byte[combinedLength(arrays)];

        for (byte[] array : arrays) {
            if (array == null) {
                continue;
            }

            System.arraycopy(array, 0, combined, lastPos, array.length);

            lastPos += array.length;
        }

        return combined;
    }

    /**
     * Compute combined array length
     */
    public static int combinedLength(byte[]... arrays) {
        int combinedLength = 0;

        for (byte[] array : arrays) {
            if (array == null) {
                continue;
            }

            combinedLength += array.length;
        }

        return combinedLength;
    }

    /**
     * Create a byte array of the given length from the given integer.
     *
     * @param integer
     * @param size
     * @return
     */
    public static byte[] toByteArray(int integer, int size) {
        ByteBuffer buffer = ByteBuffer.allocate(size);
        buffer.putInt(integer);

        return buffer.array();
    }
}

Do similar changes to all other classes wherever required ( or whichever place you get ClassNotFound exception).

Remember, do not add dependency in your pom for bouncycastle, otherwise you'll again run into this. The JVM will get confused between which class to load.

@debabratapatra
Copy link

@iyashsoni Thank you for responding and sharing your solution. I had to use older version of webpush to make it working. Here is my solution.

           <dependency>
	    <groupId>nl.martijndwars</groupId>
	    <artifactId>web-push</artifactId>
	    <version>4.0.0</version>
	</dependency>
	<dependency>
	    <groupId>org.bouncycastle</groupId>
	    <artifactId>bcprov-jdk14</artifactId>
	    <version>1.63</version>
	</dependency>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants