Giter Site home page Giter Site logo

webpush-java's Introduction

WebPush

A Web Push library for Java 8. Supports payloads and VAPID.

Build Status Maven Central

Installation

For Gradle, add the following dependency to build.gradle:

compile group: 'nl.martijndwars', name: 'web-push', version: '5.1.1'

For Maven, add the following dependency to pom.xml:

<dependency>
    <groupId>nl.martijndwars</groupId>
    <artifactId>web-push</artifactId>
    <version>5.1.1</version>
</dependency>

This library depends on BouncyCastle, which acts as a Java Cryptography Extension (JCE) provider. BouncyCastle's JARs are signed, and depending on how you package your application, you may need to include BouncyCastle yourself as well.

Building

To assemble all archives in the project:

./gradlew assemble

Usage

This library is meant to be used as a Java API. However, it also exposes a CLI to easily generate a VAPID keypair and send a push notification.

CLI

A command-line interface is available to easily generate a keypair (for VAPID) and to try sending a notification.

$ ./gradlew run
Usage: <main class> [command] [command options]
  Commands:
    generate-key      Generate a VAPID keypair
      Usage: generate-key

    send-notification      Send a push notification
      Usage: send-notification [options]
        Options:
          --subscription
            A subscription in JSON format.
          --publicKey
            The public key as base64url encoded string.
          --privateKey
            The private key as base64url encoded string.
          --payload
            The message to send.
            Default: Hello, world!
          --ttl
            The number of seconds that the push service should retain the message.

For example, to generate a keypair and output the keys in base64url encoding:

$ ./gradlew run --args="generate-key"
PublicKey:
BGgL7I82SAQM78oyGwaJdrQFhVfZqL9h4Y18BLtgJQ-9pSGXwxqAWQudqmcv41RcWgk1ssUeItv4-8khxbhYveM=

PrivateKey:
ANlfcVVFB4JiMYcI74_h9h04QZ1Ks96AyEa1yrMgDwn3

Use the public key in the call to pushManager.subscribe to get a subscription. Then, to send a notification:

$ ./gradlew run --args='send-notification --endpoint="https://fcm.googleapis.com/fcm/send/fH-M3xRoLms:APA91bGB0rkNdxTFsXaJGyyyY7LtEmtHJXy8EqW48zSssxDXXACWCvc9eXjBVU54nrBkARTj4Xvl303PoNc0_rwAMrY9dvkQzi9fkaKLP0vlwoB0uqKygPeL77Y19VYHbj_v_FolUlHa" --key="BOtBVgsHVWXzwhDAoFE8P2IgQvabz_tuJjIlNacmS3XZ3fRDuVWiBp8bPR3vHCA78edquclcXXYb-olcj3QtIZ4=" --auth="IOScBh9LW5mJ_K2JwXyNqQ==" --publicKey="BGgL7I82SAQM78oyGwaJdrQFhVfZqL9h4Y18BLtgJQ-9pSGXwxqAWQudqmcv41RcWgk1ssUeItv4-8khxbhYveM=" --privateKey="ANlfcVVFB4JiMYcI74_h9h04QZ1Ks96AyEa1yrMgDwn3" --payload="Hello world"'

Proxy

If you are behind a corporate proxy you may need to specify the proxy host. This library respects Java's Network Properties, which means that you can pass https.proxyHost and http.proxyPort when invoking java, e.g. java -Dhttp.proxyHost=proxy.corp.com -Dhttp.proxyPort=80 -Dhttps.proxyHost=proxy.corp.com -Dhttps.proxyPort=443 -jar ....

API

First, make sure you add the BouncyCastle security provider:

Security.addProvider(new BouncyCastleProvider());

Then, create an instance of the push service, either nl.martijndwars.webpush.PushService for synchronous blocking HTTP calls, or nl.martijndwars.webpush.PushAsyncService for asynchronous non-blocking HTTP calls:

PushService pushService = new PushService(...);

Then, create a notification based on the user's subscription:

Notification notification = new Notification(...);

To send a push notification:

pushService.send(notification);

See wiki/Usage-Example for detailed usage instructions. If you plan on using VAPID, read wiki/VAPID.

Testing

The integration tests use Web Push Testing Service (WPTS) to handle the Selenium and browser orchestrating. We use a forked version that fixes a bug on macOS. To install WPTS:

npm i -g github:MartijnDwars/web-push-testing-service#bump-selenium-assistant

Then start WPTS:

web-push-testing-service start wpts

Then run the tests:

./gradlew clean test

Finally, stop WPTS:

web-push-testing-service stop wpts

FAQ

Why does encryption take multiple seconds?

There may not be enough entropy to generate a random seed, which is common on headless servers. There exist two ways to overcome this problem:

  • Install haveged, a "random number generator that remedies low-entropy conditions in the Linux random device that can occur under some workloads, especially on headless servers." This tutorial explains how to install haveged on different Linux distributions.

  • Change the source for random number generation in the JVM from /dev/random to /dev/urandom. This page offers some explanation.

