encryption - Encrypt message for Web Push API in Java -


i'm trying create server capable of sending push messages using push api: https://developer.mozilla.org/en-us/docs/web/api/push_api

i've got client side working want able send messages payload java server.

i saw nodejs web-push example (https://www.npmjs.com/package/web-push) couldn't translate correctly java.

i tried following example use dh key exchange found here: http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/cryptospec.html#dh2ex

with of sheltond below able figure out code should working isn't.

when post encrypted message push service, expected 201 status code push never reaches firefox. if remove payload , headers , send post request same url message arrives in firefox no data. suspect may have way i'm encrypting data cipher.getinstance("aes/gcm/nopadding");

this code i'm using currently:

try {     final byte[] alicepubkeyenc = util.frombase64("base_64_public_key_from_push_subscription");     keypairgenerator kpg = keypairgenerator.getinstance("ec");     ecgenparameterspec kpgparams = new ecgenparameterspec("secp256r1");     kpg.initialize(kpgparams);      ecparameterspec params = ((ecpublickey) kpg.generatekeypair().getpublic()).getparams();     final ecpublickey alicepubkey = fromuncompressedpoint(alicepubkeyenc, params);     keypairgenerator bobkpairgen = keypairgenerator.getinstance("ec");     bobkpairgen.initialize(params);      keypair bobkpair = bobkpairgen.generatekeypair();     keyagreement bobkeyagree = keyagreement.getinstance("ecdh");     bobkeyagree.init(bobkpair.getprivate());       byte[] bobpubkeyenc = touncompressedpoint((ecpublickey) bobkpair.getpublic());       bobkeyagree.dophase(alicepubkey, true);     cipher bobcipher = cipher.getinstance("aes/gcm/nopadding");     secretkey bobdeskey = bobkeyagree.generatesecret("aes");     byte[] saltbytes = new byte[16];     new securerandom().nextbytes(saltbytes);     mac extract = mac.getinstance("hmacsha256");     extract.init(new secretkeyspec(saltbytes, "hmacsha256"));     final byte[] prk = extract.dofinal(bobdeskey.getencoded());      // expand     mac expand = mac.getinstance("hmacsha256");     expand.init(new secretkeyspec(prk, "hmacsha256"));     string info = "content-encoding: aesgcm128";     expand.update(info.getbytes(standardcharsets.us_ascii));     expand.update((byte) 1);     final byte[] key_bytes = expand.dofinal();      // use result     secretkeyspec key = new secretkeyspec(key_bytes, 0, 16, "aes");     bobcipher.init(cipher.encrypt_mode, key);      byte[] cleartext = "{\"this\":\"is test supposed working not\"}".getbytes();     byte[] ciphertext = bobcipher.dofinal(cleartext);      url url = new url("push_endpoint_url");     httpurlconnection urlconnection = (httpurlconnection) url.openconnection();     urlconnection.setrequestmethod("post");     urlconnection.setrequestproperty("content-length", ciphertext.length + "");     urlconnection.setrequestproperty("content-type", "application/octet-stream");     urlconnection.setrequestproperty("encryption-key", "keyid=p256dh;dh=" + util.tobase64urlsafe(bobpubkeyenc));     urlconnection.setrequestproperty("encryption", "keyid=p256dh;salt=" + util.tobase64urlsafe(saltbytes));     urlconnection.setrequestproperty("content-encoding", "aesgcm128");     urlconnection.setdoinput(true);     urlconnection.setdooutput(true);     final outputstream outputstream = urlconnection.getoutputstream();     outputstream.write(ciphertext);     outputstream.flush();     outputstream.close();     if (urlconnection.getresponsecode() == 201) {         string result = util.readstream(urlconnection.getinputstream());         log.v("push", "ok: " + result);     } else {         inputstream errorstream = urlconnection.geterrorstream();         string error = util.readstream(errorstream);         log.v("push", "not ok: " + error);     } } catch (exception e) {     log.v("push", "not ok: " + e.tostring()); } 

where "base_64_public_key_from_push_subscription" key push api subscription method in browser provided , "push_endpoint_url" push endpoint browser provided.

if values (ciphertext, base64 bobpubkeyenc , salt) successful nodejs web-push request , hard-code them in java, works. if use code above dynamic values not work.

i did notice ciphertext worked in nodejs implementation 1 byte bigger java ciphertext code above. example used here produces 81 byte cipher text in nodejs it's 82 bytes example. give clue on might wrong?

how correctly encrypt payload reaches firefox?

thanks in advance help

able receive notifications after changing code per https://jrconlin.github.io/webpushdatatestpage/

find modified code below :

 

import com.sun.org.apache.xerces.internal.impl.dv.util.base64; import java.io.bufferedinputstream; import java.io.inputstream; import java.io.outputstream; import java.math.biginteger; import java.net.httpurlconnection; import java.net.url; import java.nio.charset.standardcharsets; import java.security.keyfactory; import java.security.keypair; import java.security.keypairgenerator; import java.security.privatekey; import java.security.publickey; import java.security.securerandom; import java.security.security; import java.security.interfaces.ecpublickey; import java.security.spec.ecfieldfp; import java.security.spec.ecparameterspec; import java.security.spec.ecpoint; import java.security.spec.ecpublickeyspec; import java.security.spec.ellipticcurve; import java.util.arrays; import javax.crypto.cipher; import javax.crypto.keyagreement; import javax.crypto.mac; import javax.crypto.secretkey; import javax.crypto.spec.ivparameterspec; import javax.crypto.spec.secretkeyspec; import org.bouncycastle.jce.provider.bouncycastleprovider; public class webpushencryption { private static final byte uncompressed_point_indicator = 0x04; private static final ecparameterspec params = new ecparameterspec( new ellipticcurve(new ecfieldfp(new biginteger( "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 16)), new biginteger( "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc", 16), new biginteger( "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b", 16)), new ecpoint(new biginteger( "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296", 16), new biginteger( "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5", 16)), new biginteger( "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 16), 1); public static void main(string[] args) throws exception { security.addprovider(new bouncycastleprovider()); string endpoint = "https://updates.push.services.mozilla.com/push/v1/xxx"; final byte[] alicepubkeyenc = base64.decode("base64 encoded public key "); keypairgenerator keygen = keypairgenerator.getinstance("ecdh", "bc"); keygen.initialize(params); keypair bobkpair = keygen.generatekeypair(); privatekey localprivatekey = bobkpair.getprivate(); publickey localpublickey = bobkpair.getpublic(); final ecpublickey remotekey = fromuncompressedpoint(alicepubkeyenc, params); keyagreement bobkeyagree = keyagreement.getinstance("ecdh", "bc"); bobkeyagree.init(localprivatekey); byte[] bobpubkeyenc = touncompressedpoint((ecpublickey) bobkpair.getpublic()); bobkeyagree.dophase(remotekey, true); secretkey bobdeskey = bobkeyagree.generatesecret("aes"); byte[] saltbytes = new byte[16]; new securerandom().nextbytes(saltbytes); mac extract = mac.getinstance("hmacsha256", "bc"); extract.init(new secretkeyspec(saltbytes, "hmacsha256")); final byte[] prk = extract.dofinal(bobdeskey.getencoded()); // expand mac expand = mac.getinstance("hmacsha256", "bc"); expand.init(new secretkeyspec(prk, "hmacsha256")); //aes algorithm string info = "content-encoding: aesgcm128"; expand.update(info.getbytes(standardcharsets.us_ascii)); expand.update((byte) 1); final byte[] key_bytes = expand.dofinal(); byte[] key_bytes16 = arrays.copyof(key_bytes, 16); secretkeyspec key = new secretkeyspec(key_bytes16, 0, 16, "aes-gcm"); //nonce expand.reset(); expand.init(new secretkeyspec(prk, "hmacsha256")); string nonceinfo = "content-encoding: nonce"; expand.update(nonceinfo.getbytes(standardcharsets.us_ascii)); expand.update((byte) 1); final byte[] nonce_bytes = expand.dofinal(); byte[] nonce_bytes12 = arrays.copyof(nonce_bytes, 12); cipher bobcipher = cipher.getinstance("aes/gcm/nopadding", "bc"); byte[] iv = generatenonce(nonce_bytes12, 0); bobcipher.init(cipher.encrypt_mode, key, new ivparameterspec(iv)); byte[] cleartext = ("{\n" + " \"message\" : \"great match41eeee!\",\n" + " \"title\" : \"portugal vs. denmark4255\",\n" + " \"icon\" : \"http://icons.iconarchive.com/icons/artdesigner/tweet-my-web/256/single-bird-icon.png\",\n" + " \"tag\" : \"testtag1\",\n" + " \"url\" : \"http://www.yahoo.com\"\n" + " }").getbytes(); byte[] cc = new byte[cleartext.length + 1]; cc[0] = 0; (int = 0; < cleartext.length; i++) { cc[i + 1] = cleartext[i]; } cleartext = cc; byte[] ciphertext = bobcipher.dofinal(cleartext); url url = new url(endpoint); httpurlconnection urlconnection = (httpurlconnection) url.openconnection(); urlconnection.setrequestmethod("post"); urlconnection.setrequestproperty("content-length", ciphertext.length + ""); urlconnection.setrequestproperty("content-type", "application/octet-stream"); urlconnection.setrequestproperty("encryption-key", "keyid=p256dh;dh=" + base64.encode(bobpubkeyenc)); urlconnection.setrequestproperty("encryption", "keyid=p256dh;salt=" + base64.encode(saltbytes)); urlconnection.setrequestproperty("content-encoding", "aesgcm128"); urlconnection.setrequestproperty("ttl", "60"); urlconnection.setdoinput(true); urlconnection.setdooutput(true); final outputstream outputstream = urlconnection.getoutputstream(); outputstream.write(ciphertext); outputstream.flush(); outputstream.close(); if (urlconnection.getresponsecode() == 201) { string result = readstream(urlconnection.getinputstream()); system.out.println("push ok: " + result); } else { inputstream errorstream = urlconnection.geterrorstream(); string error = readstream(errorstream); system.out.println("push" + "not ok: " + error); } } static byte[] generatenonce(byte[] base, int index) { byte[] nonce = arrays.copyofrange(base, 0, 12); (int = 0; < 6; ++i) { nonce[nonce.length - 1 - i] ^= (byte) ((index / math.pow(256, i))) & (0xff); } return nonce; } private static string readstream(inputstream errorstream) throws exception { bufferedinputstream bs = new bufferedinputstream(errorstream); int = 0; byte[] b = new byte[1024]; stringbuilder sb = new stringbuilder(); while ((i = bs.read(b)) != -1) { sb.append(new string(b, 0, i)); } return sb.tostring(); } public static ecpublickey fromuncompressedpoint( final byte[] uncompressedpoint, final ecparameterspec params) throws exception { int offset = 0; if (uncompressedpoint[offset++] != uncompressed_point_indicator) { throw new illegalargumentexception( "invalid uncompressedpoint encoding, no uncompressed point indicator"); } int keysizebytes = (params.getorder().bitlength() + byte.size - 1) / byte.size; if (uncompressedpoint.length != 1 + 2 * keysizebytes) { throw new illegalargumentexception( "invalid uncompressedpoint encoding, not correct size"); } final biginteger x = new biginteger(1, arrays.copyofrange( uncompressedpoint, offset, offset + keysizebytes)); offset += keysizebytes; final biginteger y = new biginteger(1, arrays.copyofrange( uncompressedpoint, offset, offset + keysizebytes)); final ecpoint w = new ecpoint(x, y); final ecpublickeyspec ecpublickeyspec = new ecpublickeyspec(w, params); final keyfactory keyfactory = keyfactory.getinstance("ec"); return (ecpublickey) keyfactory.generatepublic(ecpublickeyspec); } public static byte[] touncompressedpoint(final ecpublickey publickey) { int keysizebytes = (publickey.getparams().getorder().bitlength() + byte.size - 1) / byte.size; final byte[] uncompressedpoint = new byte[1 + 2 * keysizebytes]; int offset = 0; uncompressedpoint[offset++] = 0x04; final byte[] x = publickey.getw().getaffinex().tobytearray(); if (x.length <= keysizebytes) { system.arraycopy(x, 0, uncompressedpoint, offset + keysizebytes - x.length, x.length); } else if (x.length == keysizebytes + 1 && x[0] == 0) { system.arraycopy(x, 1, uncompressedpoint, offset, keysizebytes); } else { throw new illegalstateexception("x value large"); } offset += keysizebytes; final byte[] y = publickey.getw().getaffiney().tobytearray(); if (y.length <= keysizebytes) { system.arraycopy(y, 0, uncompressedpoint, offset + keysizebytes - y.length, y.length); } else if (y.length == keysizebytes + 1 && y[0] == 0) { system.arraycopy(y, 1, uncompressedpoint, offset, keysizebytes); } else { throw new illegalstateexception("y value large"); } return uncompressedpoint; } }

Comments

Popular posts from this blog

sublimetext3 - what keyboard shortcut is to comment/uncomment for this script tag in sublime -

java - No use of nillable="0" in SOAP Webservice -

ubuntu - Laravel 5.2 quickstart guide gives Not Found Error -