2016-02-05 2 views
14

Ich versuche, einen Server zu erstellen fähig Push-Nachrichten zu senden, die Push-API: https://developer.mozilla.org/en-US/docs/Web/API/Push_APINachricht verschlüsseln für Web-Push-API in Java

Ich habe die Client-Seite arbeiten habe, aber jetzt möchte ich in der Lage sein Senden Sie Nachrichten mit einer Payload von einem Java-Server.

Ich habe das Nodejs Web-Push-Beispiel (https://www.npmjs.com/package/web-push) gesehen, aber ich konnte das nicht korrekt in Java übersetzen.

Ich habe versucht, das Beispiel nach dem DH-Schlüsselaustausch hier zu verwenden: http://docs.oracle.com/javase/7/docs/technotes/guides/security/crypto/CryptoSpec.html#DH2Ex

Mit Hilfe sheltond unten konnte ich einige Code, um herauszufinden, das ist arbeiten sollte, aber nicht.

Wenn ich die verschlüsselte Nachricht an den Push-Dienst poste, erhalte ich den erwarteten Statuscode 201, aber der Push erreicht nie Firefox. Wenn ich die Payload und die Header entferne und einfach eine POST-Anfrage an dieselbe URL sende, kommt die Nachricht erfolgreich in Firefox ohne Daten an. Ich vermute, dass es etwas damit zu tun hat, wie ich die Daten mit Cipher.getInstance ("AES/GCM/NoPadding") verschlüsseln;

Dies ist der Code, den ich zur Zeit bin mit:

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()); 
} 

wo „BASE_64_PUBLIC_KEY_FROM_PUSH_SUBSCRIPTION“ der Schlüssel Abonnement Methode der Push-API im Browser zur Verfügung gestellt und „PUSH_ENDPOINT_URL“ ist der Push den Browser bereitgestellt Endpunkt.

Wenn ich Werte (Chiffretext, base64 bobPubKeyEnc und Salz) von einer erfolgreichen Nodejs Web-Push-Anfrage erhalten und sie in Java fest codiere, funktioniert es. Wenn ich den obigen Code mit dynamischen Werten verwende, funktioniert es nicht.

Ich habe festgestellt, dass der Chiffretext, der in der Nodejs-Implementierung arbeitete, immer 1 Byte größer ist als der Java-Chiffretext mit dem obigen Code. Das Beispiel, das ich hier verwendete, erzeugt immer einen Chiffretext von 81 Byte, aber in Nodejs ist es immer 82 Bytes zum Beispiel. Gibt uns das einen Hinweis darauf, was falsch sein könnte?

Wie verschlüssele ich die Payload korrekt, sodass sie Firefox erreicht?

Vielen Dank im Voraus für jede Hilfe

+0

verwenden Sie wirklich "BASE_64_PUBLIC_KEY_FROM_PUSH_SUBSCRIPTION"? – Dawnkeeper

+0

Sorry, nein, das ist nur ein Platzhalter für den Schlüssel, den der Browser zurückgibt. Ich werde die Frage bearbeiten, danke – joaomgcd

+1

Wir sind an der gleichen Stelle fest. Der Versuch, genau dasselbe mit .NET zu tun. Ich fühle deinen Schmerz. –

Antwort

4

Able-Benachrichtigung zu erhalten, nachdem Code ändern, wie pro https://jrconlin.github.io/WebPushDataTestPage/

Finden Sie den geänderten Code unter:

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; for (int i = 0; i < 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); for (int i = 0; i < 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 i = 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 the 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 is too 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 is too large"); } return uncompressedPoint; } }
3

Siehe https://tools.ietf.org/html/draft-ietf-webpush-encryption-01#section-5 und https://w3c.github.io/push-api/#widl-PushSubscription-getKey-ArrayBuffer-PushEncryptionKeyName-name (Punkt 4).

Der Schlüssel ist mit dem in ANSI X9.62 definierten unkomprimierten Format codiert, sodass Sie x509EncodedKeySpec nicht verwenden können.

Sie könnten BouncyCastle verwenden, das die X9.62-Codierung unterstützen soll.

+0

Danke, aber hast du irgendwelche Details darüber, wie ich das machen würde? Ich habe keine Online-Beispiele für diesen speziellen Anwendungsfall gefunden. Vielen Dank im Voraus – joaomgcd

+0

Leider habe ich keine Erfahrung mit Bouncy Castle. Ich weiß nur, dass es diese Kodierung unterstützt. – Marco

2

Werfen Sie einen Blick auf die Antwort von Maarten Bodewes in this question.

Er gibt Java-Quelle für die Kodierung/Dekodierung aus dem unkomprimierten X9.62-Format in einen ECPublicKey, die meiner Meinung nach für das, was Sie versuchen, geeignet sein sollte.

== Update 1 ==

Die Spezifikation sagt "User Agents, die eine Verschlüsselung erzwingen muss eine elliptische Kurve Diffie-Hellman-Aktie an der P-256-Kurve aussetzen".

Die P-256-Kurve ist eine vom NIST für den Einsatz in Verschlüsselungsanwendungen der US-Regierung freigegebene Standardkurve. Die Definition, die Parameterwerte und das Grundprinzip zum Auswählen dieser bestimmten Kurve (zusammen mit einigen anderen) sind here.

Es gibt Unterstützung für diese Kurve in der Standard-Bibliothek mit dem Namen "secp256r1", aber aus Gründen, die ich nicht vollständig ausarbeiten konnte (ich denke, es ist mit der Trennung der Kryptografie-Anbieter aus dem JDK zu tun) selbst), scheinen Sie durch einige sehr ineffizient Reifen zu springen zu haben eine dieses ECParameterSpec Wert von diesem Namen zu erhalten:

KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC"); 
ECGenParameterSpec kpgparams = new ECGenParameterSpec("secp256r1"); 
kpg.initialize(kpgparams); 
ECParameterSpec params = ((ECPublicKey) kpg.generateKeyPair().getPublic()).getParams(); 

das ist ziemlich Schwergewicht, weil es eigentlich ein Schlüsselpaar mit dem Namen ECGenParameterSpec Objekt erzeugt, dann Extrakte die ECParameterSpec daraus. Sie sollten dann in der Lage sein, dies zum Dekodieren zu verwenden (ich würde empfehlen, diesen Wert irgendwo zwischenzuspeichern, um zu vermeiden, dass diese Schlüsselgenerierung häufig durchgeführt werden muss).

Alternativ können Sie auch einfach die Zahlen von Seite 8 aus the NIST document übernehmen und direkt in den ECParameterSpec-Konstruktor einstecken.

Es gibt einen Code here, der genau so aussieht (genau wie Zeile 124). Dieser Code lautet Apache licensed. Ich habe diesen Code nicht selbst benutzt, aber es sieht so aus, als ob die Konstanten mit denen im NIST-Dokument übereinstimmen.

== == Update 2

Der eigentliche Verschlüsselungsschlüssel wird vom Salz abgeleiteten (zufällig erzeugte) und das gemeinsame Geheimnis (von dem DH-Schlüsselaustausch vereinbart), die HMAC-basierte Schlüsselableitungsfunktion (HKDF) beschrieben in Abschnitt 3.2 von Encrypted Content-Encoding for HTTP.

Dieses Dokument verweist auf RFC 5869 und spezifiziert die Verwendung von SHA-256 als Hash in der HKDF verwendet.

Dieser RFC beschreibt einen zweistufigen Prozess: Extrahieren und Erweitern. Die Extraktphase wird definiert als:

PRK = HMAC-Hash(salt, IKM) 

Im Fall von Web-Push, soll dies ein HMAC-SHA-256 Betrieb sein, das Salz Wert sollte den „saltBytes“ Wert, die Sie bereits haben, und als Soweit ich sehen kann, sollte der IKM-Wert das gemeinsame Geheimnis sein (das Webpush-Dokument sagt nur "Diese Werte werden verwendet, um den Inhaltsverschlüsselungsschlüssel zu berechnen", ohne speziell anzugeben, dass das gemeinsame Geheimnis das IKM ist).

Die Expand-Phase nimmt den von der Phase extrahierten Wert plus einen 'info'-Wert und wiederholt HMACs, bis sie genügend Schlüsseldaten für den von Ihnen verwendeten Verschlüsselungsalgorithmus erzeugt hat (die Ausgabe jedes HMAC wird gespeist) in die nächste - siehe the RFC für Details).

In diesem Fall ist der Algorithmus AEAD_AES_128_GCM, der einen 128-Bit-Schlüssel erfordert, der kleiner ist als die Ausgabe von SHA-256, sodass Sie nur einen Hash in der Expand-Phase ausführen müssen.

Der 'info' Wert in diesem Fall als "Content-Encoding: aesgcm128" (angegeben in Encrypted Content-Encoding for HTTP), so dass der Betrieb, die Sie brauchen, ist:

HMAC-SHA-256(PRK, "Content-Encoding: aesgcm128" | 0x01) 

, wo das '|' ist Verkettung. Sie nehmen dann die ersten 16 Bytes des Ergebnisses, und das sollte der Verschlüsselungsschlüssel sein.

In Java Begriffe, die so etwas wie aussehen:

// Extract 
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); 

Als Referenz here's a link zum Teil der BouncyCastle Bibliothek, die das Zeug tut.

Schließlich Ich habe gerade bemerkt, diesen Teil in dem webpush Dokument:

Public keys, such as are encoded into the "dh" parameter, MUST be in the form of an uncompressed point

so dass es wie Sie, so etwas verwenden, müssen aussieht:

byte[] bobPubKeyEnc = toUncompressedPoint((ECPublicKey)bobKpair.getPublic()); 

anstelle der Verwendung des Standard getEncoded () Methode.

== Update 3 ==