Credit

To give credit where credit is due, the PushService is mostly a Java port of marco-c/web-push. The HttpEce class is mostly a Java port of martinthomson/encrypted-content-encoding.

Resources

Specifications

Miscellaneous

Related

The web-push-libs organization hosts implementations of the Web Push protocol in several languages:

webpush-java's People

Contributors

amreladawy avatar burkov avatar ckpalk avatar eliihen avatar elmatadorinho avatar fischermatte avatar konkit avatar marco-c avatar martijndwars avatar matthiasthiel avatar meamuri avatar minishlink avatar oxc avatar renovate-bot avatar simon04 avatar xuwei-k avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webpush-java's Issues

Remove guava dependency if not needed

webpush-java has a dependency to guava, which obviously doesn't serve a valid purpose:

// Not sure what for..
compile group: 'com.google.guava', name: 'guava', version: '19.0'

since guava is a quite heavy lib (around 3mb), it would be nice to remove this dependency.

Consider rewriting library API.

  1. At the current state the library uses obsolete GCM;
  2. API is not well written:

a) Not able to use API objects as params for web controller (which in most cases uses jackson and not gson)
b) Having to write custom dto objects introduces name clashes (Subscription, Notification etc.)
c) Having generated VAPID public key and private key I also have to provide subject, which should be optional as per https://tools.ietf.org/html/rfc7519#page-9.

P.S.:

SubscriptionKeys sk = originalSub.getKeys();
nl.martijndwars.webpush.Subscription.Keys keys = new nl.martijndwars.webpush.Subscription().new Keys(sk.getP256dh(), sk.getAuth());
nl.martijndwars.webpush.Subscription sub = new nl.martijndwars.webpush.Subscription();
sub.endpoint = originalSub.getEndpoint();
sub.keys = keys;

This is not flexible and is hard to read

key spec not recognised

Hi

I'm having trouble getting this to work.
I have generated keys with the help of:
C:\Proj\git\webpush-java>java -jar build/libs/web-push-3.0.0-all.jar generate-key
PublicKey:
BMU04E2PG6WEPFNpNjdm2axI-tPkBwIgEP60WVRy9-fgCKvPC7U1DA02XaPG9-Iwj_l6EnHo9zbW-aNhNO8BLDk=
PrivateKey:
G5yq2I6-xwK2pRizUFznueDbYm-DNgkHfWB4DqqS9Pc=

And in my java code I try to do:
String publicKey = "BMU04E2PG6WEPFNpNjdm2axI-tPkBwIgEP60WVRy9-fgCKvPC7U1DA02XaPG9-Iwj_l6EnHo9zbW-aNhNO8BLDk=";
String privateKey = "G5yq2I6-xwK2pRizUFznueDbYm-DNgkHfWB4DqqS9Pc=";
PushService pushService = new PushService(publicKey, privateKey, "mailto:[email protected]");

But I get "key spec not recognised".
What am I doing wrong, is it the subject that is wrong? what should the subject be ?

Error when building

Execution failed for task ':compileJava'.

Could not find tools.jar. Please check that C:\Program Files\Java\jre1.8.0_151 contains a valid JDK installation
I use java 8

Decryption Error in Firefox

I've been trying to get the push notification running with your example usage code, with no luck.

The browser keeps throwing this error.

The ServiceWorker for scope ‘http://localhost:8081/’ encountered an error decrypting a push message: ‘OperationError: The operation failed for an operation-specific reason’. For help with encryption, please see https://developer.mozilla.org/en-US/docs/Web/API/Push_API/Using_the_Push_API#Encryption

Any idea whats going on?

I used a node library to run a small server to make sure there's no issues in the client side. The issue seem to be in the client side.

When the push message is sent from the server side, I get a 201 http response.

