Я пытаюсь создать сервер, способный отправлять push-сообщения с помощью Push API: https://developer.mozilla.org/en-US/docs/Web/API/Push_API
У меня работает клиентская сторона, но теперь я хочу иметь возможность отправлять сообщения с помощью полезной нагрузки с сервера Java.
Я увидел пример web-push nodejs (https://www.npmjs.com/package/web-push), но я не смог правильно перевести его на Java.
Я попытался следовать примеру использования обмена ключами DH, найденного здесь: http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html#DH2Ex
С помощью sheltond ниже я смог найти код, который должен работать, но это не так.
Когда я отправляю зашифрованное сообщение в службу Push, я возвращаю ожидаемый код состояния 201, но push никогда не доходит до Firefox. Если я удалю полезную нагрузку и заголовки и просто отправлю запрос POST на тот же URL-адрес, сообщение успешно поступит в Firefox без данных. Я подозреваю, что это может быть связано с тем, как я шифрую данные с помощью Cipher.getInstance( "AES/GCM/NoPadding" );
Это код, который я использую в настоящее время:
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 the result
SecretKeySpec key = new SecretKeySpec(key_bytes, 0, 16, "AES");
bobCipher.init(Cipher.ENCRYPT_MODE, key);
byte[] cleartext = "{\"this\":\"is a test that is supposed to be working but it is 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());
}
где "BASE_64_PUBLIC_KEY_FROM_PUSH_SUBSCRIPTION" является ключом метода подписки Push API в предоставляемом браузере и "PUSH_ENDPOINT_URL" является конечной точкой push, предоставленной браузером.
Если я получаю значения (зашифрованный текст, base64 bobPubKeyEnc и соль) из успешного запроса веб-push nodejs и жесткого кода на Java, он работает. Если я использую приведенный выше код с динамическими значениями, он не работает.
Я заметил, что зашифрованный текст, который работал в реализации nodejs, всегда на 1 байт больше, чем зашифрованный текст Java с кодом выше. В примере, который я использовал здесь, всегда получается 81-байтовый шифрованный текст, но в nodejs он всегда составляет 82 байта. Означает ли это, что это может быть неправильно?
Как правильно закодировать полезную нагрузку, чтобы она дошла до Firefox?
Заранее благодарим за помощь