Zuerst sollte ich darauf hinweisen, dass es eine Verschlüsselung neueren Entwurf der Spezifikation für http Inhalt ist als die, die ich zur vorherigen verknüpft haben: draft-ietf-httpbis-encryption-encoding-00. Benutzer, die dieses System verwenden möchten, sollten sicherstellen, dass sie den neuesten verfügbaren Entwurf der Spezifikation verwenden. Dies ist ein laufender Prozess und scheint sich alle paar Monate leicht zu ändern.

Zweitens, in section 2 dieses Dokuments, gibt es an, dass vor dem Verschlüsseln einige Leerzeichen zum Klartext hinzugefügt werden müssen (und nach der Entschlüsselung entfernt werden müssen).

Dies würde den Unterschied von einem Byte in der Länge zwischen dem, was Sie erwähnt haben, und dem, was das Node.js-Beispiel erzeugt, erklären.

Das Dokument sagt:

Each record contains between 1 and 256 octets of padding, inserted into a record before the enciphered content. Padding consists of a length byte, followed that number of zero-valued octets. A receiver MUST fail to decrypt if any padding octet other than the first is non-zero, or a record has more padding than the record size can accommodate.

Ich denke also, was Sie tun müssen, ist ein einzelnes ‚0‘ Byte in die Chiffre, bevor Sie Ihren Klartext drücken. Sie könnte hinzufügen mehr Polsterung als das - Ich konnte nichts sehen, die angegeben, dass die Auffüllung muss die minimal mögliche Menge sein, aber eine einzige '0' Byte ist die einfachste (wer liest das wer versucht, diese Nachrichten von zu entschlüsseln das andere Ende sollte sicherstellen, dass sie jede legale Menge an Polsterung unterstützen). Der Mechanismus ist im Allgemeinen für die HTTP-Inhaltsverschlüsselung etwas komplizierter als dieser (da Sie die Eingabe in Datensätze aufteilen und jedem einen Puffer hinzufügen müssen), aber die Webpush-Spezifikation besagt, dass die verschlüsselte Nachricht passen muss in einen einzigen Datensatz, so dass Sie sich darüber keine Sorgen machen müssen.

Beachten Sie den folgenden Text in der webpush Verschlüsselung spec:

Note that a push service is not required to support more than 4096 octets of payload body, which equates to 4080 octets of cleartext

Die 4080 Bytes von Klartext umfasst hier das 1 Byte padding, so scheint es effektiv eine Grenze von 4079 Byte. Sie können eine größere Datensatzgröße mit dem Parameter "rs" im Header "Encryption" angeben, aber gemäß dem oben zitierten Text muss der Empfänger dies nicht unterstützen.

Eine Warnung: ein Teil des Codes, den ich gesehen habe, scheint sich auf die Verwendung von 2 Byte Padding zu ändern, vermutlich als Ergebnis einer vorgeschlagenen Spezifikationsänderung, aber ich konnte nicht aufspüren Woher kommt das? Im Moment sollte 1 Byte Padding in Ordnung sein, aber wenn dies in Zukunft nicht mehr funktioniert, müssen Sie vielleicht zu 2 Bytes gehen - wie ich oben erwähnt habe, ist diese Spezifikation in Arbeit und die Browser-Unterstützung ist jetzt experimentell.

+0

Danke. Irgendeine Idee, was ich für den ECParameterSpec-Parameter in der fromUncompressedPoint-Methode in diesem Beispiel verwenden sollte? – joaomgcd

+0

Vielen Dank! Das hat mich durch diesen Teil gebracht. Jetzt versuche ich jedoch, dies mit dem DH Key Exchange Algorithmus zu verwenden. Ich versuche, KeyPairGenerator zu tun bobKpairGen = KeyPairGenerator.getInstance ("DH"); bobKpairGen.initialize (params); Diese Parameter sind jedoch nicht mit den von Ihnen genannten EG-Parametern vereinbar. Ich bekomme "java.security.InvalidAlgorithmParameterException: Parameter-Objekt nicht eine DHParameterSpec". Irgendeine Idee, wie man dies in den DH Austausch einstecken kann? Nochmals vielen Dank – joaomgcd

+0

Probieren Sie "EC" oder "ECDH" als Parameter für den Aufruf "KeyPairGenerator.getInstance". Ich habe leider nicht die Möglichkeit, dies hier zu testen, und ich vermute, dass es genau darauf ankommt, welchen Sicherheitsanbieter Ihr System verwendet, aber ein gewisses Googeln deutet darauf hin, dass eines davon funktionieren könnte. Sie müssen auch "ECDH" übergeben, wenn Sie eine Instanz von KeyAgreement erstellen, glaube ich. Diese Seite http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html besagt, dass ECDH ein Standardname für KeyAgreement ist. – sheltond

0

Die Lösung von Santosh Kumar arbeitet mit einer Änderung:

Ich habe eine 1 -byte Cipher Padding kurz vor dem Definieren des Klartextbytes [].

Cipher bobCipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); 
byte[] iv = generateNonce(nonce_bytes12, 0); 
bobCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv)); 

// adding firefox padding: 
bobCipher.update(new byte[1]); 

byte[] cleartext = {...}; 
Verwandte Themen