HttpResponseProxy{HTTP/1.1 201 Created [Access-Control-Allow-Headers: content-encoding,encryption,crypto-key,ttl,encryption-key,content-type,authorization, Access-Control-Allow-Methods: POST, Access-Control-Allow-Origin: *, Access-Control-Expose-Headers: location,www-authenticate, Content-Type: text/html; charset=UTF-8, Date: Tue, 03 Jan 2017 01:45:54 GMT, Location: https://updates.push.services.mozilla.com/m/gAAAAABYawJSH-_1uf7tv292lj_TBcDoZ_uJlCI24KqOXcDaNyX7isiiSP0-iYb5z9G2j-EgEwPuntOQsLNq6R2lWrUjNoIefCvJNRUQIvrZQep5gCDD-2ZtBx6hWUpk3it-LI5i252aZMqRiNfcmf1w1Sv3GgOMhfEywI1IS6R7Y3rSfDg1HPJGa7plv_jfaofFsmesmyOk, Server: nginx, TTL: 2419200, Content-Length: 0, Connection: keep-alive] [Content-Type: text/html; charset=UTF-8,Content-Length: 0,Chunked: false]}

HttpEce.buildInfo(...) possible Nullpointer

Hi there,
in HttpEce.deriveKey(...) method you have following flow:

byte[] secret = null;
byte[] context = null;//follow this one
if (key != null) {
    secret = key;
} else if (dh != null) {
    byte[][] bytes = deriveDH(keyId, dh);
    secret = bytes[0];
    context = bytes[1]; //the only place you initialize context
} else if (keyId != null) {
    secret = keys.get(keyId).getPublic().getEncoded();
}
// no more changes to the context var
keyinfo = buildInfo("aesgcm", context);

and in buildinfo method you have immediately:

protected static byte[] buildInfo(String type, byte[] context) {
    ByteBuffer buffer = ByteBuffer.allocate(19 + type.length() + context.length);

so it seems that the only way to Not get NullPointer is to have key == null && dh != null,
so first & third conditions will never work anyway, and in code key is indeed passed as null when used.
Maybe I'm missing a bigger picture here?

Random number generation slow in Docker

We are using this library on an app that runs in a Docker instance. We noticed that the random number generation used for the salt can be very slow and block the execution for up to 5 minutes, just to generate the 16 bytes.

The problem is with this line in PushService:

byte[] salt = SecureRandom.getSeed( 16 );

Is there any specific reason to use getSeed() ?

Why not use nextBytes() instead? This random number generation is not blocking:

byte[] salt = new byte[16];
new SecureRandom().nextBytes( salt );

I can create a pull request for this if it helps.

Chrome notifications not using registrationId anymore?

I noticed in this commit: 64baebc#diff-635c01aada177ec436f99d8e16a73972 that for Chrome notifications, the POST body being sent is no longer the same as it was before, which was:
{ "registration_ids": [...], "raw_data": ..... }

Is this intended behavior? I've read that the entire body might need to be encrypted, which is what it seems like the code is now doing, but it seems a little strange that GcmNotification registrationIds are no longer being used anywhere in making the request (and I'm getting error=MissingRegistration when sending the notifications).

Couldnt build on Java 9/10

The following method is not found and fails at runtime.

org.bouncycastle.util.BigIntegers.fromUnsignedByteArray

Documentation Improvements

Just a few things that aren't clear at once by reading the documentation is what the PublicKey and userAuth are.

I believe userAuth is the key generated by the push messaging service from the browser side. Is the PublicKey somehow our public key?

Also please confirm that the PushService's default constructor is to be used with non-chrome browsers (Firefox at the moment.). Seem to be the case but I'd prefer to confirm and update the docs.

support Urgency

https://tools.ietf.org/html/rfc8030#section-5.3

  5.3.  Push Message Urgency

   For a device that is battery-powered, it is often critical that it
   remains dormant for extended periods.  Radio communication in
   particular consumes significant power and limits the length of time
   that the device can operate.

   To avoid consuming resources to receive trivial messages, it is
   helpful if an application server can communicate the urgency of a
   message and if the user agent can request that the push server only
   forwards messages of a specific urgency.

   An application server MAY include an Urgency header field in its
   request for push message delivery.  This header field indicates the
   message urgency.  The push service MUST NOT forward the Urgency
   header field to the user agent.  A push message without the Urgency
   header field defaults to a value of "normal".

   A user agent MAY include the Urgency header field when monitoring for
   push messages to indicate the lowest urgency of push messages that it
   is willing to receive.  A push service MUST NOT deliver push messages
   with lower urgency than the value indicated by the user agent in its
   monitoring request.  Push messages of any urgency are delivered to a
   user agent that does not include an Urgency header field when
   monitoring for messages.

   The grammar for the Urgency header field is as follows:

   Urgency = urgency-option
   urgency-option = ("very-low" / "low" / "normal" / "high")

   In order of increasing urgency:

   +----------+-----------------------------+--------------------------+
   | Urgency  | Device State                | Example Application      |
   |          |                             | Scenario                 |
   +----------+-----------------------------+--------------------------+
   | very-low | On power and Wi-Fi          | Advertisements           |
   | low      | On either power or Wi-Fi    | Topic updates            |
   | normal   | On neither power nor Wi-Fi  | Chat or Calendar Message |
   | high     | Low battery                 | Incoming phone call or   |
   |          |                             | time-sensitive alert     |
   +----------+-----------------------------+--------------------------+

                   Table 1: Illustrative Urgency Values

   Multiple values for the Urgency header field MUST NOT be included in
   requests; otherwise, the push service MUST return a 400 (Bad Request)
   status code.

Error 400 UnauthorizedRegistration

I use this code:

import java.nio.charset.StandardCharsets;
import java.security.Security;

import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.google.gson.JsonObject;

import nl.martijndwars.webpush.Notification;
import nl.martijndwars.webpush.PushService;

public class Teste {
	
	public static void main(String[] args) {
		try {
			Security.addProvider(new BouncyCastleProvider());

	        // Send notification!
			sendPushMessage(getPayload());
	        
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
	private static final int TTL = 255;

	public static void sendPushMessage(byte[] payload) {
		// Figure out if we should use GCM for this notification somehow
		try{
			Notification notification;
			PushService pushService;

			// Create a notification with the endpoint, userPublicKey from the subscription and a custom payload
			notification = new Notification(
					"https://fcm.googleapis.com/fcm/send/d8KX2q4goDM:APA91bH5Boq0076mY4-YdxIOrsD_pzfx6DorrD6FRaksk5sf64A3Z9cySX2JhxwOlql1wq-Bdo0SZvSmBbARZaxTgn4_O9MHbbG_JFY-ZJp0i6WauLwllglA54lBp6NkWB0q6axNHIa3",
					"BPNcSFiObeUbcCg4m5c1AybHv7NSdBE_X5YJ6ZFQfpXWnXQbDnEILz3qPe4Zb-9M9B6Lc_W20uSzVmH1ZyNuWwk=",
					"nJiZotPSQE4P4z75Igq57Q==",
					"Hello, world!"
					);

			// Instantiate the push service, no need to use an API key for Push API
			pushService = new PushService();

			// Send the notification
			HttpResponse httpResponse = pushService.send(notification);

			System.out.println(httpResponse.getStatusLine().getStatusCode());
			System.out.println(IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8));
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private static byte[] getPayload() {
        JsonObject jsonObject = new JsonObject();
        jsonObject.addProperty("title", "Hello");
        jsonObject.addProperty("message", "World");

        return jsonObject.toString().getBytes();
    }
}

And receive this answer:

400
<HTML>
<HEAD>
<TITLE>UnauthorizedRegistration</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>UnauthorizedRegistration</H1>
<H2>Error 400</H2>
</BODY>
</HTML>

Send data containing UTF-8 characters

When I try to send a notification (using VAPID) with a payload that contains some UTF-8 characters like 'ç', the notification is sent and received by the end user, but with no data.

All the rest is working perfectly.

Thanks!

NoSuchProviderException for "BC"

Hello,
When instanciating a PushService object, the line KeyFactory.getInstance("ECDH", "BC") in Utils.class throws a NoSuchProviderException for "BC".
Is it normal ?
Thanks.

403 MismatchSenderId VAPID

I try send notification from jar in IDEA.

Keys generated with same jar.

My arguments command:

send-notification
--subscription="{'endpoint': 'https://fcm.googleapis.com/fcm/send/f2tFGlQREiw:APA91bGKY0TakQ92UOPNlRe3_DiJ2CT0bC-m98CINcBRuKqvjBhkaO9im01T9ZLlOJeVRCXV57zSk1qgLTB5Wz6oAjrJ3Y19G4vQrWgKqsvwKeRAiCJHlmac_aJrF_tWlMgwyqfRwOHA', 'expirationTime': null, 'keys': {'p256dh': 'BKBQF2VLYXmpQ3sEcgj8wjUJjfhgz5GW7PyTvN2FB1MlAMSLN2bgwOqe9GkSbmoiRedlpgfp6CihUzGhp5DtCkM','auth': 'BJO4i2ltwyyV8ekNpaW4hQ'}}"
--publicKey="BIqmcv0cVhdGJMjBa16jAhMh-eRfiG5-g14WkSj_62pL7zSKJP1KDoPOGW2wN-BIo6VNQsxZKr1VrYXO66If2hc="
--privateKey="RauHMIAXL8UKqrtn1EvWUWeO2No62P-XTo56yM59lDI="
--payload="Hello, lovely world"

subscription I get from your UI from js-subscription-json element.

What wrong?

Service workers Push event is not triggering

I have used the same API and procedure to sent notification , I am able to send notification successfully with response code 200, but still push event is not happening on receiving notification.

{ auth:"hcQQq+1FeDuSu7V0zd5DXA==" endpoint:"https://fcm.googleapis.com/fcm/send/ cXgp0l3svNo:APA91bG8dDfZhrc0iaSyzvuV1BvnxXz9T- SmLCKOymKrEdwvrh0_SjzjnU3ORRKvW5QD- Zp196T5nAGPayR7EKu_Bkb0pQrSex7Q3DZSu54Lo83AEiUE6p-2Xn-nrquCymKVFt6Z4nY8" key:"BJv2qC3WSCsRszMi57vOBpFjnIpdJ/ uXQQFj4d0XZD9lRuZKuBgJNVFra0SFEvRlQQ88eG8RWWs7sSvO9Pbdkwk=" }
above given is my endpoint in which i am sending notification.

response body is null in chrome push

i am trying to use your new method of sending push using httppost, but the httpresponse is giving status code as 201 and body as null.
However when i use old way (using request and async) it gives proper response. Can you please help me with this?

Thanks

Check notification arrives

Currently, the selenium tests only check that the endpoint accepts the push notification (201 status code). We can also check that the notification arrives in the browser and the message is as expected. For broadcasting from a service worker to a client, see here.

Selenium tests

We should use Selenium to run end-to-end browser tests. My work in progress is in the selenium branch.

key spec not recognized

Hello.

I am trying to use webpush-java with a web page and I created a Java test application (Jar file) to try it. But I have a problem:

When I run the test in the Windows prompt I get success and a push notification in my workstation, but when I run the test in the CMS (Opencms - Tomcat) I get the error:

09 ago 2018 11:44:17,794 ERROR [s.scheduler.CmsScheduleManager: 576] Error executing scheduled job "teste para notificacao".
**java.security.spec.InvalidKeySpecException: key spec not recognized**
	at org.bouncycastle.jcajce.provider.asymmetric.util.BaseKeyFactorySpi.engineGeneratePublic(Unknown Source)
	at org.bouncycastle.jcajce.provider.asymmetric.ec.KeyFactorySpi.engineGeneratePublic(Unknown Source)
	at java.security.KeyFactory.generatePublic(Unknown Source)
	at nl.martijndwars.webpush.Utils.loadPublicKey(Utils.java:70)
	at nl.martijndwars.webpush.Notification.<init>(Notification.java:51)
	at PushServiceTest.testPushChromeVapid(PushServiceTest.java:69)
	at disparaTeste.launch(disparaTeste.java:22)
	at org.opencms.scheduler.CmsScheduleManager.executeJob(CmsScheduleManager.java:568)
	at org.opencms.scheduler.CmsScheduleManager$1.run(CmsScheduleManager.java:170)

I think the problem is in the command:

pushService.setPublicKey(Utils.loadPublicKey("BOH8nTQA5iZhl23+NCzGG9prvOZ5BE0MJXBW+GUkQIvRVTVB32JxmX0V1j6z0r7rnT7+bgi6f2g5fMPpAh5brqM="));

Have you some suggestion to solve the problem?

Thanks a lot.

400 UnauthorizedRegistration

use https://web-push-codelab.glitch.me/ for getting keys and your sendPushMessage method from UsageExample

I get 400 UnauthorizedRegistration error.

Java code:

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

SubscriptionMy subscriptionMy = new SubscriptionMy(p256dh, auth, endpoint);
sendPushMessage(subscriptionMy, getPayload().getBytes(Charset.forName("UTF-8")));
public void sendPushMessage(SubscriptionMy sub, byte[] payload) throws GeneralSecurityException, InterruptedException, JoseException, ExecutionException, IOException {

        // useGcm = true OR false -> same error
        boolean useGcm = true;
        Notification notification;
        PushService pushService;

        if (useGcm) {
            // Create a notification with the endpoint, userPublicKey from the subscription and a custom payload
            notification = new Notification(
                    sub.getEndpoint(),
                    sub.getKey(),
                    sub.getAuth(),
                    payload
            );

            // Instantiate the push service, no need to use an API key for Push API
            pushService = new PushService();
        } else {
            // Or create a GcmNotification, in case of Google Cloud Messaging
            notification = new Notification(
                    sub.getEndpoint(),
                    sub.getKey(),
                    sub.getAuth(),
                    payload
            );

            // Instantiate the push service with a GCM API key
            pushService = new PushService(GCM_API_KEY);
        }

        // Send the notification
        HttpResponse response = pushService.send(notification);

        System.out.println(response);
    }
public class SubscriptionMy {

    private String key;
    private String auth;
    private String endpoint;

    public SubscriptionMy(String key, String auth, String endpoint) {
        this.key = key;
        this.auth = auth;
        this.endpoint = endpoint;
    }

    public String getEndpoint() {
        return endpoint;
    }

    public void setAuth(String auth) {
        this.auth = auth;
    }

    public String getAuth() {
        return auth;
    }

    /**
     * Returns the base64 encoded auth string as a byte[]
     */
    public byte[] getAuthAsBytes() {
        return Base64.getDecoder().decode(getAuth());
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getKey() {
        return key;
    }

    /**
     * Returns the base64 encoded public key string as a byte[]
     */
    public byte[] getKeyAsBytes() {
        return Base64.getDecoder().decode(getKey()); 
    }

    /**
     * Returns the base64 encoded public key as a PublicKey object
     */
    public PublicKey getUserPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException {
        KeyFactory kf = KeyFactory.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME);
        ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
        ECPoint point = ecSpec.getCurve().decodePoint(getKeyAsBytes());
        ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec);

        return kf.generatePublic(pubSpec);
    }

}

Im not use getKeyAsBytesmethod because i get Exceptionjava.lang.IllegalArgumentException: Illegal base64 character 5f`

And Im not use getUserPublicKeymethod because i get Exceptionjava.lang.IllegalArgumentException: Invalid point encoding 0x42`

Please. help me.

Error: A JNI error has occurred, please check your installation and try again

Using Java on Ubuntu 16.04:
openjdk version "1.8.0_162"
OpenJDK Runtime Environment (build 1.8.0_162-8u162-b12-0ubuntu0.16.04.2-b12)
OpenJDK Server VM (build 25.162-b12, mixed mode)

Though the build was success but I am getting following error while executing the jar:
$java -jar webpush-1.jar generate-key

Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/beust/jcommander/ParameterException
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: com.beust.jcommander.ParameterException
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:338)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 7 more

Please let me know if I am missing anything.

Thanks.

org.jose4j.lang.InvalidKeyException while pushing notification.

I'm getting a org.jose4j.lang.InvalidKeyException, when i'm trying to push a notification:

Exception:

2018-02-20 18:20:46,787|000-exec-2|ERROR|boration.review.ReviewOverview|The given key (algorithm=ECDH) is not valid (not a private key or is the wrong type of key) for SHA256withECDSA / ES256 java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPrivateKey
org.jose4j.lang.InvalidKeyException: The given key (algorithm=ECDH) is not valid (not a private key or is the wrong type of key) for SHA256withECDSA / ES256 java.lang.ClassCastException: org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot be cast to java.security.interfaces.ECPrivateKey
	at org.jose4j.jws.BaseSignatureAlgorithm.validateSigningKey(BaseSignatureAlgorithm.java:160)
	at org.jose4j.jws.JsonWebSignature.sign(JsonWebSignature.java:98)
	at org.jose4j.jws.JsonWebSignature.getCompactSerialization(JsonWebSignature.java:73)
	at nl.martijndwars.webpush.PushService.preparePost(PushService.java:198)
	at nl.martijndwars.webpush.PushService.sendAsync(PushService.java:131)
	at nl.martijndwars.webpush.PushService.send(PushService.java:118)
	at de.aeneis.web.collaboration.review.ReviewOverview$2.onEvent(ReviewOverview.java:154)
	at org.zkoss.zk.ui.AbstractComponent.onEvent(AbstractComponent.java:3163)
	at org.zkoss.zk.ui.AbstractComponent.service(AbstractComponent.java:3133)
	at org.zkoss.zk.ui.AbstractComponent.service(AbstractComponent.java:3075)
	at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138)
	at org.zkoss.zk.ui.impl.UiEngineImpl.processEvent(UiEngineImpl.java:1853)
	at org.zkoss.zk.ui.impl.UiEngineImpl.process(UiEngineImpl.java:1625)
	at org.zkoss.zk.ui.impl.UiEngineImpl.execUpdate(UiEngineImpl.java:1328)
	at org.zkoss.zk.au.http.DHtmlUpdateServlet.process(DHtmlUpdateServlet.java:606)
	at de.aeneis.bpmportal.zk.UpdateServlet.process(UpdateServlet.java:53)
	at org.zkoss.zk.au.http.DHtmlUpdateServlet.doGet(DHtmlUpdateServlet.java:482)
	at de.aeneis.bpmportal.zk.UpdateServlet.doGet(UpdateServlet.java:43)
	at org.zkoss.zk.au.http.DHtmlUpdateServlet.doPost(DHtmlUpdateServlet.java:490)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:707)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at io.swagger.api.ApiOriginFilter.doFilter(ApiOriginFilter.java:16)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)

My Code:

Security.addProvider(new BouncyCastleProvider());

PushService pushService = new PushService()
  .setPublicKey(PUBLIC_KEY)
  .setPrivateKey(PRIVATE_KEY)
  .setSubject("mailto:[email protected]");

nl.martijndwars.webpush.Subscription subscription = new nl.martijndwars.webpush.Subscription();

Subscription.Keys keys = subscription.new Keys(
  "BFxzKvlzk9tarZ5f2M7DUwBJEWxeJ4IQ_yety8cE8TgfWwSLPKeUROxxlnPUNN5SCoAJd3KE3nBBcQ9JCrMPsm0=",
  "JoLTqdBPvO-FdQT01A6w-Q=="
);

nl.martijndwars.webpush.Subscription subscription2 = new nl.martijndwars.webpush.Subscription(
  "https://fcm.googleapis.com/fcm/send/d7NgqbmcoKo:APA91bG3J9OCjcjdGYGVWhtGVjB2nFUcKQTuePjUYMxQ2TH8HEUXLH7EwXcFQvIZ4pAvPT3_y7aIu6kYTJdcoo_qy4ukG3C3fLOK2-YZwYS5eRtpvo3gbuPM5ZxD6YLxmr0WLP0rOXlk",
  keys
);

Notification notification = new Notification(subscription2, "Hello");

pushService.send(notification);

The work on the client side is done and it seems to be ok.
The keys are created with GenerateKeyCommand.java
I use localhost in my browser for tests.

What i am doing wrong?

Thx

aes128gcm content encoding / MS Edge support

Hi,
I tested sending a notification to MS Edge - already working in Chrome and Firefox - without success: the push server (https://db5.notify.windows.com/w/?token=...) returns 400 Bad Request with no body.

After a quick check and according to https://github.com/web-push-libs/web-push-php/releases/tag/v4.0.0 it appears to be because it requires the aes128gcm content encoding, aesgcm won't work (I have not confirmed this).

Are there any plans to add support for aes128gcm? Thanks!

GCM not working without valid VAPID keys

Since updating from 3.0.1 to 4.0.0 we are getting errors when sending GCM notifications. We were able to send them without supplying valid keys before but it's now failing on this assertion.

public HttpPost preparePost(Notification notification, Encoding encoding) throws GeneralSecurityException, IOException, JoseException {
assert (Utils.verifyKeyPair(privateKey, publicKey));

proxy autentication with username and password dosnt'work

if i configure the JVM parameter with
-Dhttp.proxyHost=proxy.test.it -Dhttp.proxyPort=3128 -Dhttp.proxyUser=username -Dhttp.proxyPassword=passw
-Dhttps.proxyHost=proxy.test.it -Dhttps.proxyPort=3128 -Dhttps.proxyUser=username -Dhttps.proxyPassword=passw
but the autentication doesn't work.

the solution to work is:
HttpHost proxy = new HttpHost("proxy.test.it", 3128);
DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy);

CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("proxy.test.it", 3128),
new UsernamePasswordCredentials("username", "passw"));
CloseableHttpAsyncClient closeableHttpAsyncClient = HttpAsyncClients.custom()
.setProxy(proxy)
.setRoutePlanner(routePlanner)
.setDefaultCredentialsProvider(credsProvider)
.build();

    closeableHttpAsyncClient.start();

    HttpResponse httpResponse = (HttpResponse) closeableHttpAsyncClient.execute(httpPost, new ClosableCallback(closeableHttpAsyncClient)).get();

Generating VAPID Keys

Hi,

Your libs are excellent to learn for web push in java.

My requirement is sending push messages without GCM ,FCM ,etc... Without application server key when i try to subscribe in client side i am getting below error message.

app.js:57 Unable to register service worker. DOMException: Registration failed - missing applicationServerKey, and manifest empty or missing

So I have generated VAPID keys using below code and output as follows.

 ECNamedCurveParameterSpec parameterSpec = 
 ECNamedCurveTable.getParameterSpec("prime256v1");
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECDH", "BC");
        keyPairGenerator.initialize(parameterSpec);
        KeyPair serverKey = keyPairGenerator.generateKeyPair();
        
        PrivateKey priv = serverKey.getPrivate();
        PublicKey pub = serverKey.getPublic();

Below is my public and private keys:

EC Private Key
S: 84d0fbbda6e65e29b4ef314fb502879da07581280b3059cb8e8ef41b28099d5b

EC Public Key
X: 171874128880f91dea2daf4514e5bcf7020c86da1abf063d389149392c0812ff
Y: 175edbd04506da58581b72c28cd4833d4f104c7cc2b5300416ee547c5d7bd9d4

How do i pass my public key to PushManager.subscribe method.

Can you please help me out on this.

Thanks in advance.

vapid

for vapid do we need any license for commercial use

Update readme

Hi,

I have just spent a day trying to send notifications through WebPush, ad managed to get this done by specifically adding private and public keys to the PushService. Without this, I get authentication errors. This is not specifically mentioned in the ReadMe.md, so it may be worth adding this to the tutorial.

Thanks

Kees

Send notifications to a large number of subscribers

For sending notifications to multiple subscribers, I assume that we would have to create a single instance of PushService and many instances of Notification in a loop, is that correct?

If so, is it practical to send notifications to a very large number of subscribers using the above method? Are there any performance implications? Would this be highly memory or CPU intensive?

Would it make more sense to split the notifications into batches and run them separately? If so, what would be a good rule of thumb for each batch size?

Thanks!

Decrypting Web Push Payload

Hi Martin,

so my use case i want to decrypt the webpush payload for automation. I have all keys server public private , and client p256dh and auth . how i will do that

VAPID support

If this library could be updated to support VAPID, and the documentation updated to include an example, that would be great:)

Please use aes128gcm as the default encoding.

First off, thanks for this library.

RFC 8188 has been out for over a year, and some clients may soon no longer support the older "aesgcm" encoding format. Can you please make the default "aes128gcm"? Thanks!

Chrome issue.

Hi Martin,

Thank you for this library. I've successfully run this project and I can see that the notifications are being delivered to Firefox and Chrome. I've just noticed that the payload has an issue with Chrome, the data in PushEvent is always null while in Firefox everything is ok. Is there any modification that I need to do in for the payload to be delivered in Chrome clients too?

Cheers,

Rae

not working in Chrome

Following the article about sending web push notifications I managed to make it work in both Chrome and Firefox browsers. Using the webpush-java I managed to make it send notifications to Firefox, but not to Chrome. When sending to Chrome, I am getting return code 400 UnauthorizedRegistration.

The web server I use is web server for Chrome, as suggested in the article. I use the corresponding applicationServerPublicKey, as generated in the companion site, and use corresponding public/private keys (different for Firefox and Chrome).

Searching the internet didn't yield any actionable steps. Any suggestions as to what to try/how to get to the bottom of this? Please let me know if any more/additional information is needed.

Thanks!

Unable to send web pushes: receive 400 - Unauthorized Registration

I have a web app that is able to register subscriptions and send web pushes to chrome and firefox using node, but when I try to use those subscriptions with this library in java, I am getting Unauthorized Registration.

My test is below, which is modeled on this test in the web-push repo.

If you have any ideas about how I could be screwing this up, I'd be much obliged.

@Test
public void testPushChromeVapid() throws Exception {
    String endpoint = "https://fcm.googleapis.com/fcm/send/eKUKTbHcFbQ:APA91bFzJMkNQO1xHc2-JVcRRa4I76yKR9o4IvgJBx1eIyF1EordVbWbpyD5pxMZJfbjnmPo_EGU8yCATZbeypc2_hMZE3GutdJGqqFdjht7PhhaIGg2IwRdRtH8r0lSRK0wAN8bD00O";

    // Base64 string user public key/auth
    String userPublicKey = "BF1xKcbEKKydzY72hHTZOdlrGCK1OE2DLgcTA_qzOejXCAuPmP87MKgEVwaGsYjpbSIUyG2E4GxZYF74lWXG04w=";
    String userAuth = "tRoRvuq0UZqzbT0lv3r3Cw==";

    // Base64 string server public/private key
    String vapidPublicKey = "BDML-FghkuCdr0U1AlPHycUDlmAio4xL2OeX2oC_-X1ArOWdqzFea2_h3pyqxvl2x633Y-RR7hWbpbCmP73qn58";
    String vapidPrivateKey = "<redacted>";

    // Construct notification
    Notification notification = new Notification(endpoint, userPublicKey, userAuth, getPayload());

    // Construct push service
    PushService pushService = new PushService();
    pushService.setSubject("mailto:[email protected]");
    pushService.setPublicKey(Utils.loadPublicKey(vapidPublicKey));
    pushService.setPrivateKey(Utils.loadPrivateKey(vapidPrivateKey));

    // Send notification!
    HttpResponse httpResponse = pushService.send(notification);

    System.out.println(httpResponse.getStatusLine().getStatusCode());
    System.out.println(IOUtils.toString(httpResponse.getEntity().getContent(), StandardCharsets.UTF_8));
}

A very similar attempt with a valid firefox subscription results in a 401 with a mozilla error code about a problem with the authorization header.

{
    "code": 401,
    "errno": 109,
    "error": "Unauthorized",
    "message": "Request did not validate Invalid Authorization Header",
    "more_info": "http://autopush.readthedocs.io/en/latest/http.html#error-codes"
}

sun jdk support

When I ran the encryption code with open jdk 1.8 it is all working fine. But when I am running with sun jdk jdk1.8.0_20, it is trowing following exception

Exception in thread "main" java.security.InvalidKeyException: Not an EC key: ECDH
    at sun.security.ec.ECKeyFactory.checkKey(ECKeyFactory.java:121)
    at sun.security.ec.ECKeyFactory.toECKey(ECKeyFactory.java:90)
    at sun.security.ec.ECDHKeyAgreement.engineInit(ECDHKeyAgreement.java:67)
    at javax.crypto.KeyAgreement.implInit(KeyAgreement.java:341)
    at javax.crypto.KeyAgreement.chooseProvider(KeyAgreement.java:373)
    at javax.crypto.KeyAgreement.init(KeyAgreement.java:465)
    at javax.crypto.KeyAgreement.init(KeyAgreement.java:436)
    at nl.martijndwars.webpush.HttpEce.deriveDH(HttpEce.java:117)
    at nl.martijndwars.webpush.HttpEce.deriveKey(HttpEce.java:54)
    at nl.martijndwars.webpush.HttpEce.encrypt(HttpEce.java:166)
    at nl.martijndwars.webpush.PushService.encrypt(PushService.java:62)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.